mesauth-angular 0.2.14 → 0.2.20

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.
Files changed (32) hide show
  1. package/README.md +139 -61
  2. package/dist/README.md +139 -61
  3. package/dist/{fesm2020 → fesm2022}/mesauth-angular.mjs +702 -662
  4. package/dist/fesm2022/mesauth-angular.mjs.map +1 -0
  5. package/dist/index.d.ts +269 -9
  6. package/package.json +12 -12
  7. package/dist/esm2020/index.mjs +0 -10
  8. package/dist/esm2020/ma-user.component.mjs +0 -32
  9. package/dist/esm2020/mes-auth.interceptor.mjs +0 -29
  10. package/dist/esm2020/mes-auth.module.mjs +0 -23
  11. package/dist/esm2020/mes-auth.service.mjs +0 -146
  12. package/dist/esm2020/mesauth-angular.mjs +0 -5
  13. package/dist/esm2020/notification-badge.component.mjs +0 -71
  14. package/dist/esm2020/notification-panel.component.mjs +0 -333
  15. package/dist/esm2020/theme.service.mjs +0 -63
  16. package/dist/esm2020/toast-container.component.mjs +0 -83
  17. package/dist/esm2020/toast.service.mjs +0 -41
  18. package/dist/esm2020/user-profile.component.mjs +0 -223
  19. package/dist/fesm2015/mesauth-angular.mjs +0 -1012
  20. package/dist/fesm2015/mesauth-angular.mjs.map +0 -1
  21. package/dist/fesm2020/mesauth-angular.mjs.map +0 -1
  22. package/dist/ma-user.component.d.ts +0 -8
  23. package/dist/mes-auth.interceptor.d.ts +0 -13
  24. package/dist/mes-auth.module.d.ts +0 -6
  25. package/dist/mes-auth.service.d.ts +0 -102
  26. package/dist/notification-badge.component.d.ts +0 -20
  27. package/dist/notification-panel.component.d.ts +0 -36
  28. package/dist/package.json +0 -52
  29. package/dist/theme.service.d.ts +0 -19
  30. package/dist/toast-container.component.d.ts +0 -18
  31. package/dist/toast.service.d.ts +0 -18
  32. package/dist/user-profile.component.d.ts +0 -31
@@ -1,366 +1,391 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Injectable, Optional, NgModule, EventEmitter, Component, Output, HostBinding, HostListener, ViewChild } from '@angular/core';
2
+ import { Injectable, NgModule, EventEmitter, HostListener, HostBinding, Output, Component, ViewChild } from '@angular/core';
3
3
  import { HubConnectionBuilder, LogLevel } from '@microsoft/signalr';
4
4
  import { BehaviorSubject, Subject, throwError } from 'rxjs';
5
5
  import { filter, debounceTime, tap, catchError, takeUntil } from 'rxjs/operators';
6
6
  import * as i2 from '@angular/router';
7
7
  import { NavigationEnd } from '@angular/router';
8
- import * as i1 from '@angular/common/http';
9
8
  import { HTTP_INTERCEPTORS } from '@angular/common/http';
10
9
  import * as i3 from '@angular/common';
11
10
  import { NgIf, CommonModule, NgFor } from '@angular/common';
12
11
 
