mesauth-angular 1.3.3 → 1.3.4

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.
@@ -2,8 +2,8 @@ import * as i0 from '@angular/core';
2
2
  import { InjectionToken, makeEnvironmentProviders, provideAppInitializer, inject, NgZone, Injectable, NgModule, EventEmitter, signal, HostListener, HostBinding, Output, Component, ViewChild } from '@angular/core';
3
3
  import { HttpClient } from '@angular/common/http';
4
4
  import { HubConnectionBuilder, LogLevel } from '@microsoft/signalr';
5
- import { BehaviorSubject, Subject, EMPTY, of, throwError } from 'rxjs';
6
- import { tap, catchError, takeUntil } from 'rxjs/operators';
5
+ import { BehaviorSubject, Subject, EMPTY, of, timer, throwError } from 'rxjs';
6
+ import { tap, catchError, switchMap, takeUntil } from 'rxjs/operators';
7
7
  import * as i2 from '@angular/router';
8
8
  import { Router } from '@angular/router';
9
9
  import * as i3 from '@angular/common';
@@ -303,12 +303,16 @@ const mesAuthInterceptor = (req, next) => {
303
303
  // Skip redirect for the initial /auth/me check (app startup when not logged in)
304
304
  const isMeAuthPage = req.url.includes('/auth/me');
305
305
  if (status === 401 && !isLoginPage && !isAuthPage && !isMeAuthPage && !isPublicPage) {
306
- // Session expired or not authenticated - redirect to login
307
- // No isAuthenticated check: when session expires, BehaviorSubject still holds
308
- // stale user data, so checking isAuthenticated would block the redirect.
309
- isRedirecting = true;
310
- setTimeout(() => { isRedirecting = false; }, 5000);
311
- window.location.href = `${baseUrl}/login?returnUrl=${returnUrl}`;
306
+ // Wait 1.5s for the concurrent refresh's Set-Cookie to be processed, then retry once.
307
+ // If retry also gets 401, redirect to login.
308
+ return timer(1500).pipe(switchMap(() => next(req)), catchError((retryError) => {
309
+ if (retryError.status === 401) {
310
+ isRedirecting = true;
311
+ setTimeout(() => { isRedirecting = false; }, 5000);
312
+ window.location.href = `${baseUrl}/login?returnUrl=${returnUrl}`;
313
+ }
314
+ return throwError(() => retryError);
315
+ }));
312
316
  }
313
317
  else if (status === 403 && !is403Page) {
314
318
  isRedirecting = true;
@@ -545,40 +549,74 @@ class UserProfileComponent {
545
549
  <div class="user-profile-container">
546
550
  <!-- Not logged in -->
547
551
  <ng-container *ngIf="!currentUser()">
548
- <button class="login-btn" (click)="onLogin()">
549
- Login
550
- </button>
552
+ <button class="login-btn" (click)="onLogin()">Login</button>
551
553
  </ng-container>
552
554
 
553
555
  <!-- Logged in -->
554
556
  <ng-container *ngIf="currentUser()">
555
557
  <div class="user-header">
556
- <button class="notification-btn" (click)="onNotificationClick()" title="Notifications">
557
- <span class="icon">🔔</span>
558
- <span class="badge" *ngIf="unreadCount > 0">{{ unreadCount }}</span>
558
+ <!-- Notification Bell -->
559
+ <button class="notification-btn" [class.has-unread]="unreadCount > 0" (click)="onNotificationClick()" title="Notifications">
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
+ <path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/>
562
+ <path d="M13.73 21a2 2 0 0 1-3.46 0"/>
563
+ </svg>
564
+ <span class="badge" *ngIf="unreadCount > 0">{{ unreadCount > 99 ? '99+' : unreadCount }}</span>
559
565
  </button>
560
566
 
567
+ <!-- User Avatar + Dropdown -->
561
568
  <div class="user-menu-wrapper">
562
569
  <button class="user-menu-btn" (click)="toggleDropdown()">
563
- <img
564
- *ngIf="currentUser().fullName || currentUser().userName"
565
- [src]="getAvatarUrl(currentUser())"
566
- [alt]="currentUser().fullName || currentUser().userName"
567
- class="avatar"
568
- />
569
- <span *ngIf="!(currentUser().fullName || currentUser().userName)" class="avatar-initial">
570
- {{ getLastNameInitial(currentUser()) }}
571
- </span>
570
+ <div class="avatar-ring" [class.active]="dropdownOpen">
571
+ <img
572
+ *ngIf="currentUser().fullName || currentUser().userName"
573
+ [src]="getAvatarUrl(currentUser())"
574
+ [alt]="currentUser().fullName || currentUser().userName"
575
+ class="avatar"
576
+ />
577
+ <span *ngIf="!(currentUser().fullName || currentUser().userName)" class="avatar-initial">
578
+ {{ getLastNameInitial(currentUser()) }}
579
+ </span>
580
+ </div>
572
581
  </button>
573
582
 
574
583
  <div class="mes-dropdown-menu" *ngIf="dropdownOpen">
584
+ <!-- User info header -->
575
585
  <div class="mes-dropdown-header">
576
- {{ currentUser().fullName || currentUser().userName }}
586
+ <div class="dropdown-avatar-wrap">
587
+ <img
588
+ *ngIf="currentUser().fullName || currentUser().userName"
589
+ [src]="getAvatarUrl(currentUser())"
590
+ [alt]="currentUser().fullName || currentUser().userName"
591
+ class="dropdown-avatar"
592
+ />
593
+ <span *ngIf="!(currentUser().fullName || currentUser().userName)" class="dropdown-avatar-initial">
594
+ {{ getLastNameInitial(currentUser()) }}
595
+ </span>
596
+ </div>
597
+ <div class="dropdown-user-info">
598
+ <span class="dropdown-user-name">{{ currentUser().fullName || currentUser().userName }}</span>
599
+ <span class="dropdown-user-sub" *ngIf="currentUser().position || currentUser().department">
600
+ {{ currentUser().position || currentUser().department }}
601
+ </span>
602
+ </div>
577
603
  </div>
604
+
605
+ <div class="mes-dropdown-divider"></div>
606
+
578
607
  <button class="mes-dropdown-item profile-link" (click)="onViewProfile()">
608
+ <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">
609
+ <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/>
610
+ </svg>
579
611
  View Profile
580
612
  </button>
613
+
614
+ <div class="mes-dropdown-divider"></div>
615
+
581
616
  <button class="mes-dropdown-item logout-item" (click)="onLogout()">
617
+ <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">
618
+ <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"/>
619
+ </svg>
582
620
  Logout
583
621
  </button>
584
622
  </div>
@@ -586,7 +624,7 @@ class UserProfileComponent {
586
624
  </div>
587
625
  </ng-container>
588
626
  </div>
589
- `, isInline: true, styles: [":host{--primary-color: #1976d2;--primary-hover: #1565c0;--primary-light: rgba(25, 118, 210, .1);--error-color: #f44336;--error-light: #ffebee;--text-primary: #333;--text-secondary: #666;--text-muted: #999;--bg-primary: white;--bg-secondary: #f5f5f5;--bg-tertiary: #fafafa;--bg-hover: #f5f5f5;--border-color: #e0e0e0;--border-light: #f0f0f0;--shadow: rgba(0, 0, 0, .15);--shadow-light: rgba(0, 0, 0, .1)}:host(.theme-dark){--primary-color: #90caf9;--primary-hover: #64b5f6;--primary-light: rgba(144, 202, 249, .1);--error-color: #ef5350;--error-light: rgba(239, 83, 80, .1);--text-primary: #e0e0e0;--text-secondary: #b0b0b0;--text-muted: #888;--bg-primary: #1e1e1e;--bg-secondary: #2d2d2d;--bg-tertiary: #252525;--bg-hover: #333;--border-color: #404040;--border-light: #333;--shadow: rgba(0, 0, 0, .3);--shadow-light: rgba(0, 0, 0, .2)}.user-profile-container{display:flex;align-items:center;gap:16px;padding:0 16px}.login-btn{padding:8px 16px;background-color:var(--primary-color);color:#fff;border:none;border-radius:4px;cursor:pointer;font-weight:500;transition:background-color .3s}.login-btn:hover{background-color:var(--primary-hover)}.user-header{display:flex;align-items:center;gap:16px}.notification-btn{position:relative;background:none;border:none;font-size:24px;cursor:pointer;padding:8px;transition:opacity .2s}.notification-btn:hover{opacity:.7}.icon{display:inline-block}.badge{position:absolute;top:0;right:0;background-color:var(--error-color);color:#fff;border-radius:50%;width:20px;height:20px;display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:700}.user-menu-wrapper{position:relative}.user-menu-btn{background:none;border:none;cursor:pointer;padding:4px;border-radius:50%;transition:background-color .2s;display:flex;align-items:center;justify-content:center}.user-menu-btn:hover{background-color:var(--primary-light)}.avatar{width:40px;height:40px;border-radius:50%;object-fit:cover;background-color:#e0e0e0}.avatar-initial{width:40px;height:40px;border-radius:50%;background-color:var(--primary-color);color:#fff;display:flex;align-items:center;justify-content:center;font-weight:700;font-size:16px}.mes-dropdown-menu{position:absolute;top:calc(100% + 8px);right:0;background:var(--bg-primary);border:1px solid var(--border-color);border-radius:4px;box-shadow:0 2px 8px var(--shadow);min-width:200px;z-index:1000;overflow:hidden}.mes-dropdown-header{padding:12px 16px;border-bottom:1px solid var(--border-light);font-weight:600;color:var(--text-primary);font-size:14px}.mes-dropdown-item{display:block;width:100%;padding:12px 16px;border:none;background:none;text-align:left;cursor:pointer;font-size:14px;color:var(--text-primary);text-decoration:none;transition:background-color .2s}.mes-dropdown-item:hover{background-color:var(--bg-hover)}.profile-link{color:var(--primary-color)}.logout-item{border-top:1px solid var(--border-light);color:var(--error-color)}.logout-item:hover{background-color:var(--error-light)}.user-info{display:flex;flex-direction:column;gap:2px}.user-name{font-weight:500;font-size:14px;color:var(--text-primary)}.user-position{font-size:12px;color:var(--text-secondary)}.logout-btn{background:none;border:none;font-size:20px;cursor:pointer;color:var(--text-secondary);padding:4px 8px;transition:color .2s}.logout-btn:hover{color:var(--primary-color)}@media(max-width:768px){.user-info{display:none}.avatar{width:32px;height:32px}}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
627
+ `, isInline: true, styles: [":host{--primary-color: #1976d2;--primary-hover: #1565c0;--primary-light: rgba(25, 118, 210, .12);--primary-glow: rgba(25, 118, 210, .3);--error-color: #f44336;--error-light: rgba(244, 67, 54, .1);--text-primary: #212121;--text-secondary: #616161;--text-muted: #9e9e9e;--bg-primary: #ffffff;--bg-secondary: #f8f9fa;--bg-hover: #f0f4ff;--border-color: #e0e0e0;--shadow: rgba(0, 0, 0, .12);--shadow-lg: rgba(0, 0, 0, .18)}:host(.theme-dark){--primary-color: #90caf9;--primary-hover: #64b5f6;--primary-light: rgba(144, 202, 249, .12);--primary-glow: rgba(144, 202, 249, .25);--error-color: #ef5350;--error-light: rgba(239, 83, 80, .12);--text-primary: #e0e0e0;--text-secondary: #b0b0b0;--text-muted: #757575;--bg-primary: #1e1e2e;--bg-secondary: #27273a;--bg-hover: #2a2d4a;--border-color: #383850;--shadow: rgba(0, 0, 0, .35);--shadow-lg: rgba(0, 0, 0, .5)}.user-profile-container{display:flex;align-items:center;gap:4px}.login-btn{padding:7px 18px;background-color:var(--primary-color);color:#fff;border:none;border-radius:8px;cursor:pointer;font-weight:500;font-size:13px;letter-spacing:.2px;transition:background-color .2s,transform .15s}.login-btn:hover{background-color:var(--primary-hover);transform:translateY(-1px)}.user-header{display:flex;align-items:center;gap:4px}.notification-btn{position:relative;background:none;border:none;cursor:pointer;padding:8px;border-radius:10px;color:var(--text-secondary);display:flex;align-items:center;justify-content:center;transition:color .2s,background-color .2s}.notification-btn:hover{background-color:var(--primary-light);color:var(--primary-color)}.notification-btn.has-unread{color:var(--primary-color)}.bell-icon{display:block;transition:transform .35s cubic-bezier(.34,1.56,.64,1)}.notification-btn:hover .bell-icon{transform:rotate(-20deg) scale(1.15)}.badge{position:absolute;top:2px;right:2px;background-color:var(--error-color);color:#fff;border-radius:10px;min-width:17px;height:17px;padding:0 4px;display:flex;align-items:center;justify-content:center;font-size:10px;font-weight:700;line-height:1;box-shadow:0 0 0 2px var(--bg-primary);animation:badge-pop .25s cubic-bezier(.34,1.56,.64,1)}@keyframes badge-pop{0%{transform:scale(0)}to{transform:scale(1)}}.user-menu-wrapper{position:relative}.user-menu-btn{background:none;border:none;cursor:pointer;padding:2px;border-radius:50%;display:flex;align-items:center;justify-content:center;transition:transform .2s}.user-menu-btn:hover{transform:scale(1.06)}.avatar-ring{border-radius:50%;padding:2px;border:2px solid transparent;transition:border-color .25s,box-shadow .25s}.avatar-ring.active,.user-menu-btn:hover .avatar-ring{border-color:var(--primary-color);box-shadow:0 0 0 3px var(--primary-glow)}.avatar{width:36px;height:36px;border-radius:50%;object-fit:cover;display:block}.avatar-initial{width:36px;height:36px;border-radius:50%;background:linear-gradient(135deg,var(--primary-color),var(--primary-hover));color:#fff;display:flex;align-items:center;justify-content:center;font-weight:700;font-size:15px}.mes-dropdown-menu{position:absolute;top:calc(100% + 10px);right:0;background:var(--bg-primary);border:1px solid var(--border-color);border-radius:14px;box-shadow:0 8px 32px var(--shadow-lg),0 2px 8px var(--shadow);min-width:220px;z-index:1000;overflow:hidden;animation:dropdown-in .16s cubic-bezier(.16,1,.3,1)}@keyframes dropdown-in{0%{opacity:0;transform:translateY(-8px) scale(.96)}to{opacity:1;transform:translateY(0) scale(1)}}.mes-dropdown-header{display:flex;align-items:center;gap:12px;padding:16px;background:var(--bg-secondary)}.dropdown-avatar-wrap{flex-shrink:0}.dropdown-avatar{width:46px;height:46px;border-radius:50%;object-fit:cover;border:2px solid var(--primary-color);display:block}.dropdown-avatar-initial{width:46px;height:46px;border-radius:50%;background:linear-gradient(135deg,var(--primary-color),var(--primary-hover));color:#fff;display:flex;align-items:center;justify-content:center;font-weight:700;font-size:18px;border:2px solid var(--primary-color)}.dropdown-user-info{display:flex;flex-direction:column;gap:3px;min-width:0}.dropdown-user-name{font-weight:600;font-size:14px;color:var(--text-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.dropdown-user-sub{font-size:11px;color:var(--primary-color);font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.mes-dropdown-divider{height:1px;background:var(--border-color)}.mes-dropdown-item{display:flex;align-items:center;gap:10px;width:100%;padding:11px 16px;border:none;background:none;text-align:left;cursor:pointer;font-size:13.5px;font-weight:500;transition:background-color .15s}.item-icon{flex-shrink:0;opacity:.8}.profile-link{color:var(--primary-color)}.profile-link:hover{background-color:var(--primary-light)}.logout-item{color:var(--error-color)}.logout-item:hover{background-color:var(--error-light)}@media(max-width:768px){.avatar,.avatar-initial{width:32px;height:32px;font-size:13px}}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
590
628
  }
591
629
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: UserProfileComponent, decorators: [{
592
630
  type: Component,
@@ -594,40 +632,74 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
594
632
  <div class="user-profile-container">
595
633
  <!-- Not logged in -->
596
634
  <ng-container *ngIf="!currentUser()">
597
- <button class="login-btn" (click)="onLogin()">
598
- Login
599
- </button>
635
+ <button class="login-btn" (click)="onLogin()">Login</button>
600
636
  </ng-container>
601
637
 
602
638
  <!-- Logged in -->
603
639
  <ng-container *ngIf="currentUser()">
604
640
  <div class="user-header">
605
- <button class="notification-btn" (click)="onNotificationClick()" title="Notifications">
606
- <span class="icon">🔔</span>
607
- <span class="badge" *ngIf="unreadCount > 0">{{ unreadCount }}</span>
641
+ <!-- Notification Bell -->
642
+ <button class="notification-btn" [class.has-unread]="unreadCount > 0" (click)="onNotificationClick()" title="Notifications">
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
+ <path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/>
645
+ <path d="M13.73 21a2 2 0 0 1-3.46 0"/>
646
+ </svg>
647
+ <span class="badge" *ngIf="unreadCount > 0">{{ unreadCount > 99 ? '99+' : unreadCount }}</span>
608
648
  </button>
609
649
 
650
+ <!-- User Avatar + Dropdown -->
610
651
  <div class="user-menu-wrapper">
611
652
  <button class="user-menu-btn" (click)="toggleDropdown()">
612
- <img
613
- *ngIf="currentUser().fullName || currentUser().userName"
614
- [src]="getAvatarUrl(currentUser())"
615
- [alt]="currentUser().fullName || currentUser().userName"
616
- class="avatar"
617
- />
618
- <span *ngIf="!(currentUser().fullName || currentUser().userName)" class="avatar-initial">
619
- {{ getLastNameInitial(currentUser()) }}
620
- </span>
653
+ <div class="avatar-ring" [class.active]="dropdownOpen">
654
+ <img
655
+ *ngIf="currentUser().fullName || currentUser().userName"
656
+ [src]="getAvatarUrl(currentUser())"
657
+ [alt]="currentUser().fullName || currentUser().userName"
658
+ class="avatar"
659
+ />
660
+ <span *ngIf="!(currentUser().fullName || currentUser().userName)" class="avatar-initial">
661
+ {{ getLastNameInitial(currentUser()) }}
662
+ </span>
663
+ </div>
621
664
  </button>
622
665
 
623
666
  <div class="mes-dropdown-menu" *ngIf="dropdownOpen">
667
+ <!-- User info header -->
624
668
  <div class="mes-dropdown-header">
625
- {{ currentUser().fullName || currentUser().userName }}
669
+ <div class="dropdown-avatar-wrap">
670
+ <img
671
+ *ngIf="currentUser().fullName || currentUser().userName"
672
+ [src]="getAvatarUrl(currentUser())"
673
+ [alt]="currentUser().fullName || currentUser().userName"
674
+ class="dropdown-avatar"
675
+ />
676
+ <span *ngIf="!(currentUser().fullName || currentUser().userName)" class="dropdown-avatar-initial">
677
+ {{ getLastNameInitial(currentUser()) }}
678
+ </span>
679
+ </div>
680
+ <div class="dropdown-user-info">
681
+ <span class="dropdown-user-name">{{ currentUser().fullName || currentUser().userName }}</span>
682
+ <span class="dropdown-user-sub" *ngIf="currentUser().position || currentUser().department">
683
+ {{ currentUser().position || currentUser().department }}
684
+ </span>
685
+ </div>
626
686
  </div>
687
+
688
+ <div class="mes-dropdown-divider"></div>
689
+
627
690
  <button class="mes-dropdown-item profile-link" (click)="onViewProfile()">
691
+ <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">
692
+ <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/>
693
+ </svg>
628
694
  View Profile
629
695
  </button>
696
+
697
+ <div class="mes-dropdown-divider"></div>
698
+
630
699
  <button class="mes-dropdown-item logout-item" (click)="onLogout()">
700
+ <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">
701
+ <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"/>
702
+ </svg>
631
703
  Logout
632
704
  </button>
633
705
  </div>
@@ -635,7 +707,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
635
707
  </div>
636
708
  </ng-container>
637
709
  </div>
638
- `, styles: [":host{--primary-color: #1976d2;--primary-hover: #1565c0;--primary-light: rgba(25, 118, 210, .1);--error-color: #f44336;--error-light: #ffebee;--text-primary: #333;--text-secondary: #666;--text-muted: #999;--bg-primary: white;--bg-secondary: #f5f5f5;--bg-tertiary: #fafafa;--bg-hover: #f5f5f5;--border-color: #e0e0e0;--border-light: #f0f0f0;--shadow: rgba(0, 0, 0, .15);--shadow-light: rgba(0, 0, 0, .1)}:host(.theme-dark){--primary-color: #90caf9;--primary-hover: #64b5f6;--primary-light: rgba(144, 202, 249, .1);--error-color: #ef5350;--error-light: rgba(239, 83, 80, .1);--text-primary: #e0e0e0;--text-secondary: #b0b0b0;--text-muted: #888;--bg-primary: #1e1e1e;--bg-secondary: #2d2d2d;--bg-tertiary: #252525;--bg-hover: #333;--border-color: #404040;--border-light: #333;--shadow: rgba(0, 0, 0, .3);--shadow-light: rgba(0, 0, 0, .2)}.user-profile-container{display:flex;align-items:center;gap:16px;padding:0 16px}.login-btn{padding:8px 16px;background-color:var(--primary-color);color:#fff;border:none;border-radius:4px;cursor:pointer;font-weight:500;transition:background-color .3s}.login-btn:hover{background-color:var(--primary-hover)}.user-header{display:flex;align-items:center;gap:16px}.notification-btn{position:relative;background:none;border:none;font-size:24px;cursor:pointer;padding:8px;transition:opacity .2s}.notification-btn:hover{opacity:.7}.icon{display:inline-block}.badge{position:absolute;top:0;right:0;background-color:var(--error-color);color:#fff;border-radius:50%;width:20px;height:20px;display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:700}.user-menu-wrapper{position:relative}.user-menu-btn{background:none;border:none;cursor:pointer;padding:4px;border-radius:50%;transition:background-color .2s;display:flex;align-items:center;justify-content:center}.user-menu-btn:hover{background-color:var(--primary-light)}.avatar{width:40px;height:40px;border-radius:50%;object-fit:cover;background-color:#e0e0e0}.avatar-initial{width:40px;height:40px;border-radius:50%;background-color:var(--primary-color);color:#fff;display:flex;align-items:center;justify-content:center;font-weight:700;font-size:16px}.mes-dropdown-menu{position:absolute;top:calc(100% + 8px);right:0;background:var(--bg-primary);border:1px solid var(--border-color);border-radius:4px;box-shadow:0 2px 8px var(--shadow);min-width:200px;z-index:1000;overflow:hidden}.mes-dropdown-header{padding:12px 16px;border-bottom:1px solid var(--border-light);font-weight:600;color:var(--text-primary);font-size:14px}.mes-dropdown-item{display:block;width:100%;padding:12px 16px;border:none;background:none;text-align:left;cursor:pointer;font-size:14px;color:var(--text-primary);text-decoration:none;transition:background-color .2s}.mes-dropdown-item:hover{background-color:var(--bg-hover)}.profile-link{color:var(--primary-color)}.logout-item{border-top:1px solid var(--border-light);color:var(--error-color)}.logout-item:hover{background-color:var(--error-light)}.user-info{display:flex;flex-direction:column;gap:2px}.user-name{font-weight:500;font-size:14px;color:var(--text-primary)}.user-position{font-size:12px;color:var(--text-secondary)}.logout-btn{background:none;border:none;font-size:20px;cursor:pointer;color:var(--text-secondary);padding:4px 8px;transition:color .2s}.logout-btn:hover{color:var(--primary-color)}@media(max-width:768px){.user-info{display:none}.avatar{width:32px;height:32px}}\n"] }]
710
+ `, styles: [":host{--primary-color: #1976d2;--primary-hover: #1565c0;--primary-light: rgba(25, 118, 210, .12);--primary-glow: rgba(25, 118, 210, .3);--error-color: #f44336;--error-light: rgba(244, 67, 54, .1);--text-primary: #212121;--text-secondary: #616161;--text-muted: #9e9e9e;--bg-primary: #ffffff;--bg-secondary: #f8f9fa;--bg-hover: #f0f4ff;--border-color: #e0e0e0;--shadow: rgba(0, 0, 0, .12);--shadow-lg: rgba(0, 0, 0, .18)}:host(.theme-dark){--primary-color: #90caf9;--primary-hover: #64b5f6;--primary-light: rgba(144, 202, 249, .12);--primary-glow: rgba(144, 202, 249, .25);--error-color: #ef5350;--error-light: rgba(239, 83, 80, .12);--text-primary: #e0e0e0;--text-secondary: #b0b0b0;--text-muted: #757575;--bg-primary: #1e1e2e;--bg-secondary: #27273a;--bg-hover: #2a2d4a;--border-color: #383850;--shadow: rgba(0, 0, 0, .35);--shadow-lg: rgba(0, 0, 0, .5)}.user-profile-container{display:flex;align-items:center;gap:4px}.login-btn{padding:7px 18px;background-color:var(--primary-color);color:#fff;border:none;border-radius:8px;cursor:pointer;font-weight:500;font-size:13px;letter-spacing:.2px;transition:background-color .2s,transform .15s}.login-btn:hover{background-color:var(--primary-hover);transform:translateY(-1px)}.user-header{display:flex;align-items:center;gap:4px}.notification-btn{position:relative;background:none;border:none;cursor:pointer;padding:8px;border-radius:10px;color:var(--text-secondary);display:flex;align-items:center;justify-content:center;transition:color .2s,background-color .2s}.notification-btn:hover{background-color:var(--primary-light);color:var(--primary-color)}.notification-btn.has-unread{color:var(--primary-color)}.bell-icon{display:block;transition:transform .35s cubic-bezier(.34,1.56,.64,1)}.notification-btn:hover .bell-icon{transform:rotate(-20deg) scale(1.15)}.badge{position:absolute;top:2px;right:2px;background-color:var(--error-color);color:#fff;border-radius:10px;min-width:17px;height:17px;padding:0 4px;display:flex;align-items:center;justify-content:center;font-size:10px;font-weight:700;line-height:1;box-shadow:0 0 0 2px var(--bg-primary);animation:badge-pop .25s cubic-bezier(.34,1.56,.64,1)}@keyframes badge-pop{0%{transform:scale(0)}to{transform:scale(1)}}.user-menu-wrapper{position:relative}.user-menu-btn{background:none;border:none;cursor:pointer;padding:2px;border-radius:50%;display:flex;align-items:center;justify-content:center;transition:transform .2s}.user-menu-btn:hover{transform:scale(1.06)}.avatar-ring{border-radius:50%;padding:2px;border:2px solid transparent;transition:border-color .25s,box-shadow .25s}.avatar-ring.active,.user-menu-btn:hover .avatar-ring{border-color:var(--primary-color);box-shadow:0 0 0 3px var(--primary-glow)}.avatar{width:36px;height:36px;border-radius:50%;object-fit:cover;display:block}.avatar-initial{width:36px;height:36px;border-radius:50%;background:linear-gradient(135deg,var(--primary-color),var(--primary-hover));color:#fff;display:flex;align-items:center;justify-content:center;font-weight:700;font-size:15px}.mes-dropdown-menu{position:absolute;top:calc(100% + 10px);right:0;background:var(--bg-primary);border:1px solid var(--border-color);border-radius:14px;box-shadow:0 8px 32px var(--shadow-lg),0 2px 8px var(--shadow);min-width:220px;z-index:1000;overflow:hidden;animation:dropdown-in .16s cubic-bezier(.16,1,.3,1)}@keyframes dropdown-in{0%{opacity:0;transform:translateY(-8px) scale(.96)}to{opacity:1;transform:translateY(0) scale(1)}}.mes-dropdown-header{display:flex;align-items:center;gap:12px;padding:16px;background:var(--bg-secondary)}.dropdown-avatar-wrap{flex-shrink:0}.dropdown-avatar{width:46px;height:46px;border-radius:50%;object-fit:cover;border:2px solid var(--primary-color);display:block}.dropdown-avatar-initial{width:46px;height:46px;border-radius:50%;background:linear-gradient(135deg,var(--primary-color),var(--primary-hover));color:#fff;display:flex;align-items:center;justify-content:center;font-weight:700;font-size:18px;border:2px solid var(--primary-color)}.dropdown-user-info{display:flex;flex-direction:column;gap:3px;min-width:0}.dropdown-user-name{font-weight:600;font-size:14px;color:var(--text-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.dropdown-user-sub{font-size:11px;color:var(--primary-color);font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.mes-dropdown-divider{height:1px;background:var(--border-color)}.mes-dropdown-item{display:flex;align-items:center;gap:10px;width:100%;padding:11px 16px;border:none;background:none;text-align:left;cursor:pointer;font-size:13.5px;font-weight:500;transition:background-color .15s}.item-icon{flex-shrink:0;opacity:.8}.profile-link{color:var(--primary-color)}.profile-link:hover{background-color:var(--primary-light)}.logout-item{color:var(--error-color)}.logout-item:hover{background-color:var(--error-light)}@media(max-width:768px){.avatar,.avatar-initial{width:32px;height:32px;font-size:13px}}\n"] }]
639
711
  }], ctorParameters: () => [{ type: MesAuthService }, { type: i2.Router }, { type: ThemeService }, { type: i0.ChangeDetectorRef }], propDecorators: { notificationClick: [{
640
712
  type: Output
641
713
  }], themeClass: [{
@@ -717,43 +789,83 @@ class ToastContainerComponent {
717
789
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: ToastContainerComponent, deps: [{ token: ToastService }, { token: ThemeService }], target: i0.ɵɵFactoryTarget.Component });
718
790
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.18", type: ToastContainerComponent, isStandalone: true, selector: "ma-toast-container", host: { properties: { "class": "this.themeClass" } }, ngImport: i0, template: `
719
791
  <div class="toast-container">
720
- <div
721
- *ngFor="let toast of toasts"
722
- class="toast"
723
- [class]="'toast-' + toast.type"
724
- [@slideIn]
725
- >
792
+ <div *ngFor="let toast of toasts" class="toast toast-{{ toast.type }}">
793
+
794
+ <!-- Type icon -->
795
+ <div class="toast-icon">
796
+ <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">
797
+ <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"/>
798
+ </svg>
799
+ <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">
800
+ <path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/>
801
+ </svg>
802
+ <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">
803
+ <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"/>
804
+ </svg>
805
+ <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">
806
+ <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"/>
807
+ </svg>
808
+ </div>
809
+
810
+ <!-- Content -->
726
811
  <div class="toast-content">
727
812
  <div *ngIf="toast.title" class="toast-title">{{ toast.title }}</div>
728
813
  <div class="toast-message" [innerHTML]="toast.message"></div>
729
814
  </div>
815
+
816
+ <!-- Close -->
730
817
  <button class="toast-close" (click)="close(toast.id)" aria-label="Close">
731
-
818
+ <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">
819
+ <line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
820
+ </svg>
732
821
  </button>
822
+
823
+ <!-- Auto-dismiss progress bar -->
824
+ <div class="toast-progress" [style.animation-duration]="(toast.duration || 5000) + 'ms'"></div>
733
825
  </div>
734
826
  </div>
735
- `, isInline: true, styles: [":host{--info-color: #2196f3;--success-color: #4caf50;--warning-color: #ff9800;--error-color: #f44336;--text-primary: #333;--bg-primary: white;--shadow: rgba(0, 0, 0, .15);--text-secondary: #999;--border-color: rgba(0, 0, 0, .1)}:host(.theme-dark){--info-color: #64b5f6;--success-color: #81c784;--warning-color: #ffb74d;--error-color: #ef5350;--text-primary: #e0e0e0;--bg-primary: #1e1e1e;--shadow: rgba(0, 0, 0, .3);--text-secondary: #888;--border-color: rgba(255, 255, 255, .1)}.toast-container{position:fixed;top:20px;right:20px;z-index:9999;pointer-events:none}.toast{display:flex;align-items:flex-start;gap:12px;padding:12px 16px;margin-bottom:12px;border-radius:4px;background:var(--bg-primary);border:1px solid var(--border-color);box-shadow:0 4px 12px var(--shadow);pointer-events:auto;min-width:280px;max-width:400px;animation:slideIn .3s ease-out}.toast-content{flex:1}.toast-title{font-weight:600;font-size:14px;margin-bottom:4px}.toast-message{font-size:13px;line-height:1.4}.toast-close{background:none;border:none;cursor:pointer;font-size:18px;color:var(--text-secondary);padding:0;width:24px;height:24px;display:flex;align-items:center;justify-content:center;flex-shrink:0;transition:color .2s}.toast-close:hover{color:var(--text-primary)}.toast-info{border-left:4px solid var(--info-color)}.toast-info .toast-title{color:var(--info-color)}.toast-info .toast-message{color:var(--text-primary)}.toast-success{border-left:4px solid var(--success-color)}.toast-success .toast-title{color:var(--success-color)}.toast-success .toast-message{color:var(--text-primary)}.toast-warning{border-left:4px solid var(--warning-color)}.toast-warning .toast-title{color:var(--warning-color)}.toast-warning .toast-message{color:var(--text-primary)}.toast-error{border-left:4px solid var(--error-color)}.toast-error .toast-title{color:var(--error-color)}.toast-error .toast-message{color:var(--text-primary)}@keyframes slideIn{0%{transform:translate(400px);opacity:0}to{transform:translate(0);opacity:1}}@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"] }] });
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"] }] });
736
828
  }
737
829
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: ToastContainerComponent, decorators: [{
738
830
  type: Component,
739
831
  args: [{ selector: 'ma-toast-container', standalone: true, imports: [CommonModule], template: `
740
832
  <div class="toast-container">
741
- <div
742
- *ngFor="let toast of toasts"
743
- class="toast"
744
- [class]="'toast-' + toast.type"
745
- [@slideIn]
746
- >
833
+ <div *ngFor="let toast of toasts" class="toast toast-{{ toast.type }}">
834
+
835
+ <!-- Type icon -->
836
+ <div class="toast-icon">
837
+ <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">
838
+ <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"/>
839
+ </svg>
840
+ <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">
841
+ <path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/>
842
+ </svg>
843
+ <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">
844
+ <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"/>
845
+ </svg>
846
+ <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">
847
+ <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"/>
848
+ </svg>
849
+ </div>
850
+
851
+ <!-- Content -->
747
852
  <div class="toast-content">
748
853
  <div *ngIf="toast.title" class="toast-title">{{ toast.title }}</div>
749
854
  <div class="toast-message" [innerHTML]="toast.message"></div>
750
855
  </div>
856
+
857
+ <!-- Close -->
751
858
  <button class="toast-close" (click)="close(toast.id)" aria-label="Close">
752
-
859
+ <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">
860
+ <line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
861
+ </svg>
753
862
  </button>
863
+
864
+ <!-- Auto-dismiss progress bar -->
865
+ <div class="toast-progress" [style.animation-duration]="(toast.duration || 5000) + 'ms'"></div>
754
866
  </div>
755
867
  </div>
756
- `, styles: [":host{--info-color: #2196f3;--success-color: #4caf50;--warning-color: #ff9800;--error-color: #f44336;--text-primary: #333;--bg-primary: white;--shadow: rgba(0, 0, 0, .15);--text-secondary: #999;--border-color: rgba(0, 0, 0, .1)}:host(.theme-dark){--info-color: #64b5f6;--success-color: #81c784;--warning-color: #ffb74d;--error-color: #ef5350;--text-primary: #e0e0e0;--bg-primary: #1e1e1e;--shadow: rgba(0, 0, 0, .3);--text-secondary: #888;--border-color: rgba(255, 255, 255, .1)}.toast-container{position:fixed;top:20px;right:20px;z-index:9999;pointer-events:none}.toast{display:flex;align-items:flex-start;gap:12px;padding:12px 16px;margin-bottom:12px;border-radius:4px;background:var(--bg-primary);border:1px solid var(--border-color);box-shadow:0 4px 12px var(--shadow);pointer-events:auto;min-width:280px;max-width:400px;animation:slideIn .3s ease-out}.toast-content{flex:1}.toast-title{font-weight:600;font-size:14px;margin-bottom:4px}.toast-message{font-size:13px;line-height:1.4}.toast-close{background:none;border:none;cursor:pointer;font-size:18px;color:var(--text-secondary);padding:0;width:24px;height:24px;display:flex;align-items:center;justify-content:center;flex-shrink:0;transition:color .2s}.toast-close:hover{color:var(--text-primary)}.toast-info{border-left:4px solid var(--info-color)}.toast-info .toast-title{color:var(--info-color)}.toast-info .toast-message{color:var(--text-primary)}.toast-success{border-left:4px solid var(--success-color)}.toast-success .toast-title{color:var(--success-color)}.toast-success .toast-message{color:var(--text-primary)}.toast-warning{border-left:4px solid var(--warning-color)}.toast-warning .toast-title{color:var(--warning-color)}.toast-warning .toast-message{color:var(--text-primary)}.toast-error{border-left:4px solid var(--error-color)}.toast-error .toast-title{color:var(--error-color)}.toast-error .toast-message{color:var(--text-primary)}@keyframes slideIn{0%{transform:translate(400px);opacity:0}to{transform:translate(0);opacity:1}}@media(max-width:600px){.toast-container{top:10px;right:10px;left:10px}.toast{min-width:auto;max-width:100%}}\n"] }]
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"] }]
757
869
  }], ctorParameters: () => [{ type: ToastService }, { type: ThemeService }], propDecorators: { themeClass: [{
758
870
  type: HostBinding,
759
871
  args: ['class']
@@ -794,6 +906,23 @@ class NotificationPanelComponent {
794
906
  getNotificationMessage(notification) {
795
907
  return notification.message || '';
796
908
  }
909
+ // Normalize type to string — API may return integer (0/1/2/3) or string ('Info'/'Warning'/'Error'/'Success')
910
+ typeOf(notification) {
911
+ const t = notification.type;
912
+ if (t === 0 || t === 'Info')
913
+ return 'Info';
914
+ if (t === 1 || t === 'Warning')
915
+ return 'Warning';
916
+ if (t === 2 || t === 'Error')
917
+ return 'Error';
918
+ if (t === 3 || t === 'Success')
919
+ return 'Success';
920
+ return 'Info';
921
+ }
922
+ toastType(type) {
923
+ const t = this.typeOf({ type });
924
+ return t.toLowerCase();
925
+ }
797
926
  sanitizer = inject(DomSanitizer);
798
927
  constructor(authService, toastService, themeService) {
799
928
  this.authService = authService;
@@ -814,7 +943,7 @@ class NotificationPanelComponent {
814
943
  .pipe(takeUntil(this.destroy$))
815
944
  .subscribe((notification) => {
816
945
  // Show toast for new notification
817
- this.toastService.show(notification.messageHtml || notification.message || '', notification.title, 'info', 5000);
946
+ this.toastService.show(notification.messageHtml || notification.message || '', notification.title, this.toastType(notification.type), 5000);
818
947
  // Reload notifications list
819
948
  this.loadNotifications();
820
949
  });
@@ -997,25 +1126,29 @@ class NotificationPanelComponent {
997
1126
  <div class="notification-panel" [class.open]="isOpen">
998
1127
  <!-- Header -->
999
1128
  <div class="panel-header">
1000
- <h3>Notifications</h3>
1001
- <button class="close-btn" (click)="close()" title="Close">✕</button>
1129
+ <div class="panel-header-left">
1130
+ <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">
1131
+ <path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/>
1132
+ <path d="M13.73 21a2 2 0 0 1-3.46 0"/>
1133
+ </svg>
1134
+ <h3>Notifications</h3>
1135
+ </div>
1136
+ <button class="close-btn" (click)="close()" title="Close">
1137
+ <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
+ <line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
1139
+ </svg>
1140
+ </button>
1002
1141
  </div>
1003
1142
 
1004
1143
  <!-- Tabs -->
1005
1144
  <div class="tabs">
1006
- <button
1007
- class="tab-btn"
1008
- [class.active]="activeTab === 'unread'"
1009
- (click)="switchTab('unread')"
1010
- >
1011
- Unread ({{ unreadNotifications.length }})
1145
+ <button class="tab-btn" [class.active]="activeTab === 'unread'" (click)="switchTab('unread')">
1146
+ Unread
1147
+ <span class="tab-count" *ngIf="unreadNotifications.length > 0">{{ unreadNotifications.length }}</span>
1012
1148
  </button>
1013
- <button
1014
- class="tab-btn"
1015
- [class.active]="activeTab === 'read'"
1016
- (click)="switchTab('read')"
1017
- >
1018
- Read ({{ readNotifications.length }})
1149
+ <button class="tab-btn" [class.active]="activeTab === 'read'" (click)="switchTab('read')">
1150
+ Read
1151
+ <span class="tab-count read-count" *ngIf="readNotifications.length > 0">{{ readNotifications.length }}</span>
1019
1152
  </button>
1020
1153
  </div>
1021
1154
 
@@ -1028,6 +1161,29 @@ class NotificationPanelComponent {
1028
1161
  [class.unread]="!notification.isRead"
1029
1162
  (click)="openDetails(notification)"
1030
1163
  >
1164
+ <div class="notif-accent"
1165
+ [class.type-info]="typeOf(notification) === 'Info'"
1166
+ [class.type-success]="typeOf(notification) === 'Success'"
1167
+ [class.type-warning]="typeOf(notification) === 'Warning'"
1168
+ [class.type-error]="typeOf(notification) === 'Error'"></div>
1169
+ <div class="notif-type-icon"
1170
+ [class.type-info]="typeOf(notification) === 'Info'"
1171
+ [class.type-success]="typeOf(notification) === 'Success'"
1172
+ [class.type-warning]="typeOf(notification) === 'Warning'"
1173
+ [class.type-error]="typeOf(notification) === 'Error'">
1174
+ <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">
1175
+ <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
+ </svg>
1177
+ <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">
1178
+ <polyline points="20 6 9 17 4 12"/>
1179
+ </svg>
1180
+ <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">
1181
+ <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
+ </svg>
1183
+ <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">
1184
+ <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
+ </svg>
1186
+ </div>
1031
1187
  <div class="notification-content">
1032
1188
  <div class="notification-title">{{ notification.title }}</div>
1033
1189
  <div class="notification-message">{{ getNotificationMessage(notification) }}</div>
@@ -1036,28 +1192,26 @@ class NotificationPanelComponent {
1036
1192
  <span class="time">{{ dateLabels.get(notification.id) }}</span>
1037
1193
  </div>
1038
1194
  </div>
1039
- <button
1040
- class="read-btn"
1041
- (click)="markAsRead(notification.id, $event)"
1042
- title="Mark as read"
1043
- *ngIf="!notification.isRead"
1044
- >
1045
-
1195
+ <button class="icon-btn read-btn" (click)="markAsRead(notification.id, $event)" title="Mark as read" *ngIf="!notification.isRead">
1196
+ <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
+ <polyline points="20 6 9 17 4 12"/>
1198
+ </svg>
1046
1199
  </button>
1047
- <button
1048
- class="delete-btn"
1049
- (click)="delete(notification.id, $event)"
1050
- title="Delete notification"
1051
- *ngIf="notification.isRead"
1052
- >
1053
- 🗑
1200
+ <button class="icon-btn delete-btn" (click)="delete(notification.id, $event)" title="Delete" *ngIf="notification.isRead">
1201
+ <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
+ <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
+ </svg>
1054
1204
  </button>
1055
1205
  </div>
1056
1206
  </ng-container>
1057
1207
 
1058
1208
  <ng-container *ngIf="currentNotifications.length === 0">
1059
1209
  <div class="empty-state">
1060
- No {{ activeTab }} notifications
1210
+ <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">
1211
+ <path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/>
1212
+ <path d="M13.73 21a2 2 0 0 1-3.46 0"/>
1213
+ </svg>
1214
+ <p>No {{ activeTab }} notifications</p>
1061
1215
  </div>
1062
1216
  </ng-container>
1063
1217
  </div>
@@ -1066,13 +1220,16 @@ class NotificationPanelComponent {
1066
1220
  <div class="panel-footer" *ngIf="currentNotifications.length > 0">
1067
1221
  <div class="footer-actions" *ngIf="activeTab === 'unread'">
1068
1222
  <button class="action-btn" (click)="markAllAsRead()" *ngIf="unreadNotifications.length > 0">
1069
- Mark all as read
1223
+ <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>
1224
+ Mark all read
1070
1225
  </button>
1071
1226
  <button class="action-btn delete-all-btn" (click)="deleteAllUnread()" *ngIf="unreadNotifications.length > 0">
1227
+ <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>
1072
1228
  Delete all
1073
1229
  </button>
1074
1230
  </div>
1075
1231
  <button class="action-btn delete-all-btn" (click)="deleteAllRead()" *ngIf="activeTab === 'read' && readNotifications.length > 0">
1232
+ <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>
1076
1233
  Delete all
1077
1234
  </button>
1078
1235
  </div>
@@ -1081,9 +1238,33 @@ class NotificationPanelComponent {
1081
1238
  <!-- Details Modal -->
1082
1239
  <div class="modal-overlay" *ngIf="selectedNotification" (click)="closeDetails()">
1083
1240
  <div class="modal-container" (click)="$event.stopPropagation()">
1084
- <div class="modal-header">
1085
- <h3>{{ selectedNotification.title }}</h3>
1086
- <button class="close-btn" (click)="closeDetails()" title="Close">✕</button>
1241
+ <div class="modal-header"
1242
+ [class.modal-type-info]="typeOf(selectedNotification) === 'Info'"
1243
+ [class.modal-type-success]="typeOf(selectedNotification) === 'Success'"
1244
+ [class.modal-type-warning]="typeOf(selectedNotification) === 'Warning'"
1245
+ [class.modal-type-error]="typeOf(selectedNotification) === 'Error'">
1246
+ <div class="modal-header-left">
1247
+ <div class="modal-type-icon">
1248
+ <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">
1249
+ <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"/>
1250
+ </svg>
1251
+ <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">
1252
+ <path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/>
1253
+ </svg>
1254
+ <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">
1255
+ <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"/>
1256
+ </svg>
1257
+ <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">
1258
+ <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"/>
1259
+ </svg>
1260
+ </div>
1261
+ <h3>{{ selectedNotification.title }}</h3>
1262
+ </div>
1263
+ <button class="close-btn" (click)="closeDetails()" title="Close">
1264
+ <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
+ <line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
1266
+ </svg>
1267
+ </button>
1087
1268
  </div>
1088
1269
  <div class="modal-meta">
1089
1270
  <span class="app-name">{{ selectedNotification.sourceAppName }}</span>
@@ -1095,7 +1276,7 @@ class NotificationPanelComponent {
1095
1276
  </div>
1096
1277
  </div>
1097
1278
  </div>
1098
- `, isInline: true, styles: [":host{display:block;position:relative;--primary-color: #1976d2;--primary-hover: #1565c0;--success-color: #4caf50;--error-color: #f44336;--text-primary: #333;--text-secondary: #666;--text-muted: #999;--bg-primary: white;--bg-secondary: #f5f5f5;--bg-tertiary: #fafafa;--bg-hover: #f5f5f5;--bg-unread: #e3f2fd;--border-color: #e0e0e0;--border-light: #f0f0f0;--shadow: rgba(0, 0, 0, .1)}:host(.theme-dark){display:block;position:relative;--primary-color: #90caf9;--primary-hover: #64b5f6;--success-color: #81c784;--error-color: #ef5350;--text-primary: #e0e0e0;--text-secondary: #b0b0b0;--text-muted: #888;--bg-primary: #1e1e1e;--bg-secondary: #2d2d2d;--bg-tertiary: #252525;--bg-hover: #333;--bg-unread: rgba(144, 202, 249, .1);--border-color: #404040;--border-light: #333;--shadow: rgba(0, 0, 0, .3)}.notification-panel{position:fixed;top:0;right:-350px;width:350px;height:100vh;background:var(--bg-primary);box-shadow:-2px 0 8px var(--shadow);display:flex;flex-direction:column;z-index:1030;transition:right .3s ease}.notification-panel.open{right:0}.panel-header{display:flex;justify-content:space-between;align-items:center;padding:16px;border-bottom:1px solid var(--border-color);background-color:var(--bg-secondary)}.panel-header h3{margin:0;font-size:18px;color:var(--text-primary)}.close-btn{background:none;border:none;font-size:20px;cursor:pointer;color:var(--text-secondary);padding:0;width:32px;height:32px;display:flex;align-items:center;justify-content:center;transition:color .2s}.close-btn:hover{color:var(--text-primary)}.tabs{display:flex;border-bottom:1px solid var(--border-color);background-color:var(--bg-secondary)}.tab-btn{flex:1;padding:12px 16px;background:none;border:none;color:var(--text-secondary);cursor:pointer;font-size:14px;font-weight:500;transition:all .2s;border-bottom:2px solid transparent}.tab-btn:hover{background-color:var(--bg-hover);color:var(--text-primary)}.tab-btn.active{color:var(--primary-color);border-bottom-color:var(--primary-color);background-color:var(--bg-primary)}.notifications-list{flex:1;overflow-y:auto}.notification-item{display:flex;gap:12px;padding:12px 16px;border-bottom:1px solid var(--border-light);cursor:pointer;background-color:var(--bg-tertiary);transition:background-color .2s}.notification-item:hover{background-color:var(--bg-hover)}.notification-item.unread{background-color:var(--bg-unread)}.notification-content{flex:1;min-width:0}.notification-title{font-weight:600;color:var(--text-primary);font-size:14px;margin-bottom:4px}.notification-message{color:var(--text-secondary);font-size:12px;line-height:1.4;margin-bottom:6px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.notification-meta{display:flex;justify-content:space-between;font-size:12px;color:var(--text-muted)}.app-name{font-weight:500;color:var(--primary-color)}.read-btn{background:none;border:none;color:var(--text-muted);cursor:pointer;font-size:14px;padding:0;width:24px;height:24px;display:flex;align-items:center;justify-content:center;flex-shrink:0;transition:color .2s}.read-btn:hover{color:var(--success-color)}.delete-btn{background:none;border:none;color:var(--text-muted);cursor:pointer;font-size:14px;padding:0;width:24px;height:24px;display:flex;align-items:center;justify-content:center;flex-shrink:0;transition:color .2s}.delete-btn:hover{color:var(--error-color)}.empty-state{display:flex;align-items:center;justify-content:center;height:100%;color:var(--text-muted);font-size:14px}.panel-footer{padding:12px 16px;border-top:1px solid var(--border-color);background-color:var(--bg-secondary)}.footer-actions{display:flex;gap:8px}.footer-actions .action-btn{flex:1}.action-btn{width:100%;padding:8px;background-color:var(--primary-color);color:#fff;border:none;border-radius:4px;cursor:pointer;font-weight:500;transition:background-color .2s}.action-btn:hover{background-color:var(--primary-hover)}.delete-all-btn{background-color:var(--error-color);color:#fff}.delete-all-btn:hover{background-color:#d32f2f}.modal-overlay{position:fixed;inset:0;width:100vw;height:100vh;background-color:#00000080;display:flex;align-items:center;justify-content:center;z-index:1060}.modal-container{background:var(--bg-primary);border-radius:8px;width:90%;max-width:600px;max-height:80vh;display:flex;flex-direction:column;box-shadow:0 4px 20px var(--shadow)}.modal-header{display:flex;justify-content:space-between;align-items:center;padding:16px 20px;border-bottom:1px solid var(--border-color);background-color:var(--bg-secondary);border-radius:8px 8px 0 0}.modal-header h3{margin:0;font-size:18px;color:var(--text-primary)}.modal-meta{display:flex;justify-content:space-between;padding:8px 20px;font-size:12px;color:var(--text-muted);background-color:var(--bg-tertiary);border-bottom:1px solid var(--border-light)}.modal-body{padding:20px;overflow-y:auto;flex:1;color:var(--text-primary);font-size:14px;line-height:1.6}.modal-footer{padding:12px 20px;border-top:1px solid var(--border-color);background-color:var(--bg-secondary);border-radius:0 0 8px 8px;display:flex;justify-content:flex-end}.modal-footer .action-btn{width:auto;padding:8px 24px}@media(max-width:600px){.notification-panel{width:100%;right:-100%}.modal-container{width:95%;max-height:90vh}}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }] });
1279
+ `, isInline: true, styles: [":host{display:block;position:relative;--primary-color: #1976d2;--primary-hover: #1565c0;--primary-light: rgba(25, 118, 210, .1);--success-color: #43a047;--error-color: #f44336;--error-hover: #d32f2f;--unread-accent: #1976d2;--info-color: #2196f3;--info-bg: rgba(33, 150, 243, .1);--success-bg: rgba(67, 160, 71, .1);--warning-color: #f57c00;--warning-bg: rgba(245, 124, 0, .1);--error-bg: rgba(244, 67, 54, .1);--text-primary: #212121;--text-secondary: #616161;--text-muted: #9e9e9e;--bg-primary: #ffffff;--bg-secondary: #f8f9fa;--bg-hover: #f0f4ff;--bg-unread: rgba(25, 118, 210, .06);--border-color: #e0e0e0;--border-light: #eeeeee;--shadow: rgba(0, 0, 0, .15)}:host(.theme-dark){display:block;position:relative;--primary-color: #90caf9;--primary-hover: #64b5f6;--primary-light: rgba(144, 202, 249, .1);--success-color: #66bb6a;--error-color: #ef5350;--error-hover: #c62828;--unread-accent: #90caf9;--info-color: #64b5f6;--info-bg: rgba(100, 181, 246, .12);--success-bg: rgba(102, 187, 106, .12);--warning-color: #ffb74d;--warning-bg: rgba(255, 183, 77, .12);--error-bg: rgba(239, 83, 80, .12);--text-primary: #e0e0e0;--text-secondary: #b0b0b0;--text-muted: #757575;--bg-primary: #1e1e2e;--bg-secondary: #27273a;--bg-hover: #2a2d4a;--bg-unread: rgba(144, 202, 249, .08);--border-color: #383850;--border-light: #2e2e42;--shadow: rgba(0, 0, 0, .4)}.notification-panel{position:fixed;top:0;right:-360px;width:360px;height:100vh;background:var(--bg-primary);box-shadow:-4px 0 24px var(--shadow);display:flex;flex-direction:column;z-index:1030;transition:right .3s cubic-bezier(.16,1,.3,1)}.notification-panel.open{right:0}.panel-header{display:flex;justify-content:space-between;align-items:center;padding:16px 18px;border-bottom:1px solid var(--border-color);background:var(--bg-secondary);flex-shrink:0}.panel-header-left{display:flex;align-items:center;gap:9px;color:var(--primary-color)}.panel-header h3{margin:0;font-size:16px;font-weight:700;color:var(--text-primary);letter-spacing:.1px}.close-btn{background:none;border:none;cursor:pointer;color:var(--text-muted);width:32px;height:32px;border-radius:8px;display:flex;align-items:center;justify-content:center;transition:color .2s,background-color .2s}.close-btn:hover{color:var(--text-primary);background-color:var(--bg-hover)}.tabs{display:flex;gap:6px;padding:10px 14px;border-bottom:1px solid var(--border-color);background:var(--bg-secondary);flex-shrink:0}.tab-btn{flex:1;display:flex;align-items:center;justify-content:center;gap:6px;padding:7px 12px;background:none;border:1px solid var(--border-color);border-radius:20px;color:var(--text-secondary);cursor:pointer;font-size:13px;font-weight:500;transition:all .18s}.tab-btn:hover{background:var(--bg-hover);color:var(--primary-color);border-color:var(--primary-color)}.tab-btn.active{background:var(--primary-color);border-color:var(--primary-color);color:#fff}.tab-count{background:#ffffff40;border-radius:10px;min-width:18px;height:18px;padding:0 5px;font-size:10px;font-weight:700;display:flex;align-items:center;justify-content:center}.tab-btn:not(.active) .tab-count{background:var(--error-color);color:#fff}.read-count{background:#ffffff40}.tab-btn:not(.active) .read-count{background:var(--text-muted)}.notifications-list{flex:1;overflow-y:auto}.notification-item{display:flex;align-items:flex-start;gap:0;border-bottom:1px solid var(--border-light);cursor:pointer;background:var(--bg-primary);transition:background-color .15s;position:relative}.notification-item:hover{background:var(--bg-hover)}.notification-item.unread{background:var(--bg-unread)}.notif-accent{width:3px;align-self:stretch;flex-shrink:0;background:transparent;border-radius:0 2px 2px 0;opacity:.3}.notification-item.unread .notif-accent{opacity:1}.notif-accent.type-info{background:var(--info-color)}.notif-accent.type-success{background:var(--success-color)}.notif-accent.type-warning{background:var(--warning-color)}.notif-accent.type-error{background:var(--error-color)}.notif-type-icon{flex-shrink:0;width:26px;height:26px;border-radius:7px;display:flex;align-items:center;justify-content:center;align-self:center;margin-left:10px}.notif-type-icon.type-info{color:var(--info-color);background:var(--info-bg)}.notif-type-icon.type-success{color:var(--success-color);background:var(--success-bg)}.notif-type-icon.type-warning{color:var(--warning-color);background:var(--warning-bg)}.notif-type-icon.type-error{color:var(--error-color);background:var(--error-bg)}.notification-content{flex:1;min-width:0;padding:12px 8px 12px 12px}.notification-title{font-weight:600;color:var(--text-primary);font-size:13.5px;margin-bottom:3px;line-height:1.35}.notification-message{color:var(--text-secondary);font-size:12px;line-height:1.45;margin-bottom:7px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.notification-meta{display:flex;justify-content:space-between;font-size:11px;color:var(--text-muted)}.app-name{font-weight:600;color:var(--primary-color)}.icon-btn{background:none;border:none;cursor:pointer;width:32px;height:32px;border-radius:8px;display:flex;align-items:center;justify-content:center;flex-shrink:0;align-self:center;margin-right:8px;transition:color .15s,background-color .15s;color:var(--text-muted)}.read-btn:hover{color:var(--success-color);background:#43a0471a}.delete-btn:hover{color:var(--error-color);background:#f443361a}.empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;height:100%;padding:40px 20px;color:var(--text-muted)}.empty-icon{opacity:.35}.empty-state p{margin:0;font-size:13px}.panel-footer{padding:10px 14px;border-top:1px solid var(--border-color);background:var(--bg-secondary);flex-shrink:0}.footer-actions{display:flex;gap:8px}.footer-actions .action-btn{flex:1}.action-btn{display:flex;align-items:center;justify-content:center;gap:6px;width:100%;padding:8px 12px;background:var(--primary-color);color:#fff;border:none;border-radius:8px;cursor:pointer;font-size:12.5px;font-weight:600;transition:background-color .18s,transform .12s}.action-btn:hover{background:var(--primary-hover);transform:translateY(-1px)}.delete-all-btn{background:var(--error-color)}.delete-all-btn:hover{background:var(--error-hover)}.modal-overlay{position:fixed;inset:0;background:#00000080;display:flex;align-items:center;justify-content:center;z-index:1060;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}.modal-container{background:var(--bg-primary);border-radius:14px;width:90%;max-width:580px;max-height:80vh;display:flex;flex-direction:column;box-shadow:0 16px 48px #00000040;animation:modal-in .2s cubic-bezier(.16,1,.3,1)}@keyframes modal-in{0%{opacity:0;transform:scale(.94) translateY(8px)}to{opacity:1;transform:scale(1) translateY(0)}}.modal-header{display:flex;justify-content:space-between;align-items:center;padding:16px 20px;border-bottom:1px solid var(--border-color);background:var(--bg-secondary);border-radius:14px 14px 0 0;border-top:3px solid transparent}.modal-header.modal-type-info{border-top-color:var(--info-color)}.modal-header.modal-type-success{border-top-color:var(--success-color)}.modal-header.modal-type-warning{border-top-color:var(--warning-color)}.modal-header.modal-type-error{border-top-color:var(--error-color)}.modal-header-left{display:flex;align-items:center;gap:10px;min-width:0}.modal-type-icon{flex-shrink:0;width:32px;height:32px;border-radius:8px;display:flex;align-items:center;justify-content:center}.modal-type-info .modal-type-icon{color:var(--info-color);background:var(--info-bg)}.modal-type-success .modal-type-icon{color:var(--success-color);background:var(--success-bg)}.modal-type-warning .modal-type-icon{color:var(--warning-color);background:var(--warning-bg)}.modal-type-error .modal-type-icon{color:var(--error-color);background:var(--error-bg)}.modal-header h3{margin:0;font-size:15px;font-weight:700;color:var(--text-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.modal-meta{display:flex;justify-content:space-between;padding:8px 20px;font-size:11.5px;color:var(--text-muted);border-bottom:1px solid var(--border-light)}.modal-body{padding:20px;overflow-y:auto;flex:1;color:var(--text-primary);font-size:14px;line-height:1.65}.modal-footer{padding:12px 20px;border-top:1px solid var(--border-color);background:var(--bg-secondary);border-radius:0 0 14px 14px;display:flex;justify-content:flex-end}.modal-footer .action-btn{width:auto;padding:8px 24px}@media(max-width:600px){.notification-panel{width:100%;right:-100%}.modal-container{width:95%;max-height:90vh}}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }] });
1099
1280
  }
1100
1281
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: NotificationPanelComponent, decorators: [{
1101
1282
  type: Component,
@@ -1103,25 +1284,29 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
1103
1284
  <div class="notification-panel" [class.open]="isOpen">
1104
1285
  <!-- Header -->
1105
1286
  <div class="panel-header">
1106
- <h3>Notifications</h3>
1107
- <button class="close-btn" (click)="close()" title="Close">✕</button>
1287
+ <div class="panel-header-left">
1288
+ <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">
1289
+ <path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/>
1290
+ <path d="M13.73 21a2 2 0 0 1-3.46 0"/>
1291
+ </svg>
1292
+ <h3>Notifications</h3>
1293
+ </div>
1294
+ <button class="close-btn" (click)="close()" title="Close">
1295
+ <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
+ <line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
1297
+ </svg>
1298
+ </button>
1108
1299
  </div>
1109
1300
 
1110
1301
  <!-- Tabs -->
1111
1302
  <div class="tabs">
1112
- <button
1113
- class="tab-btn"
1114
- [class.active]="activeTab === 'unread'"
1115
- (click)="switchTab('unread')"
1116
- >
1117
- Unread ({{ unreadNotifications.length }})
1303
+ <button class="tab-btn" [class.active]="activeTab === 'unread'" (click)="switchTab('unread')">
1304
+ Unread
1305
+ <span class="tab-count" *ngIf="unreadNotifications.length > 0">{{ unreadNotifications.length }}</span>
1118
1306
  </button>
1119
- <button
1120
- class="tab-btn"
1121
- [class.active]="activeTab === 'read'"
1122
- (click)="switchTab('read')"
1123
- >
1124
- Read ({{ readNotifications.length }})
1307
+ <button class="tab-btn" [class.active]="activeTab === 'read'" (click)="switchTab('read')">
1308
+ Read
1309
+ <span class="tab-count read-count" *ngIf="readNotifications.length > 0">{{ readNotifications.length }}</span>
1125
1310
  </button>
1126
1311
  </div>
1127
1312
 
@@ -1134,6 +1319,29 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
1134
1319
  [class.unread]="!notification.isRead"
1135
1320
  (click)="openDetails(notification)"
1136
1321
  >
1322
+ <div class="notif-accent"
1323
+ [class.type-info]="typeOf(notification) === 'Info'"
1324
+ [class.type-success]="typeOf(notification) === 'Success'"
1325
+ [class.type-warning]="typeOf(notification) === 'Warning'"
1326
+ [class.type-error]="typeOf(notification) === 'Error'"></div>
1327
+ <div class="notif-type-icon"
1328
+ [class.type-info]="typeOf(notification) === 'Info'"
1329
+ [class.type-success]="typeOf(notification) === 'Success'"
1330
+ [class.type-warning]="typeOf(notification) === 'Warning'"
1331
+ [class.type-error]="typeOf(notification) === 'Error'">
1332
+ <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">
1333
+ <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
+ </svg>
1335
+ <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">
1336
+ <polyline points="20 6 9 17 4 12"/>
1337
+ </svg>
1338
+ <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">
1339
+ <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
+ </svg>
1341
+ <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">
1342
+ <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
+ </svg>
1344
+ </div>
1137
1345
  <div class="notification-content">
1138
1346
  <div class="notification-title">{{ notification.title }}</div>
1139
1347
  <div class="notification-message">{{ getNotificationMessage(notification) }}</div>
@@ -1142,28 +1350,26 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
1142
1350
  <span class="time">{{ dateLabels.get(notification.id) }}</span>
1143
1351
  </div>
1144
1352
  </div>
1145
- <button
1146
- class="read-btn"
1147
- (click)="markAsRead(notification.id, $event)"
1148
- title="Mark as read"
1149
- *ngIf="!notification.isRead"
1150
- >
1151
-
1353
+ <button class="icon-btn read-btn" (click)="markAsRead(notification.id, $event)" title="Mark as read" *ngIf="!notification.isRead">
1354
+ <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
+ <polyline points="20 6 9 17 4 12"/>
1356
+ </svg>
1152
1357
  </button>
1153
- <button
1154
- class="delete-btn"
1155
- (click)="delete(notification.id, $event)"
1156
- title="Delete notification"
1157
- *ngIf="notification.isRead"
1158
- >
1159
- 🗑
1358
+ <button class="icon-btn delete-btn" (click)="delete(notification.id, $event)" title="Delete" *ngIf="notification.isRead">
1359
+ <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
+ <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
+ </svg>
1160
1362
  </button>
1161
1363
  </div>
1162
1364
  </ng-container>
1163
1365
 
1164
1366
  <ng-container *ngIf="currentNotifications.length === 0">
1165
1367
  <div class="empty-state">
1166
- No {{ activeTab }} notifications
1368
+ <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">
1369
+ <path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/>
1370
+ <path d="M13.73 21a2 2 0 0 1-3.46 0"/>
1371
+ </svg>
1372
+ <p>No {{ activeTab }} notifications</p>
1167
1373
  </div>
1168
1374
  </ng-container>
1169
1375
  </div>
@@ -1172,13 +1378,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
1172
1378
  <div class="panel-footer" *ngIf="currentNotifications.length > 0">
1173
1379
  <div class="footer-actions" *ngIf="activeTab === 'unread'">
1174
1380
  <button class="action-btn" (click)="markAllAsRead()" *ngIf="unreadNotifications.length > 0">
1175
- Mark all as read
1381
+ <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>
1382
+ Mark all read
1176
1383
  </button>
1177
1384
  <button class="action-btn delete-all-btn" (click)="deleteAllUnread()" *ngIf="unreadNotifications.length > 0">
1385
+ <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>
1178
1386
  Delete all
1179
1387
  </button>
1180
1388
  </div>
1181
1389
  <button class="action-btn delete-all-btn" (click)="deleteAllRead()" *ngIf="activeTab === 'read' && readNotifications.length > 0">
1390
+ <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>
1182
1391
  Delete all
1183
1392
  </button>
1184
1393
  </div>
@@ -1187,9 +1396,33 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
1187
1396
  <!-- Details Modal -->
1188
1397
  <div class="modal-overlay" *ngIf="selectedNotification" (click)="closeDetails()">
1189
1398
  <div class="modal-container" (click)="$event.stopPropagation()">
1190
- <div class="modal-header">
1191
- <h3>{{ selectedNotification.title }}</h3>
1192
- <button class="close-btn" (click)="closeDetails()" title="Close">✕</button>
1399
+ <div class="modal-header"
1400
+ [class.modal-type-info]="typeOf(selectedNotification) === 'Info'"
1401
+ [class.modal-type-success]="typeOf(selectedNotification) === 'Success'"
1402
+ [class.modal-type-warning]="typeOf(selectedNotification) === 'Warning'"
1403
+ [class.modal-type-error]="typeOf(selectedNotification) === 'Error'">
1404
+ <div class="modal-header-left">
1405
+ <div class="modal-type-icon">
1406
+ <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">
1407
+ <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"/>
1408
+ </svg>
1409
+ <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">
1410
+ <path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/>
1411
+ </svg>
1412
+ <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">
1413
+ <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"/>
1414
+ </svg>
1415
+ <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">
1416
+ <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"/>
1417
+ </svg>
1418
+ </div>
1419
+ <h3>{{ selectedNotification.title }}</h3>
1420
+ </div>
1421
+ <button class="close-btn" (click)="closeDetails()" title="Close">
1422
+ <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
+ <line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
1424
+ </svg>
1425
+ </button>
1193
1426
  </div>
1194
1427
  <div class="modal-meta">
1195
1428
  <span class="app-name">{{ selectedNotification.sourceAppName }}</span>
@@ -1201,7 +1434,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
1201
1434
  </div>
1202
1435
  </div>
1203
1436
  </div>
1204
- `, styles: [":host{display:block;position:relative;--primary-color: #1976d2;--primary-hover: #1565c0;--success-color: #4caf50;--error-color: #f44336;--text-primary: #333;--text-secondary: #666;--text-muted: #999;--bg-primary: white;--bg-secondary: #f5f5f5;--bg-tertiary: #fafafa;--bg-hover: #f5f5f5;--bg-unread: #e3f2fd;--border-color: #e0e0e0;--border-light: #f0f0f0;--shadow: rgba(0, 0, 0, .1)}:host(.theme-dark){display:block;position:relative;--primary-color: #90caf9;--primary-hover: #64b5f6;--success-color: #81c784;--error-color: #ef5350;--text-primary: #e0e0e0;--text-secondary: #b0b0b0;--text-muted: #888;--bg-primary: #1e1e1e;--bg-secondary: #2d2d2d;--bg-tertiary: #252525;--bg-hover: #333;--bg-unread: rgba(144, 202, 249, .1);--border-color: #404040;--border-light: #333;--shadow: rgba(0, 0, 0, .3)}.notification-panel{position:fixed;top:0;right:-350px;width:350px;height:100vh;background:var(--bg-primary);box-shadow:-2px 0 8px var(--shadow);display:flex;flex-direction:column;z-index:1030;transition:right .3s ease}.notification-panel.open{right:0}.panel-header{display:flex;justify-content:space-between;align-items:center;padding:16px;border-bottom:1px solid var(--border-color);background-color:var(--bg-secondary)}.panel-header h3{margin:0;font-size:18px;color:var(--text-primary)}.close-btn{background:none;border:none;font-size:20px;cursor:pointer;color:var(--text-secondary);padding:0;width:32px;height:32px;display:flex;align-items:center;justify-content:center;transition:color .2s}.close-btn:hover{color:var(--text-primary)}.tabs{display:flex;border-bottom:1px solid var(--border-color);background-color:var(--bg-secondary)}.tab-btn{flex:1;padding:12px 16px;background:none;border:none;color:var(--text-secondary);cursor:pointer;font-size:14px;font-weight:500;transition:all .2s;border-bottom:2px solid transparent}.tab-btn:hover{background-color:var(--bg-hover);color:var(--text-primary)}.tab-btn.active{color:var(--primary-color);border-bottom-color:var(--primary-color);background-color:var(--bg-primary)}.notifications-list{flex:1;overflow-y:auto}.notification-item{display:flex;gap:12px;padding:12px 16px;border-bottom:1px solid var(--border-light);cursor:pointer;background-color:var(--bg-tertiary);transition:background-color .2s}.notification-item:hover{background-color:var(--bg-hover)}.notification-item.unread{background-color:var(--bg-unread)}.notification-content{flex:1;min-width:0}.notification-title{font-weight:600;color:var(--text-primary);font-size:14px;margin-bottom:4px}.notification-message{color:var(--text-secondary);font-size:12px;line-height:1.4;margin-bottom:6px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.notification-meta{display:flex;justify-content:space-between;font-size:12px;color:var(--text-muted)}.app-name{font-weight:500;color:var(--primary-color)}.read-btn{background:none;border:none;color:var(--text-muted);cursor:pointer;font-size:14px;padding:0;width:24px;height:24px;display:flex;align-items:center;justify-content:center;flex-shrink:0;transition:color .2s}.read-btn:hover{color:var(--success-color)}.delete-btn{background:none;border:none;color:var(--text-muted);cursor:pointer;font-size:14px;padding:0;width:24px;height:24px;display:flex;align-items:center;justify-content:center;flex-shrink:0;transition:color .2s}.delete-btn:hover{color:var(--error-color)}.empty-state{display:flex;align-items:center;justify-content:center;height:100%;color:var(--text-muted);font-size:14px}.panel-footer{padding:12px 16px;border-top:1px solid var(--border-color);background-color:var(--bg-secondary)}.footer-actions{display:flex;gap:8px}.footer-actions .action-btn{flex:1}.action-btn{width:100%;padding:8px;background-color:var(--primary-color);color:#fff;border:none;border-radius:4px;cursor:pointer;font-weight:500;transition:background-color .2s}.action-btn:hover{background-color:var(--primary-hover)}.delete-all-btn{background-color:var(--error-color);color:#fff}.delete-all-btn:hover{background-color:#d32f2f}.modal-overlay{position:fixed;inset:0;width:100vw;height:100vh;background-color:#00000080;display:flex;align-items:center;justify-content:center;z-index:1060}.modal-container{background:var(--bg-primary);border-radius:8px;width:90%;max-width:600px;max-height:80vh;display:flex;flex-direction:column;box-shadow:0 4px 20px var(--shadow)}.modal-header{display:flex;justify-content:space-between;align-items:center;padding:16px 20px;border-bottom:1px solid var(--border-color);background-color:var(--bg-secondary);border-radius:8px 8px 0 0}.modal-header h3{margin:0;font-size:18px;color:var(--text-primary)}.modal-meta{display:flex;justify-content:space-between;padding:8px 20px;font-size:12px;color:var(--text-muted);background-color:var(--bg-tertiary);border-bottom:1px solid var(--border-light)}.modal-body{padding:20px;overflow-y:auto;flex:1;color:var(--text-primary);font-size:14px;line-height:1.6}.modal-footer{padding:12px 20px;border-top:1px solid var(--border-color);background-color:var(--bg-secondary);border-radius:0 0 8px 8px;display:flex;justify-content:flex-end}.modal-footer .action-btn{width:auto;padding:8px 24px}@media(max-width:600px){.notification-panel{width:100%;right:-100%}.modal-container{width:95%;max-height:90vh}}\n"] }]
1437
+ `, styles: [":host{display:block;position:relative;--primary-color: #1976d2;--primary-hover: #1565c0;--primary-light: rgba(25, 118, 210, .1);--success-color: #43a047;--error-color: #f44336;--error-hover: #d32f2f;--unread-accent: #1976d2;--info-color: #2196f3;--info-bg: rgba(33, 150, 243, .1);--success-bg: rgba(67, 160, 71, .1);--warning-color: #f57c00;--warning-bg: rgba(245, 124, 0, .1);--error-bg: rgba(244, 67, 54, .1);--text-primary: #212121;--text-secondary: #616161;--text-muted: #9e9e9e;--bg-primary: #ffffff;--bg-secondary: #f8f9fa;--bg-hover: #f0f4ff;--bg-unread: rgba(25, 118, 210, .06);--border-color: #e0e0e0;--border-light: #eeeeee;--shadow: rgba(0, 0, 0, .15)}:host(.theme-dark){display:block;position:relative;--primary-color: #90caf9;--primary-hover: #64b5f6;--primary-light: rgba(144, 202, 249, .1);--success-color: #66bb6a;--error-color: #ef5350;--error-hover: #c62828;--unread-accent: #90caf9;--info-color: #64b5f6;--info-bg: rgba(100, 181, 246, .12);--success-bg: rgba(102, 187, 106, .12);--warning-color: #ffb74d;--warning-bg: rgba(255, 183, 77, .12);--error-bg: rgba(239, 83, 80, .12);--text-primary: #e0e0e0;--text-secondary: #b0b0b0;--text-muted: #757575;--bg-primary: #1e1e2e;--bg-secondary: #27273a;--bg-hover: #2a2d4a;--bg-unread: rgba(144, 202, 249, .08);--border-color: #383850;--border-light: #2e2e42;--shadow: rgba(0, 0, 0, .4)}.notification-panel{position:fixed;top:0;right:-360px;width:360px;height:100vh;background:var(--bg-primary);box-shadow:-4px 0 24px var(--shadow);display:flex;flex-direction:column;z-index:1030;transition:right .3s cubic-bezier(.16,1,.3,1)}.notification-panel.open{right:0}.panel-header{display:flex;justify-content:space-between;align-items:center;padding:16px 18px;border-bottom:1px solid var(--border-color);background:var(--bg-secondary);flex-shrink:0}.panel-header-left{display:flex;align-items:center;gap:9px;color:var(--primary-color)}.panel-header h3{margin:0;font-size:16px;font-weight:700;color:var(--text-primary);letter-spacing:.1px}.close-btn{background:none;border:none;cursor:pointer;color:var(--text-muted);width:32px;height:32px;border-radius:8px;display:flex;align-items:center;justify-content:center;transition:color .2s,background-color .2s}.close-btn:hover{color:var(--text-primary);background-color:var(--bg-hover)}.tabs{display:flex;gap:6px;padding:10px 14px;border-bottom:1px solid var(--border-color);background:var(--bg-secondary);flex-shrink:0}.tab-btn{flex:1;display:flex;align-items:center;justify-content:center;gap:6px;padding:7px 12px;background:none;border:1px solid var(--border-color);border-radius:20px;color:var(--text-secondary);cursor:pointer;font-size:13px;font-weight:500;transition:all .18s}.tab-btn:hover{background:var(--bg-hover);color:var(--primary-color);border-color:var(--primary-color)}.tab-btn.active{background:var(--primary-color);border-color:var(--primary-color);color:#fff}.tab-count{background:#ffffff40;border-radius:10px;min-width:18px;height:18px;padding:0 5px;font-size:10px;font-weight:700;display:flex;align-items:center;justify-content:center}.tab-btn:not(.active) .tab-count{background:var(--error-color);color:#fff}.read-count{background:#ffffff40}.tab-btn:not(.active) .read-count{background:var(--text-muted)}.notifications-list{flex:1;overflow-y:auto}.notification-item{display:flex;align-items:flex-start;gap:0;border-bottom:1px solid var(--border-light);cursor:pointer;background:var(--bg-primary);transition:background-color .15s;position:relative}.notification-item:hover{background:var(--bg-hover)}.notification-item.unread{background:var(--bg-unread)}.notif-accent{width:3px;align-self:stretch;flex-shrink:0;background:transparent;border-radius:0 2px 2px 0;opacity:.3}.notification-item.unread .notif-accent{opacity:1}.notif-accent.type-info{background:var(--info-color)}.notif-accent.type-success{background:var(--success-color)}.notif-accent.type-warning{background:var(--warning-color)}.notif-accent.type-error{background:var(--error-color)}.notif-type-icon{flex-shrink:0;width:26px;height:26px;border-radius:7px;display:flex;align-items:center;justify-content:center;align-self:center;margin-left:10px}.notif-type-icon.type-info{color:var(--info-color);background:var(--info-bg)}.notif-type-icon.type-success{color:var(--success-color);background:var(--success-bg)}.notif-type-icon.type-warning{color:var(--warning-color);background:var(--warning-bg)}.notif-type-icon.type-error{color:var(--error-color);background:var(--error-bg)}.notification-content{flex:1;min-width:0;padding:12px 8px 12px 12px}.notification-title{font-weight:600;color:var(--text-primary);font-size:13.5px;margin-bottom:3px;line-height:1.35}.notification-message{color:var(--text-secondary);font-size:12px;line-height:1.45;margin-bottom:7px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.notification-meta{display:flex;justify-content:space-between;font-size:11px;color:var(--text-muted)}.app-name{font-weight:600;color:var(--primary-color)}.icon-btn{background:none;border:none;cursor:pointer;width:32px;height:32px;border-radius:8px;display:flex;align-items:center;justify-content:center;flex-shrink:0;align-self:center;margin-right:8px;transition:color .15s,background-color .15s;color:var(--text-muted)}.read-btn:hover{color:var(--success-color);background:#43a0471a}.delete-btn:hover{color:var(--error-color);background:#f443361a}.empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;height:100%;padding:40px 20px;color:var(--text-muted)}.empty-icon{opacity:.35}.empty-state p{margin:0;font-size:13px}.panel-footer{padding:10px 14px;border-top:1px solid var(--border-color);background:var(--bg-secondary);flex-shrink:0}.footer-actions{display:flex;gap:8px}.footer-actions .action-btn{flex:1}.action-btn{display:flex;align-items:center;justify-content:center;gap:6px;width:100%;padding:8px 12px;background:var(--primary-color);color:#fff;border:none;border-radius:8px;cursor:pointer;font-size:12.5px;font-weight:600;transition:background-color .18s,transform .12s}.action-btn:hover{background:var(--primary-hover);transform:translateY(-1px)}.delete-all-btn{background:var(--error-color)}.delete-all-btn:hover{background:var(--error-hover)}.modal-overlay{position:fixed;inset:0;background:#00000080;display:flex;align-items:center;justify-content:center;z-index:1060;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}.modal-container{background:var(--bg-primary);border-radius:14px;width:90%;max-width:580px;max-height:80vh;display:flex;flex-direction:column;box-shadow:0 16px 48px #00000040;animation:modal-in .2s cubic-bezier(.16,1,.3,1)}@keyframes modal-in{0%{opacity:0;transform:scale(.94) translateY(8px)}to{opacity:1;transform:scale(1) translateY(0)}}.modal-header{display:flex;justify-content:space-between;align-items:center;padding:16px 20px;border-bottom:1px solid var(--border-color);background:var(--bg-secondary);border-radius:14px 14px 0 0;border-top:3px solid transparent}.modal-header.modal-type-info{border-top-color:var(--info-color)}.modal-header.modal-type-success{border-top-color:var(--success-color)}.modal-header.modal-type-warning{border-top-color:var(--warning-color)}.modal-header.modal-type-error{border-top-color:var(--error-color)}.modal-header-left{display:flex;align-items:center;gap:10px;min-width:0}.modal-type-icon{flex-shrink:0;width:32px;height:32px;border-radius:8px;display:flex;align-items:center;justify-content:center}.modal-type-info .modal-type-icon{color:var(--info-color);background:var(--info-bg)}.modal-type-success .modal-type-icon{color:var(--success-color);background:var(--success-bg)}.modal-type-warning .modal-type-icon{color:var(--warning-color);background:var(--warning-bg)}.modal-type-error .modal-type-icon{color:var(--error-color);background:var(--error-bg)}.modal-header h3{margin:0;font-size:15px;font-weight:700;color:var(--text-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.modal-meta{display:flex;justify-content:space-between;padding:8px 20px;font-size:11.5px;color:var(--text-muted);border-bottom:1px solid var(--border-light)}.modal-body{padding:20px;overflow-y:auto;flex:1;color:var(--text-primary);font-size:14px;line-height:1.65}.modal-footer{padding:12px 20px;border-top:1px solid var(--border-color);background:var(--bg-secondary);border-radius:0 0 14px 14px;display:flex;justify-content:flex-end}.modal-footer .action-btn{width:auto;padding:8px 24px}@media(max-width:600px){.notification-panel{width:100%;right:-100%}.modal-container{width:95%;max-height:90vh}}\n"] }]
1205
1438
  }], ctorParameters: () => [{ type: MesAuthService }, { type: ToastService }, { type: ThemeService }], propDecorators: { notificationRead: [{
1206
1439
  type: Output
1207
1440
  }], themeClass: [{