13
- var NotificationType;
14
- (function (NotificationType) {
15
- NotificationType["Info"] = "Info";
16
- NotificationType["Warning"] = "Warning";
17
- NotificationType["Error"] = "Error";
18
- NotificationType["Success"] = "Success";
19
- })(NotificationType || (NotificationType = {}));
20
- class MesAuthService {
21
- constructor(http, router) {
22
- this.http = http;
23
- this.router = router;
24
- this.hubConnection = null;
25
- this._currentUser = new BehaviorSubject(null);
26
- this.currentUser$ = this._currentUser.asObservable();
27
- this._notifications = new Subject();
28
- this.notifications$ = this._notifications.asObservable();
29
- this.apiBase = '';
30
- this.config = null;
31
- // Listen for route changes - only refresh user data if needed for SPA navigation
32
- // This helps maintain authentication state in single-page applications
33
- if (this.router) {
34
- this.router.events
35
- .pipe(filter(event => event instanceof NavigationEnd), debounceTime(1000) // Longer debounce to avoid interfering with login flow
36
- )
37
- .subscribe((event) => {
38
- // Only refresh if user is logged in and navigating to protected routes
39
- // Avoid refreshing during login/logout flows
40
- if (this._currentUser.value && this.isProtectedRoute(event.url)) {
41
- // Small delay to ensure any login/logout operations complete
42
- setTimeout(() => {
43
- if (this._currentUser.value) {
44
- this.refreshUser();
45
- }
46
- }, 100);
47
- }
48
- });
49
- }
50
- }
51
- isProtectedRoute(url) {
52
- // Consider routes protected if they don't include auth-related paths
53
- return !url.includes('/login') && !url.includes('/auth') && !url.includes('/signin') && !url.includes('/logout');
54
- }
55
- init(config) {
56
- this.config = config;
57
- this.apiBase = config.apiBaseUrl.replace(/\/$/, '');
58
- this.fetchCurrentUser();
59
- this.fetchInitialNotifications();
60
- }
61
- getConfig() {
62
- return this.config;
63
- }
64
- fetchCurrentUser() {
65
- if (!this.apiBase)
66
- return;
67
- const url = `${this.apiBase}/auth/me`;
68
- this.http.get(url, { withCredentials: this.config?.withCredentials ?? true }).subscribe({
69
- next: (u) => {
70
- this._currentUser.next(u);
71
- if (u && this.config) {
72
- this.startConnection(this.config);
73
- }
74
- },
75
- error: (err) => { }
76
- });
77
- }
78
- fetchInitialNotifications() {
79
- if (!this.apiBase)
80
- return;
81
- this.http.get(`${this.apiBase}/notif/me`, { withCredentials: this.config?.withCredentials ?? true }).subscribe({
82
- next: (notifications) => {
83
- if (Array.isArray(notifications?.items)) {
84
- notifications.items.forEach((n) => this._notifications.next(n));
85
- }
86
- },
87
- error: (err) => { }
88
- });
89
- }
90
- getUnreadCount() {
91
- return this.http.get(`${this.apiBase}/notif/me/unread-count`, { withCredentials: this.config?.withCredentials ?? true });
92
- }
93
- getNotifications(page = 1, pageSize = 20, includeRead = false, type) {
94
- let url = `${this.apiBase}/notif/me?page=${page}&pageSize=${pageSize}&includeRead=${includeRead}`;
95
- if (type) {
96
- url += `&type=${type}`;
97
- }
98
- return this.http.get(url, { withCredentials: this.config?.withCredentials ?? true });
99
- }
100
- markAsRead(notificationId) {
101
- return this.http.patch(`${this.apiBase}/notif/${notificationId}/read`, {}, { withCredentials: this.config?.withCredentials ?? true });
102
- }
103
- markAllAsRead() {
104
- return this.http.patch(`${this.apiBase}/notif/me/read-all`, {}, { withCredentials: this.config?.withCredentials ?? true });
105
- }
106
- deleteNotification(notificationId) {
107
- return this.http.delete(`${this.apiBase}/notif/${notificationId}`, { withCredentials: this.config?.withCredentials ?? true });
108
- }
109
- startConnection(config) {
110
- if (this.hubConnection)
111
- return;
112
- const signalrUrl = config.apiBaseUrl.replace(/\/$/, '') + '/hub/notification';
113
- const builder = new HubConnectionBuilder()
114
- .withUrl(signalrUrl, { withCredentials: config.withCredentials ?? true })
115
- .withAutomaticReconnect()
116
- .configureLogging(LogLevel.Warning);
117
- this.hubConnection = builder.build();
118
- this.hubConnection.on('ReceiveNotification', (n) => {
119
- this._notifications.next(n);
120
- });
121
- this.hubConnection.start().then(() => { }).catch((err) => { });
122
- this.hubConnection.onclose(() => { });
123
- this.hubConnection.onreconnecting(() => { });
124
- this.hubConnection.onreconnected(() => { });
125
- }
126
- stop() {
127
- if (!this.hubConnection)
128
- return;
129
- this.hubConnection.stop().catch(() => { });
130
- this.hubConnection = null;
131
- }
132
- logout() {
133
- const url = `${this.apiBase}/auth/logout`;
134
- return this.http.post(url, {}, { withCredentials: this.config?.withCredentials ?? true }).pipe(tap(() => {
135
- this._currentUser.next(null);
136
- this.stop();
137
- }));
138
- }
139
- refreshUser() {
140
- this.fetchCurrentUser();
141
- }
142
- }
143
- MesAuthService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: MesAuthService, deps: [{ token: i1.HttpClient }, { token: i2.Router, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
144
- MesAuthService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: MesAuthService });
145
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: MesAuthService, decorators: [{
146
- type: Injectable
147
- }], ctorParameters: function () { return [{ type: i1.HttpClient }, { type: i2.Router, decorators: [{
148
- type: Optional
149
- }] }]; } });
12
+ var NotificationType;
13
+ (function (NotificationType) {
14
+ NotificationType["Info"] = "Info";
15
+ NotificationType["Warning"] = "Warning";
16
+ NotificationType["Error"] = "Error";
17
+ NotificationType["Success"] = "Success";
18
+ })(NotificationType || (NotificationType = {}));
19
+ class MesAuthService {
20
+ hubConnection = null;
21
+ _currentUser = new BehaviorSubject(null);
22
+ currentUser$ = this._currentUser.asObservable();
23
+ _notifications = new Subject();
24
+ notifications$ = this._notifications.asObservable();
25
+ apiBase = '';
26
+ config = null;
27
+ http;
28
+ router;
29
+ constructor() {
30
+ // Empty constructor - all dependencies passed to init()
31
+ }
32
+ isProtectedRoute(url) {
33
+ // Consider routes protected if they don't include auth-related paths
34
+ return !url.includes('/login') && !url.includes('/auth') && !url.includes('/signin') && !url.includes('/logout');
35
+ }
36
+ init(config, httpClient, router) {
37
+ this.config = config;
38
+ this.http = httpClient;
39
+ this.router = router;
40
+ this.apiBase = config.apiBaseUrl.replace(/\/$/, '');
41
+ // Listen for route changes - only refresh user data if needed for SPA navigation
42
+ // This helps maintain authentication state in single-page applications
43
+ if (this.router) {
44
+ this.router.events
45
+ .pipe(filter(event => event instanceof NavigationEnd), debounceTime(1000) // Longer debounce to avoid interfering with login flow
46
+ )
47
+ .subscribe((event) => {
48
+ // Only refresh if user is logged in and navigating to protected routes
49
+ // Avoid refreshing during login/logout flows
50
+ if (this._currentUser.value && this.isProtectedRoute(event.url)) {
51
+ // Small delay to ensure any login/logout operations complete
52
+ setTimeout(() => {
53
+ if (this._currentUser.value) {
54
+ this.refreshUser();
55
+ }
56
+ }, 100);
57
+ }
58
+ });
59
+ }
60
+ this.fetchCurrentUser();
61
+ this.fetchInitialNotifications();
62
+ }
63
+ getConfig() {
64
+ return this.config;
65
+ }
66
+ fetchCurrentUser() {
67
+ if (!this.apiBase)
68
+ return;
69
+ const url = `${this.apiBase}/auth/me`;
70
+ this.http.get(url, { withCredentials: this.config?.withCredentials ?? true }).subscribe({
71
+ next: (u) => {
72
+ this._currentUser.next(u);
73
+ if (u && this.config) {
74
+ this.startConnection(this.config);
75
+ }
76
+ },
77
+ error: (err) => {
78
+ // Silently handle auth errors (401/403) - user is not logged in
79
+ if (err.status === 401 || err.status === 403) {
80
+ this._currentUser.next(null);
81
+ }
82
+ }
83
+ });
84
+ }
85
+ fetchInitialNotifications() {
86
+ if (!this.apiBase)
87
+ return;
88
+ this.http.get(`${this.apiBase}/notif/me`, { withCredentials: this.config?.withCredentials ?? true }).subscribe({
89
+ next: (notifications) => {
90
+ if (Array.isArray(notifications?.items)) {
91
+ notifications.items.forEach((n) => this._notifications.next(n));
92
+ }
93
+ },
94
+ error: (err) => {
95
+ // Silently handle auth errors (401/403) - user is not logged in
96
+ // No need to emit anything
97
+ }
98
+ });
99
+ }
100
+ getUnreadCount() {
101
+ return this.http.get(`${this.apiBase}/notif/me/unread-count`, { withCredentials: this.config?.withCredentials ?? true });
102
+ }
103
+ getNotifications(page = 1, pageSize = 20, includeRead = false, type) {
104
+ let url = `${this.apiBase}/notif/me?page=${page}&pageSize=${pageSize}&includeRead=${includeRead}`;
105
+ if (type) {
106
+ url += `&type=${type}`;
107
+ }
108
+ return this.http.get(url, { withCredentials: this.config?.withCredentials ?? true });
109
+ }
110
+ markAsRead(notificationId) {
111
+ return this.http.patch(`${this.apiBase}/notif/${notificationId}/read`, {}, { withCredentials: this.config?.withCredentials ?? true });
112
+ }
113
+ markAllAsRead() {
114
+ return this.http.patch(`${this.apiBase}/notif/me/read-all`, {}, { withCredentials: this.config?.withCredentials ?? true });
115
+ }
116
+ deleteNotification(notificationId) {
117
+ return this.http.delete(`${this.apiBase}/notif/${notificationId}`, { withCredentials: this.config?.withCredentials ?? true });
118
+ }
119
+ getFrontEndRoutes() {
120
+ if (!this.apiBase)
121
+ throw new Error('MesAuth not initialized');
122
+ return this.http.get(`${this.apiBase}/fe-routes/me`, { withCredentials: this.config?.withCredentials ?? true });
123
+ }
124
+ registerFrontEndRoutes(routes) {
125
+ if (!this.apiBase)
126
+ throw new Error('MesAuth not initialized');
127
+ return this.http.post(`${this.apiBase}/fe-routes/register`, routes, { withCredentials: this.config?.withCredentials ?? true });
128
+ }
129
+ startConnection(config) {
130
+ if (this.hubConnection)
131
+ return;
132
+ const signalrUrl = config.apiBaseUrl.replace(/\/$/, '') + '/hub/notification';
133
+ const builder = new HubConnectionBuilder()
134
+ .withUrl(signalrUrl, { withCredentials: config.withCredentials ?? true })
135
+ .withAutomaticReconnect()
136
+ .configureLogging(LogLevel.Warning);
137
+ this.hubConnection = builder.build();
138
+ this.hubConnection.on('ReceiveNotification', (n) => {
139
+ this._notifications.next(n);
140
+ });
141
+ this.hubConnection.start().then(() => { }).catch((err) => { });
142
+ this.hubConnection.onclose(() => { });
143
+ this.hubConnection.onreconnecting(() => { });
144
+ this.hubConnection.onreconnected(() => { });
145
+ }
146
+ stop() {
147
+ if (!this.hubConnection)
148
+ return;
149
+ this.hubConnection.stop().catch(() => { });
150
+ this.hubConnection = null;
151
+ }
152
+ logout() {
153
+ const url = `${this.apiBase}/auth/logout`;
154
+ return this.http.post(url, {}, { withCredentials: this.config?.withCredentials ?? true }).pipe(tap(() => {
155
+ this._currentUser.next(null);
156
+ this.stop();
157
+ }));
158
+ }
159
+ refreshUser() {
160
+ this.fetchCurrentUser();
161
+ }
162
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: MesAuthService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
163
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: MesAuthService });
164
+ }
165
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: MesAuthService, decorators: [{
166
+ type: Injectable
167
+ }], ctorParameters: () => [] });
150
168
 
151
- class MesAuthInterceptor {
152
- constructor(authService, router) {
153
- this.authService = authService;
154
- this.router = router;
155
- }
156
- intercept(req, next) {
157
- return next.handle(req).pipe(catchError((error) => {
158
- if (error.status === 403) {
159
- const config = this.authService.getConfig();
160
- const baseUrl = config?.userBaseUrl || '';
161
- const returnUrl = encodeURIComponent(window.location.href);
162
- window.location.href = `${baseUrl}/403?returnUrl=${returnUrl}`;
163
- }
164
- return throwError(error);
165
- }));
166
- }
167
- }
168
- MesAuthInterceptor.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: MesAuthInterceptor, deps: [{ token: MesAuthService }, { token: i2.Router }], target: i0.ɵɵFactoryTarget.Injectable });
169
- MesAuthInterceptor.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: MesAuthInterceptor });
170
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: MesAuthInterceptor, decorators: [{
171
- type: Injectable
172
- }], ctorParameters: function () { return [{ type: MesAuthService }, { type: i2.Router }]; } });
169
+ class MesAuthInterceptor {
170
+ authService;
171
+ router;
172
+ constructor(authService, router) {
173
+ this.authService = authService;
174
+ this.router = router;
175
+ }
176
+ intercept(req, next) {
177
+ return next.handle(req).pipe(catchError((error) => {
178
+ if (error.status === 403) {
179
+ const config = this.authService.getConfig();
180
+ const baseUrl = config?.userBaseUrl || '';
181
+ // Use router URL for internal navigation (cleaner URLs)
182
+ // Falls back to window.location for full URL if needed
183
+ const currentUrl = this.router.url || window.location.pathname + window.location.search;
184
+ const returnUrl = encodeURIComponent(currentUrl);
185
+ window.location.href = `${baseUrl}/403?returnUrl=${returnUrl}`;
186
+ }
187
+ return throwError(error);
188
+ }));
189
+ }
190
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: MesAuthInterceptor, deps: [{ token: MesAuthService }, { token: i2.Router }], target: i0.ɵɵFactoryTarget.Injectable });
191
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: MesAuthInterceptor });
192
+ }
193
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: MesAuthInterceptor, decorators: [{
194
+ type: Injectable
195
+ }], ctorParameters: () => [{ type: MesAuthService }, { type: i2.Router }] });
173
196
 
174
- class MesAuthModule {
175
- }
176
- MesAuthModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: MesAuthModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
177
- MesAuthModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "14.3.0", ngImport: i0, type: MesAuthModule });
178
- MesAuthModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: MesAuthModule, providers: [
179
- MesAuthService,
180
- { provide: HTTP_INTERCEPTORS, useClass: MesAuthInterceptor, multi: true }
181
- ] });
182
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: MesAuthModule, decorators: [{
183
- type: NgModule,
184
- args: [{
185
- providers: [
186
- MesAuthService,
187
- { provide: HTTP_INTERCEPTORS, useClass: MesAuthInterceptor, multi: true }
188
- ]
189
- }]
197
+ class MesAuthModule {
198
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: MesAuthModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
199
+ static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.16", ngImport: i0, type: MesAuthModule });
200
+ static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: MesAuthModule, providers: [
201
+ MesAuthService,
202
+ { provide: HTTP_INTERCEPTORS, useClass: MesAuthInterceptor, multi: true }
203
+ ] });
204
+ }
205
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: MesAuthModule, decorators: [{
206
+ type: NgModule,
207
+ args: [{
208
+ providers: [
209
+ MesAuthService,
210
+ { provide: HTTP_INTERCEPTORS, useClass: MesAuthInterceptor, multi: true }
211
+ ]
212
+ }]
190
213
  }] });
191
214
 
192
- class ThemeService {
193
- constructor() {
194
- this._currentTheme = new BehaviorSubject('light');
195
- this.currentTheme$ = this._currentTheme.asObservable();
196
- this.observer = null;
197
- this.detectTheme();
198
- this.startWatching();
199
- }
200
- ngOnDestroy() {
201
- this.stopWatching();
202
- }
203
- detectTheme() {
204
- const html = document.documentElement;
205
- const isDark = html.classList.contains('dark') ||
206
- html.getAttribute('data-theme') === 'dark' ||
207
- html.getAttribute('theme') === 'dark' ||
208
- html.getAttribute('data-coreui-theme') === 'dark';
209
- this._currentTheme.next(isDark ? 'dark' : 'light');
210
- }
211
- startWatching() {
212
- if (typeof MutationObserver === 'undefined') {
213
- // Fallback for older browsers - check periodically
214
- setInterval(() => this.detectTheme(), 1000);
215
- return;
216
- }
217
- this.observer = new MutationObserver(() => {
218
- this.detectTheme();
219
- });
220
- this.observer.observe(document.documentElement, {
221
- attributes: true,
222
- attributeFilter: ['class', 'data-theme', 'theme', 'data-coreui-theme']
223
- });
224
- }
225
- stopWatching() {
226
- if (this.observer) {
227
- this.observer.disconnect();
228
- this.observer = null;
229
- }
230
- }
231
- get currentTheme() {
232
- return this._currentTheme.value;
233
- }
234
- // Method to manually set theme if needed
235
- setTheme(theme) {
236
- this._currentTheme.next(theme);
237
- }
238
- // Re-detect theme from DOM
239
- refreshTheme() {
240
- this.detectTheme();
241
- }
242
- }
243
- ThemeService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ThemeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
244
- ThemeService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ThemeService, providedIn: 'root' });
245
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ThemeService, decorators: [{
246
- type: Injectable,
247
- args: [{
248
- providedIn: 'root'
249
- }]
250
- }], ctorParameters: function () { return []; } });
215
+ class ThemeService {
216
+ _currentTheme = new BehaviorSubject('light');
217
+ currentTheme$ = this._currentTheme.asObservable();
218
+ observer = null;
219
+ constructor() {
220
+ this.detectTheme();
221
+ this.startWatching();
222
+ }
223
+ ngOnDestroy() {
224
+ this.stopWatching();
225
+ }
226
+ detectTheme() {
227
+ const html = document.documentElement;
228
+ const isDark = html.classList.contains('dark') ||
229
+ html.getAttribute('data-theme') === 'dark' ||
230
+ html.getAttribute('theme') === 'dark' ||
231
+ html.getAttribute('data-coreui-theme') === 'dark';
232
+ this._currentTheme.next(isDark ? 'dark' : 'light');
233
+ }
234
+ startWatching() {
235
+ if (typeof MutationObserver === 'undefined') {
236
+ // Fallback for older browsers - check periodically
237
+ setInterval(() => this.detectTheme(), 1000);
238
+ return;
239
+ }
240
+ this.observer = new MutationObserver(() => {
241
+ this.detectTheme();
242
+ });
243
+ this.observer.observe(document.documentElement, {
244
+ attributes: true,
245
+ attributeFilter: ['class', 'data-theme', 'theme', 'data-coreui-theme']
246
+ });
247
+ }
248
+ stopWatching() {
249
+ if (this.observer) {
250
+ this.observer.disconnect();
251
+ this.observer = null;
252
+ }
253
+ }
254
+ get currentTheme() {
255
+ return this._currentTheme.value;
256
+ }
257
+ // Method to manually set theme if needed
258
+ setTheme(theme) {
259
+ this._currentTheme.next(theme);
260
+ }
261
+ // Re-detect theme from DOM
262
+ refreshTheme() {
263
+ this.detectTheme();
264
+ }
265
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: ThemeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
266
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: ThemeService, providedIn: 'root' });
267
+ }
268
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: ThemeService, decorators: [{
269
+ type: Injectable,
270
+ args: [{
271
+ providedIn: 'root'
272
+ }]
273
+ }], ctorParameters: () => [] });
251
274
 
252
- class UserProfileComponent {
253
- constructor(authService, router, themeService) {
254
- this.authService = authService;
255
- this.router = router;
256
- this.themeService = themeService;
257
- this.notificationClick = new EventEmitter();
258
- this.currentUser = null;
259
- this.currentTheme = 'light';
260
- this.unreadCount = 0;
261
- this.dropdownOpen = false;
262
- this.destroy$ = new Subject();
263
- }
264
- get themeClass() {
265
- return `theme-${this.currentTheme}`;
266
- }
267
- ngOnInit() {
268
- this.authService.currentUser$
269
- .pipe(takeUntil(this.destroy$))
270
- .subscribe(user => {
271
- this.currentUser = user;
272
- });
273
- this.themeService.currentTheme$
274
- .pipe(takeUntil(this.destroy$))
275
- .subscribe(theme => {
276
- this.currentTheme = theme;
277
- });
278
- this.loadUnreadCount();
279
- // Listen for new notifications
280
- this.authService.notifications$
281
- .pipe(takeUntil(this.destroy$))
282
- .subscribe(() => {
283
- console.log('Notification received, updating unread count');
284
- this.loadUnreadCount();
285
- });
286
- }
287
- ngOnDestroy() {
288
- this.destroy$.next();
289
- this.destroy$.complete();
290
- }
291
- loadUnreadCount() {
292
- this.authService.getUnreadCount().subscribe({
293
- next: (response) => {
294
- this.unreadCount = response.unreadCount || 0;
295
- },
296
- error: (err) => { }
297
- });
298
- }
299
- getAvatarUrl(user) {
300
- const config = this.authService.getConfig();
301
- const baseUrl = config?.apiBaseUrl || '';
302
- // Use userId for the avatar endpoint
303
- const userId = user.userId;
304
- if (userId && baseUrl) {
305
- return `${baseUrl.replace(/\/$/, '')}/auth/${userId}/avatar`;
306
- }
307
- // Fallback to UI avatars service if no userId or baseUrl
308
- const displayName = user.userName || user.userId || 'User';
309
- return `https://ui-avatars.com/api/?name=${encodeURIComponent(displayName)}&background=1976d2&color=fff`;
310
- }
311
- getLastNameInitial(user) {
312
- const fullName = user.fullName || user.userName || 'U';
313
- const parts = fullName.split(' ');
314
- const lastPart = parts[parts.length - 1];
315
- return lastPart.charAt(0).toUpperCase();
316
- }
317
- toggleDropdown() {
318
- this.dropdownOpen = !this.dropdownOpen;
319
- }
320
- onDocumentClick(event) {
321
- const target = event.target;
322
- const clickedInside = target.closest('.user-menu-wrapper');
323
- if (!clickedInside) {
324
- this.dropdownOpen = false;
325
- }
326
- }
327
- onLogin() {
328
- const config = this.authService.getConfig();
329
- const baseUrl = config?.userBaseUrl || '';
330
- const returnUrl = encodeURIComponent(this.router.url);
331
- window.location.href = `${baseUrl}/login?returnUrl=${returnUrl}`;
332
- }
333
- onViewProfile() {
334
- const config = this.authService.getConfig();
335
- const baseUrl = config?.userBaseUrl || '';
336
- window.location.href = `${baseUrl}/profile`;
337
- this.dropdownOpen = false;
338
- }
339
- onLogout() {
340
- this.authService.logout().subscribe({
341
- next: () => {
342
- // Clear current user after successful logout
343
- this.dropdownOpen = false;
344
- // Navigate to login with return URL
345
- const config = this.authService.getConfig();
346
- const baseUrl = config?.userBaseUrl || '';
347
- const returnUrl = encodeURIComponent(window.location.href);
348
- window.location.href = `${baseUrl}/login?returnUrl=${returnUrl}`;
349
- },
350
- error: (err) => {
351
- // Still navigate to login even if logout fails
352
- const config = this.authService.getConfig();
353
- const baseUrl = config?.userBaseUrl || '';
354
- window.location.href = `${baseUrl}/login`;
355
- }
356
- });
357
- }
358
- onNotificationClick() {
359
- this.notificationClick.emit();
360
- }
361
- }
362
- UserProfileComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: UserProfileComponent, deps: [{ token: MesAuthService }, { token: i2.Router }, { token: ThemeService }], target: i0.ɵɵFactoryTarget.Component });
363
- UserProfileComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: UserProfileComponent, isStandalone: true, selector: "ma-user-profile", outputs: { notificationClick: "notificationClick" }, host: { listeners: { "document:click": "onDocumentClick($event)" }, properties: { "class": "this.themeClass" } }, ngImport: i0, template: `
275
+ class UserProfileComponent {
276
+ authService;
277
+ router;
278
+ themeService;
279
+ notificationClick = new EventEmitter();
280
+ get themeClass() {
281
+ return `theme-${this.currentTheme}`;
282
+ }
283
+ currentUser = null;
284
+ currentTheme = 'light';
285
+ unreadCount = 0;
286
+ dropdownOpen = false;
287
+ destroy$ = new Subject();
288
+ constructor(authService, router, themeService) {
289
+ this.authService = authService;
290
+ this.router = router;
291
+ this.themeService = themeService;
292
+ }
293
+ ngOnInit() {
294
+ this.authService.currentUser$
295
+ .pipe(takeUntil(this.destroy$))
296
+ .subscribe(user => {
297
+ this.currentUser = user;
298
+ });
299
+ this.themeService.currentTheme$
300
+ .pipe(takeUntil(this.destroy$))
301
+ .subscribe(theme => {
302
+ this.currentTheme = theme;
303
+ });
304
+ this.loadUnreadCount();
305
+ // Listen for new notifications
306
+ this.authService.notifications$
307
+ .pipe(takeUntil(this.destroy$))
308
+ .subscribe(() => {
309
+ console.log('Notification received, updating unread count');
310
+ this.loadUnreadCount();
311
+ });
312
+ }
313
+ ngOnDestroy() {
314
+ this.destroy$.next();
315
+ this.destroy$.complete();
316
+ }
317
+ loadUnreadCount() {
318
+ this.authService.getUnreadCount().subscribe({
319
+ next: (response) => {
320
+ this.unreadCount = response.unreadCount || 0;
321
+ },
322
+ error: (err) => { }
323
+ });
324
+ }
325
+ getAvatarUrl(user) {
326
+ const config = this.authService.getConfig();
327
+ const baseUrl = config?.apiBaseUrl || '';
328
+ // Use userId for the avatar endpoint
329
+ const userId = user.userId;
330
+ if (userId && baseUrl) {
331
+ return `${baseUrl.replace(/\/$/, '')}/auth/${userId}/avatar`;
332
+ }
333
+ // Fallback to UI avatars service if no userId or baseUrl
334
+ const displayName = user.userName || user.userId || 'User';
335
+ return `https://ui-avatars.com/api/?name=${encodeURIComponent(displayName)}&background=1976d2&color=fff`;
336
+ }
337
+ getLastNameInitial(user) {
338
+ const fullName = user.fullName || user.userName || 'U';
339
+ const parts = fullName.split(' ');
340
+ const lastPart = parts[parts.length - 1];
341
+ return lastPart.charAt(0).toUpperCase();
342
+ }
343
+ toggleDropdown() {
344
+ this.dropdownOpen = !this.dropdownOpen;
345
+ }
346
+ onDocumentClick(event) {
347
+ const target = event.target;
348
+ const clickedInside = target.closest('.user-menu-wrapper');
349
+ if (!clickedInside) {
350
+ this.dropdownOpen = false;
351
+ }
352
+ }
353
+ onLogin() {
354
+ const config = this.authService.getConfig();
355
+ const baseUrl = config?.userBaseUrl || '';
356
+ const returnUrl = encodeURIComponent(this.router.url);
357
+ window.location.href = `${baseUrl}/login?returnUrl=${returnUrl}`;
358
+ }
359
+ onViewProfile() {
360
+ const config = this.authService.getConfig();
361
+ const baseUrl = config?.userBaseUrl || '';
362
+ window.location.href = `${baseUrl}/profile`;
363
+ this.dropdownOpen = false;
364
+ }
365
+ onLogout() {
366
+ this.authService.logout().subscribe({
367
+ next: () => {
368
+ // Clear current user after successful logout
369
+ this.dropdownOpen = false;
370
+ // Navigate to login with return URL
371
+ const config = this.authService.getConfig();
372
+ const baseUrl = config?.userBaseUrl || '';
373
+ const returnUrl = encodeURIComponent(window.location.href);
374
+ window.location.href = `${baseUrl}/login?returnUrl=${returnUrl}`;
375
+ },
376
+ error: (err) => {
377
+ // Still navigate to login even if logout fails
378
+ const config = this.authService.getConfig();
379
+ const baseUrl = config?.userBaseUrl || '';
380
+ window.location.href = `${baseUrl}/login`;
381
+ }
382
+ });
383
+ }
384
+ onNotificationClick() {
385
+ this.notificationClick.emit();
386
+ }
387
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: UserProfileComponent, deps: [{ token: MesAuthService }, { token: i2.Router }, { token: ThemeService }], target: i0.ɵɵFactoryTarget.Component });
388
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.16", type: UserProfileComponent, isStandalone: true, selector: "ma-user-profile", outputs: { notificationClick: "notificationClick" }, host: { listeners: { "document:click": "onDocumentClick($event)" }, properties: { "class": "this.themeClass" } }, ngImport: i0, template: `
364
389
  <div class="user-profile-container">
365
390
  <!-- Not logged in -->
366
391
  <ng-container *ngIf="!currentUser">
@@ -405,9 +430,10 @@ UserProfileComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", v
405
430
  </div>
406
431
  </ng-container>
407
432
  </div>
408
- `, 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"] }] });
409
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: UserProfileComponent, decorators: [{
410
- type: Component,
433
+ `, 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"] }] });
434
+ }
435
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: UserProfileComponent, decorators: [{
436
+ type: Component,
411
437
  args: [{ selector: 'ma-user-profile', standalone: true, imports: [NgIf], template: `
412
438
  <div class="user-profile-container">
413
439
  <!-- Not logged in -->
@@ -453,88 +479,87 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
453
479
  </div>
454
480
  </ng-container>
455
481
  </div>
456
- `, 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"] }]
457
- }], ctorParameters: function () { return [{ type: MesAuthService }, { type: i2.Router }, { type: ThemeService }]; }, propDecorators: { notificationClick: [{
458
- type: Output
459
- }], themeClass: [{
460
- type: HostBinding,
461
- args: ['class']
462
- }], onDocumentClick: [{
463
- type: HostListener,
464
- args: ['document:click', ['$event']]
482
+ `, 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"] }]
483
+ }], ctorParameters: () => [{ type: MesAuthService }, { type: i2.Router }, { type: ThemeService }], propDecorators: { notificationClick: [{
484
+ type: Output
485
+ }], themeClass: [{
486
+ type: HostBinding,
487
+ args: ['class']
488
+ }], onDocumentClick: [{
489
+ type: HostListener,
490
+ args: ['document:click', ['$event']]
465
491
  }] } });
466
492
 
467
- class ToastService {
468
- constructor() {
469
- this.toasts$ = new BehaviorSubject([]);
470
- this.toasts = this.toasts$.asObservable();
471
- }
472
- show(message, title, type = 'info', duration = 5000) {
473
- const id = Math.random().toString(36).substr(2, 9);
474
- const toast = {
475
- id,
476
- message,
477
- title,
478
- type,
479
- duration
480
- };
481
- const currentToasts = this.toasts$.value;
482
- this.toasts$.next([...currentToasts, toast]);
483
- if (duration > 0) {
484
- setTimeout(() => {
485
- this.remove(id);
486
- }, duration);
487
- }
488
- return id;
489
- }
490
- remove(id) {
491
- const currentToasts = this.toasts$.value;
492
- this.toasts$.next(currentToasts.filter(t => t.id !== id));
493
- }
494
- clear() {
495
- this.toasts$.next([]);
496
- }
497
- }
498
- ToastService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ToastService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
499
- ToastService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ToastService, providedIn: 'root' });
500
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ToastService, decorators: [{
501
- type: Injectable,
502
- args: [{ providedIn: 'root' }]
493
+ class ToastService {
494
+ toasts$ = new BehaviorSubject([]);
495
+ toasts = this.toasts$.asObservable();
496
+ show(message, title, type = 'info', duration = 5000) {
497
+ const id = Math.random().toString(36).substr(2, 9);
498
+ const toast = {
499
+ id,
500
+ message,
501
+ title,
502
+ type,
503
+ duration
504
+ };
505
+ const currentToasts = this.toasts$.value;
506
+ this.toasts$.next([...currentToasts, toast]);
507
+ if (duration > 0) {
508
+ setTimeout(() => {
509
+ this.remove(id);
510
+ }, duration);
511
+ }
512
+ return id;
513
+ }
514
+ remove(id) {
515
+ const currentToasts = this.toasts$.value;
516
+ this.toasts$.next(currentToasts.filter(t => t.id !== id));
517
+ }
518
+ clear() {
519
+ this.toasts$.next([]);
520
+ }
521
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: ToastService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
522
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: ToastService, providedIn: 'root' });
523
+ }
524
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: ToastService, decorators: [{
525
+ type: Injectable,
526
+ args: [{ providedIn: 'root' }]
503
527
  }] });
504
528
 
505
- class ToastContainerComponent {
506
- constructor(toastService, themeService) {
507
- this.toastService = toastService;
508
- this.themeService = themeService;
509
- this.toasts = [];
510
- this.currentTheme = 'light';
511
- this.destroy$ = new Subject();
512
- }
513
- get themeClass() {
514
- return `theme-${this.currentTheme}`;
515
- }
516
- ngOnInit() {
517
- this.toastService.toasts
518
- .pipe(takeUntil(this.destroy$))
519
- .subscribe(toasts => {
520
- this.toasts = toasts;
521
- });
522
- this.themeService.currentTheme$
523
- .pipe(takeUntil(this.destroy$))
524
- .subscribe(theme => {
525
- this.currentTheme = theme;
526
- });
527
- }
528
- ngOnDestroy() {
529
- this.destroy$.next();
530
- this.destroy$.complete();
531
- }
532
- close(id) {
533
- this.toastService.remove(id);
534
- }
535
- }
536
- ToastContainerComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ToastContainerComponent, deps: [{ token: ToastService }, { token: ThemeService }], target: i0.ɵɵFactoryTarget.Component });
537
- ToastContainerComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: ToastContainerComponent, isStandalone: true, selector: "ma-toast-container", host: { properties: { "class": "this.themeClass" } }, ngImport: i0, template: `
529
+ class ToastContainerComponent {
530
+ toastService;
531
+ themeService;
532
+ get themeClass() {
533
+ return `theme-${this.currentTheme}`;
534
+ }
535
+ toasts = [];
536
+ currentTheme = 'light';
537
+ destroy$ = new Subject();
538
+ constructor(toastService, themeService) {
539
+ this.toastService = toastService;
540
+ this.themeService = themeService;
541
+ }
542
+ ngOnInit() {
543
+ this.toastService.toasts
544
+ .pipe(takeUntil(this.destroy$))
545
+ .subscribe(toasts => {
546
+ this.toasts = toasts;
547
+ });
548
+ this.themeService.currentTheme$
549
+ .pipe(takeUntil(this.destroy$))
550
+ .subscribe(theme => {
551
+ this.currentTheme = theme;
552
+ });
553
+ }
554
+ ngOnDestroy() {
555
+ this.destroy$.next();
556
+ this.destroy$.complete();
557
+ }
558
+ close(id) {
559
+ this.toastService.remove(id);
560
+ }
561
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: ToastContainerComponent, deps: [{ token: ToastService }, { token: ThemeService }], target: i0.ɵɵFactoryTarget.Component });
562
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.16", type: ToastContainerComponent, isStandalone: true, selector: "ma-toast-container", host: { properties: { "class": "this.themeClass" } }, ngImport: i0, template: `
538
563
  <div class="toast-container">
539
564
  <div
540
565
  *ngFor="let toast of toasts"
@@ -551,9 +576,10 @@ ToastContainerComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0"
551
576
  </button>
552
577
  </div>
553
578
  </div>
554
- `, 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"] }] });
555
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ToastContainerComponent, decorators: [{
556
- type: Component,
579
+ `, 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"] }] });
580
+ }
581
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: ToastContainerComponent, decorators: [{
582
+ type: Component,
557
583
  args: [{ selector: 'ma-toast-container', standalone: true, imports: [CommonModule], template: `
558
584
  <div class="toast-container">
559
585
  <div
@@ -571,159 +597,161 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
571
597
  </button>
572
598
  </div>
573
599
  </div>
574
- `, 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"] }]
575
- }], ctorParameters: function () { return [{ type: ToastService }, { type: ThemeService }]; }, propDecorators: { themeClass: [{
576
- type: HostBinding,
577
- args: ['class']
600
+ `, 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"] }]
601
+ }], ctorParameters: () => [{ type: ToastService }, { type: ThemeService }], propDecorators: { themeClass: [{
602
+ type: HostBinding,
603
+ args: ['class']
578
604
  }] } });
579
605
 
580
- class NotificationPanelComponent {
581
- constructor(authService, toastService, themeService) {
582
- this.authService = authService;
583
- this.toastService = toastService;
584
- this.themeService = themeService;
585
- this.notificationRead = new EventEmitter();
586
- this.isOpen = false;
587
- this.notifications = [];
588
- this.currentTheme = 'light';
589
- this.activeTab = 'unread'; // Default to unread tab
590
- this.destroy$ = new Subject();
591
- }
592
- get themeClass() {
593
- return `theme-${this.currentTheme}`;
594
- }
595
- get unreadNotifications() {
596
- return this.notifications.filter(n => !n.isRead);
597
- }
598
- get readNotifications() {
599
- return this.notifications.filter(n => n.isRead);
600
- }
601
- get currentNotifications() {
602
- return this.activeTab === 'unread' ? this.unreadNotifications : this.readNotifications;
603
- }
604
- getNotificationMessage(notification) {
605
- return notification.messageHtml || notification.message || '';
606
- }
607
- ngOnInit() {
608
- this.themeService.currentTheme$
609
- .pipe(takeUntil(this.destroy$))
610
- .subscribe(theme => {
611
- this.currentTheme = theme;
612
- });
613
- this.loadNotifications();
614
- // Listen for new real-time notifications
615
- this.authService.notifications$
616
- .pipe(takeUntil(this.destroy$))
617
- .subscribe((notification) => {
618
- // Show toast for new notification
619
- this.toastService.show(notification.messageHtml || notification.message || '', notification.title, 'info', 5000);
620
- // Reload notifications list
621
- this.loadNotifications();
622
- });
623
- }
624
- ngOnDestroy() {
625
- this.destroy$.next();
626
- this.destroy$.complete();
627
- }
628
- loadNotifications() {
629
- this.authService.getNotifications(1, 50, true).subscribe({
630
- next: (response) => {
631
- this.notifications = response.items || [];
632
- },
633
- error: (err) => { }
634
- });
635
- }
636
- open() {
637
- this.isOpen = true;
638
- this.activeTab = 'unread'; // Reset to unread tab when opening
639
- }
640
- close() {
641
- this.isOpen = false;
642
- }
643
- switchTab(tab) {
644
- this.activeTab = tab;
645
- }
646
- markAsRead(notificationId, event) {
647
- if (event) {
648
- event.stopPropagation();
649
- }
650
- this.authService.markAsRead(notificationId).subscribe({
651
- next: () => {
652
- const notification = this.notifications.find(n => n.id === notificationId);
653
- if (notification) {
654
- notification.isRead = true;
655
- this.notificationRead.emit();
656
- }
657
- },
658
- error: (err) => { }
659
- });
660
- }
661
- markAllAsRead() {
662
- this.authService.markAllAsRead().subscribe({
663
- next: () => {
664
- this.notifications.forEach(n => n.isRead = true);
665
- this.notificationRead.emit();
666
- },
667
- error: (err) => { }
668
- });
669
- }
670
- deleteAllRead() {
671
- const readNotificationIds = this.notifications
672
- .filter(n => n.isRead)
673
- .map(n => n.id);
674
- // Delete all read notifications
675
- const deletePromises = readNotificationIds.map(id => this.authService.deleteNotification(id).toPromise());
676
- Promise.all(deletePromises).then(() => {
677
- // Remove all read notifications from the local array
678
- this.notifications = this.notifications.filter(n => !n.isRead);
679
- }).catch((err) => {
680
- // If bulk delete fails, reload notifications to get current state
681
- this.loadNotifications();
682
- });
683
- }
684
- deleteAllUnread() {
685
- const unreadNotificationIds = this.notifications
686
- .filter(n => !n.isRead)
687
- .map(n => n.id);
688
- // Delete all unread notifications
689
- const deletePromises = unreadNotificationIds.map(id => this.authService.deleteNotification(id).toPromise());
690
- Promise.all(deletePromises).then(() => {
691
- // Remove all unread notifications from the local array
692
- this.notifications = this.notifications.filter(n => n.isRead);
693
- }).catch((err) => {
694
- // If bulk delete fails, reload notifications to get current state
695
- this.loadNotifications();
696
- });
697
- }
698
- delete(notificationId, event) {
699
- event.stopPropagation();
700
- this.authService.deleteNotification(notificationId).subscribe({
701
- next: () => {
702
- this.notifications = this.notifications.filter(n => n.id !== notificationId);
703
- },
704
- error: (err) => { }
705
- });
706
- }
707
- formatDate(dateString) {
708
- const date = new Date(dateString);
709
- const now = new Date();
710
- const diffMs = now.getTime() - date.getTime();
711
- const diffMins = Math.floor(diffMs / 60000);
712
- const diffHours = Math.floor(diffMs / 3600000);
713
- const diffDays = Math.floor(diffMs / 86400000);
714
- if (diffMins < 1)
715
- return 'Now';
716
- if (diffMins < 60)
717
- return `${diffMins}m ago`;
718
- if (diffHours < 24)
719
- return `${diffHours}h ago`;
720
- if (diffDays < 7)
721
- return `${diffDays}d ago`;
722
- return date.toLocaleDateString();
723
- }
724
- }
725
- NotificationPanelComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: NotificationPanelComponent, deps: [{ token: MesAuthService }, { token: ToastService }, { token: ThemeService }], target: i0.ɵɵFactoryTarget.Component });
726
- NotificationPanelComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: NotificationPanelComponent, isStandalone: true, selector: "ma-notification-panel", outputs: { notificationRead: "notificationRead" }, host: { properties: { "class": "this.themeClass" } }, ngImport: i0, template: `
606
+ class NotificationPanelComponent {
607
+ authService;
608
+ toastService;
609
+ themeService;
610
+ notificationRead = new EventEmitter();
611
+ get themeClass() {
612
+ return `theme-${this.currentTheme}`;
613
+ }
614
+ isOpen = false;
615
+ notifications = [];
616
+ currentTheme = 'light';
617
+ activeTab = 'unread'; // Default to unread tab
618
+ destroy$ = new Subject();
619
+ get unreadNotifications() {
620
+ return this.notifications.filter(n => !n.isRead);
621
+ }
622
+ get readNotifications() {
623
+ return this.notifications.filter(n => n.isRead);
624
+ }
625
+ get currentNotifications() {
626
+ return this.activeTab === 'unread' ? this.unreadNotifications : this.readNotifications;
627
+ }
628
+ getNotificationMessage(notification) {
629
+ return notification.messageHtml || notification.message || '';
630
+ }
631
+ constructor(authService, toastService, themeService) {
632
+ this.authService = authService;
633
+ this.toastService = toastService;
634
+ this.themeService = themeService;
635
+ }
636
+ ngOnInit() {
637
+ this.themeService.currentTheme$
638
+ .pipe(takeUntil(this.destroy$))
639
+ .subscribe(theme => {
640
+ this.currentTheme = theme;
641
+ });
642
+ this.loadNotifications();
643
+ // Listen for new real-time notifications
644
+ this.authService.notifications$
645
+ .pipe(takeUntil(this.destroy$))
646
+ .subscribe((notification) => {
647
+ // Show toast for new notification
648
+ this.toastService.show(notification.messageHtml || notification.message || '', notification.title, 'info', 5000);
649
+ // Reload notifications list
650
+ this.loadNotifications();
651
+ });
652
+ }
653
+ ngOnDestroy() {
654
+ this.destroy$.next();
655
+ this.destroy$.complete();
656
+ }
657
+ loadNotifications() {
658
+ this.authService.getNotifications(1, 50, true).subscribe({
659
+ next: (response) => {
660
+ this.notifications = response.items || [];
661
+ },
662
+ error: (err) => { }
663
+ });
664
+ }
665
+ open() {
666
+ this.isOpen = true;
667
+ this.activeTab = 'unread'; // Reset to unread tab when opening
668
+ }
669
+ close() {
670
+ this.isOpen = false;
671
+ }
672
+ switchTab(tab) {
673
+ this.activeTab = tab;
674
+ }
675
+ markAsRead(notificationId, event) {
676
+ if (event) {
677
+ event.stopPropagation();
678
+ }
679
+ this.authService.markAsRead(notificationId).subscribe({
680
+ next: () => {
681
+ const notification = this.notifications.find(n => n.id === notificationId);
682
+ if (notification) {
683
+ notification.isRead = true;
684
+ this.notificationRead.emit();
685
+ }
686
+ },
687
+ error: (err) => { }
688
+ });
689
+ }
690
+ markAllAsRead() {
691
+ this.authService.markAllAsRead().subscribe({
692
+ next: () => {
693
+ this.notifications.forEach(n => n.isRead = true);
694
+ this.notificationRead.emit();
695
+ },
696
+ error: (err) => { }
697
+ });
698
+ }
699
+ deleteAllRead() {
700
+ const readNotificationIds = this.notifications
701
+ .filter(n => n.isRead)
702
+ .map(n => n.id);
703
+ // Delete all read notifications
704
+ const deletePromises = readNotificationIds.map(id => this.authService.deleteNotification(id).toPromise());
705
+ Promise.all(deletePromises).then(() => {
706
+ // Remove all read notifications from the local array
707
+ this.notifications = this.notifications.filter(n => !n.isRead);
708
+ }).catch((err) => {
709
+ // If bulk delete fails, reload notifications to get current state
710
+ this.loadNotifications();
711
+ });
712
+ }
713
+ deleteAllUnread() {
714
+ const unreadNotificationIds = this.notifications
715
+ .filter(n => !n.isRead)
716
+ .map(n => n.id);
717
+ // Delete all unread notifications
718
+ const deletePromises = unreadNotificationIds.map(id => this.authService.deleteNotification(id).toPromise());
719
+ Promise.all(deletePromises).then(() => {
720
+ // Remove all unread notifications from the local array
721
+ this.notifications = this.notifications.filter(n => n.isRead);
722
+ }).catch((err) => {
723
+ // If bulk delete fails, reload notifications to get current state
724
+ this.loadNotifications();
725
+ });
726
+ }
727
+ delete(notificationId, event) {
728
+ event.stopPropagation();
729
+ this.authService.deleteNotification(notificationId).subscribe({
730
+ next: () => {
731
+ this.notifications = this.notifications.filter(n => n.id !== notificationId);
732
+ },
733
+ error: (err) => { }
734
+ });
735
+ }
736
+ formatDate(dateString) {
737
+ const date = new Date(dateString);
738
+ const now = new Date();
739
+ const diffMs = now.getTime() - date.getTime();
740
+ const diffMins = Math.floor(diffMs / 60000);
741
+ const diffHours = Math.floor(diffMs / 3600000);
742
+ const diffDays = Math.floor(diffMs / 86400000);
743
+ if (diffMins < 1)
744
+ return 'Now';
745
+ if (diffMins < 60)
746
+ return `${diffMins}m ago`;
747
+ if (diffHours < 24)
748
+ return `${diffHours}h ago`;
749
+ if (diffDays < 7)
750
+ return `${diffDays}d ago`;
751
+ return date.toLocaleDateString();
752
+ }
753
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: NotificationPanelComponent, deps: [{ token: MesAuthService }, { token: ToastService }, { token: ThemeService }], target: i0.ɵɵFactoryTarget.Component });
754
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.16", type: NotificationPanelComponent, isStandalone: true, selector: "ma-notification-panel", outputs: { notificationRead: "notificationRead" }, host: { properties: { "class": "this.themeClass" } }, ngImport: i0, template: `
727
755
  <div class="notification-panel" [class.open]="isOpen">
728
756
  <!-- Header -->
729
757
  <div class="panel-header">
@@ -807,9 +835,10 @@ NotificationPanelComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0
807
835
  </button>
808
836
  </div>
809
837
  </div>
810
- `, isInline: true, styles: [":host{--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){--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:1000;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:13px;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}@media (max-width: 600px){.notification-panel{width:100%;right:-100%}}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }] });
811
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: NotificationPanelComponent, decorators: [{
812
- type: Component,
838
+ `, isInline: true, styles: [":host{--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){--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:1000;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:13px;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}@media(max-width:600px){.notification-panel{width:100%;right:-100%}}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }] });
839
+ }
840
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: NotificationPanelComponent, decorators: [{
841
+ type: Component,
813
842
  args: [{ selector: 'ma-notification-panel', standalone: true, imports: [NgIf, NgFor], template: `
814
843
  <div class="notification-panel" [class.open]="isOpen">
815
844
  <!-- Header -->
@@ -894,108 +923,119 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
894
923
  </button>
895
924
  </div>
896
925
  </div>
897
- `, styles: [":host{--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){--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:1000;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:13px;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}@media (max-width: 600px){.notification-panel{width:100%;right:-100%}}\n"] }]
898
- }], ctorParameters: function () { return [{ type: MesAuthService }, { type: ToastService }, { type: ThemeService }]; }, propDecorators: { notificationRead: [{
899
- type: Output
900
- }], themeClass: [{
901
- type: HostBinding,
902
- args: ['class']
926
+ `, styles: [":host{--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){--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:1000;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:13px;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}@media(max-width:600px){.notification-panel{width:100%;right:-100%}}\n"] }]
927
+ }], ctorParameters: () => [{ type: MesAuthService }, { type: ToastService }, { type: ThemeService }], propDecorators: { notificationRead: [{
928
+ type: Output
929
+ }], themeClass: [{
930
+ type: HostBinding,
931
+ args: ['class']
903
932
  }] } });
904
933
 
905
- class MaUserComponent {
906
- onNotificationRead() {
907
- this.userProfile.loadUnreadCount();
908
- }
909
- }
910
- MaUserComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: MaUserComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
911
- MaUserComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: MaUserComponent, isStandalone: true, selector: "ma-user", viewQueries: [{ propertyName: "userProfile", first: true, predicate: UserProfileComponent, descendants: true }], ngImport: i0, template: `
934
+ class MaUserComponent {
935
+ userProfile;
936
+ ngAfterViewInit() {
937
+ // Ensure proper initialization
938
+ if (this.userProfile) {
939
+ this.userProfile.loadUnreadCount();
940
+ }
941
+ }
942
+ onNotificationRead() {
943
+ if (this.userProfile) {
944
+ this.userProfile.loadUnreadCount();
945
+ }
946
+ }
947
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: MaUserComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
948
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.16", type: MaUserComponent, isStandalone: true, selector: "ma-user", viewQueries: [{ propertyName: "userProfile", first: true, predicate: UserProfileComponent, descendants: true }], ngImport: i0, template: `
912
949
  <ma-toast-container></ma-toast-container>
913
950
  <div class="user-header">
914
951
  <ma-user-profile (notificationClick)="notificationPanel.open()"></ma-user-profile>
915
952
  </div>
916
953
  <ma-notification-panel #notificationPanel (notificationRead)="onNotificationRead()"></ma-notification-panel>
917
- `, isInline: true, styles: [".user-header{display:flex;justify-content:flex-end}\n"], dependencies: [{ kind: "component", type: ToastContainerComponent, selector: "ma-toast-container" }, { kind: "component", type: UserProfileComponent, selector: "ma-user-profile", outputs: ["notificationClick"] }, { kind: "component", type: NotificationPanelComponent, selector: "ma-notification-panel", outputs: ["notificationRead"] }] });
918
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: MaUserComponent, decorators: [{
919
- type: Component,
954
+ `, isInline: true, styles: [".user-header{display:flex;justify-content:flex-end}\n"], dependencies: [{ kind: "component", type: ToastContainerComponent, selector: "ma-toast-container" }, { kind: "component", type: UserProfileComponent, selector: "ma-user-profile", outputs: ["notificationClick"] }, { kind: "component", type: NotificationPanelComponent, selector: "ma-notification-panel", outputs: ["notificationRead"] }] });
955
+ }
956
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: MaUserComponent, decorators: [{
957
+ type: Component,
920
958
  args: [{ selector: 'ma-user', standalone: true, imports: [ToastContainerComponent, UserProfileComponent, NotificationPanelComponent], template: `
921
959
  <ma-toast-container></ma-toast-container>
922
960
  <div class="user-header">
923
961
  <ma-user-profile (notificationClick)="notificationPanel.open()"></ma-user-profile>
924
962
  </div>
925
963
  <ma-notification-panel #notificationPanel (notificationRead)="onNotificationRead()"></ma-notification-panel>
926
- `, styles: [".user-header{display:flex;justify-content:flex-end}\n"] }]
927
- }], propDecorators: { userProfile: [{
928
- type: ViewChild,
929
- args: [UserProfileComponent]
964
+ `, styles: [".user-header{display:flex;justify-content:flex-end}\n"] }]
965
+ }], propDecorators: { userProfile: [{
966
+ type: ViewChild,
967
+ args: [UserProfileComponent]
930
968
  }] } });
931
969
 
932
- class NotificationBadgeComponent {
933
- constructor(authService, themeService) {
934
- this.authService = authService;
935
- this.themeService = themeService;
936
- this.notificationClick = new EventEmitter();
937
- this.unreadCount = 0;
938
- this.currentTheme = 'light';
939
- this.destroy$ = new Subject();
940
- }
941
- get themeClass() {
942
- return `theme-${this.currentTheme}`;
943
- }
944
- ngOnInit() {
945
- this.themeService.currentTheme$
946
- .pipe(takeUntil(this.destroy$))
947
- .subscribe(theme => {
948
- this.currentTheme = theme;
949
- });
950
- this.loadUnreadCount();
951
- // Listen for new notifications
952
- this.authService.notifications$
953
- .pipe(takeUntil(this.destroy$))
954
- .subscribe(() => {
955
- this.loadUnreadCount();
956
- });
957
- }
958
- ngOnDestroy() {
959
- this.destroy$.next();
960
- this.destroy$.complete();
961
- }
962
- loadUnreadCount() {
963
- this.authService.getUnreadCount().subscribe({
964
- next: (response) => {
965
- this.unreadCount = response.unreadCount || 0;
966
- },
967
- error: (err) => console.error('Error loading unread count:', err)
968
- });
969
- }
970
- onNotificationClick() {
971
- this.notificationClick.emit();
972
- }
973
- }
974
- NotificationBadgeComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: NotificationBadgeComponent, deps: [{ token: MesAuthService }, { token: ThemeService }], target: i0.ɵɵFactoryTarget.Component });
975
- NotificationBadgeComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: NotificationBadgeComponent, isStandalone: true, selector: "ma-notification-badge", outputs: { notificationClick: "notificationClick" }, host: { properties: { "class": "this.themeClass" } }, ngImport: i0, template: `
970
+ class NotificationBadgeComponent {
971
+ authService;
972
+ themeService;
973
+ notificationClick = new EventEmitter();
974
+ get themeClass() {
975
+ return `theme-${this.currentTheme}`;
976
+ }
977
+ unreadCount = 0;
978
+ currentTheme = 'light';
979
+ destroy$ = new Subject();
980
+ constructor(authService, themeService) {
981
+ this.authService = authService;
982
+ this.themeService = themeService;
983
+ }
984
+ ngOnInit() {
985
+ this.themeService.currentTheme$
986
+ .pipe(takeUntil(this.destroy$))
987
+ .subscribe(theme => {
988
+ this.currentTheme = theme;
989
+ });
990
+ this.loadUnreadCount();
991
+ // Listen for new notifications
992
+ this.authService.notifications$
993
+ .pipe(takeUntil(this.destroy$))
994
+ .subscribe(() => {
995
+ this.loadUnreadCount();
996
+ });
997
+ }
998
+ ngOnDestroy() {
999
+ this.destroy$.next();
1000
+ this.destroy$.complete();
1001
+ }
1002
+ loadUnreadCount() {
1003
+ this.authService.getUnreadCount().subscribe({
1004
+ next: (response) => {
1005
+ this.unreadCount = response.unreadCount || 0;
1006
+ },
1007
+ error: (err) => console.error('Error loading unread count:', err)
1008
+ });
1009
+ }
1010
+ onNotificationClick() {
1011
+ this.notificationClick.emit();
1012
+ }
1013
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: NotificationBadgeComponent, deps: [{ token: MesAuthService }, { token: ThemeService }], target: i0.ɵɵFactoryTarget.Component });
1014
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.16", type: NotificationBadgeComponent, isStandalone: true, selector: "ma-notification-badge", outputs: { notificationClick: "notificationClick" }, host: { properties: { "class": "this.themeClass" } }, ngImport: i0, template: `
976
1015
  <button class="notification-btn" (click)="onNotificationClick()" title="Notifications">
977
1016
  <span class="icon">🔔</span>
978
1017
  <span class="badge" *ngIf="unreadCount > 0">{{ unreadCount }}</span>
979
1018
  </button>
980
- `, isInline: true, styles: [":host{--error-color: #f44336}:host(.theme-dark){--error-color: #ef5350}.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}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
981
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: NotificationBadgeComponent, decorators: [{
982
- type: Component,
1019
+ `, isInline: true, styles: [":host{--error-color: #f44336}:host(.theme-dark){--error-color: #ef5350}.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}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
1020
+ }
1021
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: NotificationBadgeComponent, decorators: [{
1022
+ type: Component,
983
1023
  args: [{ selector: 'ma-notification-badge', standalone: true, imports: [NgIf], template: `
984
1024
  <button class="notification-btn" (click)="onNotificationClick()" title="Notifications">
985
1025
  <span class="icon">🔔</span>
986
1026
  <span class="badge" *ngIf="unreadCount > 0">{{ unreadCount }}</span>
987
1027
  </button>
988
- `, styles: [":host{--error-color: #f44336}:host(.theme-dark){--error-color: #ef5350}.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}\n"] }]
989
- }], ctorParameters: function () { return [{ type: MesAuthService }, { type: ThemeService }]; }, propDecorators: { notificationClick: [{
990
- type: Output
991
- }], themeClass: [{
992
- type: HostBinding,
993
- args: ['class']
1028
+ `, styles: [":host{--error-color: #f44336}:host(.theme-dark){--error-color: #ef5350}.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}\n"] }]
1029
+ }], ctorParameters: () => [{ type: MesAuthService }, { type: ThemeService }], propDecorators: { notificationClick: [{
1030
+ type: Output
1031
+ }], themeClass: [{
1032
+ type: HostBinding,
1033
+ args: ['class']
994
1034
  }] } });
995
1035
 
996
- /**
997
- * Generated bundle index. Do not edit.
1036
+ /**
1037
+ * Generated bundle index. Do not edit.
998
1038
  */
999
1039
 
1000
- export { MaUserComponent, MesAuthInterceptor, MesAuthModule, MesAuthService, NotificationBadgeComponent, NotificationPanelComponent, NotificationType, ThemeService, ToastContainerComponent, UserProfileComponent };
1040
+ export { MaUserComponent, MesAuthInterceptor, MesAuthModule, MesAuthService, NotificationBadgeComponent, NotificationPanelComponent, NotificationType, ThemeService, ToastContainerComponent, ToastService, UserProfileComponent };
1001
1041
  //# sourceMappingURL=mesauth-angular.mjs.map