mesauth-angular 0.2.15 → 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 -691
  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 -175
  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 -1042
  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 -121
  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,395 +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._feNav = new BehaviorSubject(null);
30
- this.feNav$ = this._feNav.asObservable();
31
- this.apiBase = '';
32
- this.config = null;
33
- // Listen for route changes - only refresh user data if needed for SPA navigation
34
- // This helps maintain authentication state in single-page applications
35
- if (this.router) {
36
- this.router.events
37
- .pipe(filter(event => event instanceof NavigationEnd), debounceTime(1000) // Longer debounce to avoid interfering with login flow
38
- )
39
- .subscribe((event) => {
40
- // Only refresh if user is logged in and navigating to protected routes
41
- // Avoid refreshing during login/logout flows
42
- if (this._currentUser.value && this.isProtectedRoute(event.url)) {
43
- // Small delay to ensure any login/logout operations complete
44
- setTimeout(() => {
45
- if (this._currentUser.value) {
46
- this.refreshUser();
47
- }
48
- }, 100);
49
- }
50
- });
51
- }
52
- }
53
- isProtectedRoute(url) {
54
- // Consider routes protected if they don't include auth-related paths
55
- return !url.includes('/login') && !url.includes('/auth') && !url.includes('/signin') && !url.includes('/logout');
56
- }
57
- init(config) {
58
- this.config = config;
59
- this.apiBase = config.apiBaseUrl.replace(/\/$/, '');
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
- this.fetchFeNav();
76
- }
77
- else {
78
- this._feNav.next(null);
79
- }
80
- },
81
- error: (err) => {
82
- this._currentUser.next(null);
83
- this._feNav.next(null);
84
- }
85
- });
86
- }
87
- fetchInitialNotifications() {
88
- if (!this.apiBase)
89
- return;
90
- this.http.get(`${this.apiBase}/notif/me`, { withCredentials: this.config?.withCredentials ?? true }).subscribe({
91
- next: (notifications) => {
92
- if (Array.isArray(notifications?.items)) {
93
- notifications.items.forEach((n) => this._notifications.next(n));
94
- }
95
- },
96
- error: (err) => { }
97
- });
98
- }
99
- fetchFeNav() {
100
- if (!this.apiBase || !this._currentUser.value?.userId)
101
- return;
102
- const url = `${this.apiBase}/fenavs/${this._currentUser.value.userId}`;
103
- this.http.get(url, { withCredentials: this.config?.withCredentials ?? true }).subscribe({
104
- next: (response) => {
105
- if (response?.content) {
106
- const feNavData = {
107
- items: response.content,
108
- lastUpdated: new Date().toISOString()
109
- };
110
- this._feNav.next(feNavData);
111
- }
112
- },
113
- error: (err) => {
114
- this._feNav.next(null);
115
- }
116
- });
117
- }
118
- getUnreadCount() {
119
- return this.http.get(`${this.apiBase}/notif/me/unread-count`, { withCredentials: this.config?.withCredentials ?? true });
120
- }
121
- getNotifications(page = 1, pageSize = 20, includeRead = false, type) {
122
- let url = `${this.apiBase}/notif/me?page=${page}&pageSize=${pageSize}&includeRead=${includeRead}`;
123
- if (type) {
124
- url += `&type=${type}`;
125
- }
126
- return this.http.get(url, { withCredentials: this.config?.withCredentials ?? true });
127
- }
128
- markAsRead(notificationId) {
129
- return this.http.patch(`${this.apiBase}/notif/${notificationId}/read`, {}, { withCredentials: this.config?.withCredentials ?? true });
130
- }
131
- markAllAsRead() {
132
- return this.http.patch(`${this.apiBase}/notif/me/read-all`, {}, { withCredentials: this.config?.withCredentials ?? true });
133
- }
134
- deleteNotification(notificationId) {
135
- return this.http.delete(`${this.apiBase}/notif/${notificationId}`, { withCredentials: this.config?.withCredentials ?? true });
136
- }
137
- startConnection(config) {
138
- if (this.hubConnection)
139
- return;
140
- const signalrUrl = config.apiBaseUrl.replace(/\/$/, '') + '/hub/notification';
141
- const builder = new HubConnectionBuilder()
142
- .withUrl(signalrUrl, { withCredentials: config.withCredentials ?? true })
143
- .withAutomaticReconnect()
144
- .configureLogging(LogLevel.Warning);
145
- this.hubConnection = builder.build();
146
- this.hubConnection.on('ReceiveNotification', (n) => {
147
- this._notifications.next(n);
148
- });
149
- this.hubConnection.start().then(() => { }).catch((err) => { });
150
- this.hubConnection.onclose(() => { });
151
- this.hubConnection.onreconnecting(() => { });
152
- this.hubConnection.onreconnected(() => { });
153
- }
154
- stop() {
155
- if (!this.hubConnection)
156
- return;
157
- this.hubConnection.stop().catch(() => { });
158
- this.hubConnection = null;
159
- }
160
- logout() {
161
- const url = `${this.apiBase}/auth/logout`;
162
- return this.http.post(url, {}, { withCredentials: this.config?.withCredentials ?? true }).pipe(tap(() => {
163
- this._currentUser.next(null);
164
- this._feNav.next(null);
165
- this.stop();
166
- }));
167
- }
168
- refreshUser() {
169
- this.fetchCurrentUser();
170
- }
171
- }
172
- 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 });
173
- MesAuthService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: MesAuthService });
174
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: MesAuthService, decorators: [{
175
- type: Injectable
176
- }], ctorParameters: function () { return [{ type: i1.HttpClient }, { type: i2.Router, decorators: [{
177
- type: Optional
178
- }] }]; } });
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: () => [] });
179
168
 
180
- class MesAuthInterceptor {
181
- constructor(authService, router) {
182
- this.authService = authService;
183
- this.router = router;
184
- }
185
- intercept(req, next) {
186
- return next.handle(req).pipe(catchError((error) => {
187
- if (error.status === 403) {
188
- const config = this.authService.getConfig();
189
- const baseUrl = config?.userBaseUrl || '';
190
- const returnUrl = encodeURIComponent(window.location.href);
191
- window.location.href = `${baseUrl}/403?returnUrl=${returnUrl}`;
192
- }
193
- return throwError(error);
194
- }));
195
- }
196
- }
197
- 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 });
198
- MesAuthInterceptor.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: MesAuthInterceptor });
199
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: MesAuthInterceptor, decorators: [{
200
- type: Injectable
201
- }], 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 }] });
202
196
 
203
- class MesAuthModule {
204
- }
205
- MesAuthModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: MesAuthModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
206
- MesAuthModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "14.3.0", ngImport: i0, type: MesAuthModule });
207
- MesAuthModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: MesAuthModule, providers: [
208
- MesAuthService,
209
- { provide: HTTP_INTERCEPTORS, useClass: MesAuthInterceptor, multi: true }
210
- ] });
211
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: MesAuthModule, decorators: [{
212
- type: NgModule,
213
- args: [{
214
- providers: [
215
- MesAuthService,
216
- { provide: HTTP_INTERCEPTORS, useClass: MesAuthInterceptor, multi: true }
217
- ]
218
- }]
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
+ }]
219
213
  }] });
220
214
 
221
- class ThemeService {
222
- constructor() {
223
- this._currentTheme = new BehaviorSubject('light');
224
- this.currentTheme$ = this._currentTheme.asObservable();
225
- this.observer = null;
226
- this.detectTheme();
227
- this.startWatching();
228
- }
229
- ngOnDestroy() {
230
- this.stopWatching();
231
- }
232
- detectTheme() {
233
- const html = document.documentElement;
234
- const isDark = html.classList.contains('dark') ||
235
- html.getAttribute('data-theme') === 'dark' ||
236
- html.getAttribute('theme') === 'dark' ||
237
- html.getAttribute('data-coreui-theme') === 'dark';
238
- this._currentTheme.next(isDark ? 'dark' : 'light');
239
- }
240
- startWatching() {
241
- if (typeof MutationObserver === 'undefined') {
242
- // Fallback for older browsers - check periodically
243
- setInterval(() => this.detectTheme(), 1000);
244
- return;
245
- }
246
- this.observer = new MutationObserver(() => {
247
- this.detectTheme();
248
- });
249
- this.observer.observe(document.documentElement, {
250
- attributes: true,
251
- attributeFilter: ['class', 'data-theme', 'theme', 'data-coreui-theme']
252
- });
253
- }
254
- stopWatching() {
255
- if (this.observer) {
256
- this.observer.disconnect();
257
- this.observer = null;
258
- }
259
- }
260
- get currentTheme() {
261
- return this._currentTheme.value;
262
- }
263
- // Method to manually set theme if needed
264
- setTheme(theme) {
265
- this._currentTheme.next(theme);
266
- }
267
- // Re-detect theme from DOM
268
- refreshTheme() {
269
- this.detectTheme();
270
- }
271
- }
272
- ThemeService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ThemeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
273
- ThemeService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ThemeService, providedIn: 'root' });
274
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ThemeService, decorators: [{
275
- type: Injectable,
276
- args: [{
277
- providedIn: 'root'
278
- }]
279
- }], 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: () => [] });
280
274
 
281
- class UserProfileComponent {
282
- constructor(authService, router, themeService) {
283
- this.authService = authService;
284
- this.router = router;
285
- this.themeService = themeService;
286
- this.notificationClick = new EventEmitter();
287
- this.currentUser = null;
288
- this.currentTheme = 'light';
289
- this.unreadCount = 0;
290
- this.dropdownOpen = false;
291
- this.destroy$ = new Subject();
292
- }
293
- get themeClass() {
294
- return `theme-${this.currentTheme}`;
295
- }
296
- ngOnInit() {
297
- this.authService.currentUser$
298
- .pipe(takeUntil(this.destroy$))
299
- .subscribe(user => {
300
- this.currentUser = user;
301
- });
302
- this.themeService.currentTheme$
303
- .pipe(takeUntil(this.destroy$))
304
- .subscribe(theme => {
305
- this.currentTheme = theme;
306
- });
307
- this.loadUnreadCount();
308
- // Listen for new notifications
309
- this.authService.notifications$
310
- .pipe(takeUntil(this.destroy$))
311
- .subscribe(() => {
312
- console.log('Notification received, updating unread count');
313
- this.loadUnreadCount();
314
- });
315
- }
316
- ngOnDestroy() {
317
- this.destroy$.next();
318
- this.destroy$.complete();
319
- }
320
- loadUnreadCount() {
321
- this.authService.getUnreadCount().subscribe({
322
- next: (response) => {
323
- this.unreadCount = response.unreadCount || 0;
324
- },
325
- error: (err) => { }
326
- });
327
- }
328
- getAvatarUrl(user) {
329
- const config = this.authService.getConfig();
330
- const baseUrl = config?.apiBaseUrl || '';
331
- // Use userId for the avatar endpoint
332
- const userId = user.userId;
333
- if (userId && baseUrl) {
334
- return `${baseUrl.replace(/\/$/, '')}/auth/${userId}/avatar`;
335
- }
336
- // Fallback to UI avatars service if no userId or baseUrl
337
- const displayName = user.userName || user.userId || 'User';
338
- return `https://ui-avatars.com/api/?name=${encodeURIComponent(displayName)}&background=1976d2&color=fff`;
339
- }
340
- getLastNameInitial(user) {
341
- const fullName = user.fullName || user.userName || 'U';
342
- const parts = fullName.split(' ');
343
- const lastPart = parts[parts.length - 1];
344
- return lastPart.charAt(0).toUpperCase();
345
- }
346
- toggleDropdown() {
347
- this.dropdownOpen = !this.dropdownOpen;
348
- }
349
- onDocumentClick(event) {
350
- const target = event.target;
351
- const clickedInside = target.closest('.user-menu-wrapper');
352
- if (!clickedInside) {
353
- this.dropdownOpen = false;
354
- }
355
- }
356
- onLogin() {
357
- const config = this.authService.getConfig();
358
- const baseUrl = config?.userBaseUrl || '';
359
- const returnUrl = encodeURIComponent(this.router.url);
360
- window.location.href = `${baseUrl}/login?returnUrl=${returnUrl}`;
361
- }
362
- onViewProfile() {
363
- const config = this.authService.getConfig();
364
- const baseUrl = config?.userBaseUrl || '';
365
- window.location.href = `${baseUrl}/profile`;
366
- this.dropdownOpen = false;
367
- }
368
- onLogout() {
369
- this.authService.logout().subscribe({
370
- next: () => {
371
- // Clear current user after successful logout
372
- this.dropdownOpen = false;
373
- // Navigate to login with return URL
374
- const config = this.authService.getConfig();
375
- const baseUrl = config?.userBaseUrl || '';
376
- const returnUrl = encodeURIComponent(window.location.href);
377
- window.location.href = `${baseUrl}/login?returnUrl=${returnUrl}`;
378
- },
379
- error: (err) => {
380
- // Still navigate to login even if logout fails
381
- const config = this.authService.getConfig();
382
- const baseUrl = config?.userBaseUrl || '';
383
- window.location.href = `${baseUrl}/login`;
384
- }
385
- });
386
- }
387
- onNotificationClick() {
388
- this.notificationClick.emit();
389
- }
390
- }
391
- 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 });
392
- 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: `
393
389
  <div class="user-profile-container">
394
390
  <!-- Not logged in -->
395
391
  <ng-container *ngIf="!currentUser">
@@ -434,9 +430,10 @@ UserProfileComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", v
434
430
  </div>
435
431
  </ng-container>
436
432
  </div>
437
- `, 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"] }] });
438
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: UserProfileComponent, decorators: [{
439
- 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,
440
437
  args: [{ selector: 'ma-user-profile', standalone: true, imports: [NgIf], template: `
441
438
  <div class="user-profile-container">
442
439
  <!-- Not logged in -->
@@ -482,88 +479,87 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
482
479
  </div>
483
480
  </ng-container>
484
481
  </div>
485
- `, 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"] }]
486
- }], ctorParameters: function () { return [{ type: MesAuthService }, { type: i2.Router }, { type: ThemeService }]; }, propDecorators: { notificationClick: [{
487
- type: Output
488
- }], themeClass: [{
489
- type: HostBinding,
490
- args: ['class']
491
- }], onDocumentClick: [{
492
- type: HostListener,
493
- 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']]
494
491
  }] } });
495
492
 
496
- class ToastService {
497
- constructor() {
498
- this.toasts$ = new BehaviorSubject([]);
499
- this.toasts = this.toasts$.asObservable();
500
- }
501
- show(message, title, type = 'info', duration = 5000) {
502
- const id = Math.random().toString(36).substr(2, 9);
503
- const toast = {
504
- id,
505
- message,
506
- title,
507
- type,
508
- duration
509
- };
510
- const currentToasts = this.toasts$.value;
511
- this.toasts$.next([...currentToasts, toast]);
512
- if (duration > 0) {
513
- setTimeout(() => {
514
- this.remove(id);
515
- }, duration);
516
- }
517
- return id;
518
- }
519
- remove(id) {
520
- const currentToasts = this.toasts$.value;
521
- this.toasts$.next(currentToasts.filter(t => t.id !== id));
522
- }
523
- clear() {
524
- this.toasts$.next([]);
525
- }
526
- }
527
- ToastService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ToastService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
528
- ToastService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ToastService, providedIn: 'root' });
529
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ToastService, decorators: [{
530
- type: Injectable,
531
- 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' }]
532
527
  }] });
533
528
 
534
- class ToastContainerComponent {
535
- constructor(toastService, themeService) {
536
- this.toastService = toastService;
537
- this.themeService = themeService;
538
- this.toasts = [];
539
- this.currentTheme = 'light';
540
- this.destroy$ = new Subject();
541
- }
542
- get themeClass() {
543
- return `theme-${this.currentTheme}`;
544
- }
545
- ngOnInit() {
546
- this.toastService.toasts
547
- .pipe(takeUntil(this.destroy$))
548
- .subscribe(toasts => {
549
- this.toasts = toasts;
550
- });
551
- this.themeService.currentTheme$
552
- .pipe(takeUntil(this.destroy$))
553
- .subscribe(theme => {
554
- this.currentTheme = theme;
555
- });
556
- }
557
- ngOnDestroy() {
558
- this.destroy$.next();
559
- this.destroy$.complete();
560
- }
561
- close(id) {
562
- this.toastService.remove(id);
563
- }
564
- }
565
- 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 });
566
- 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: `
567
563
  <div class="toast-container">
568
564
  <div
569
565
  *ngFor="let toast of toasts"
@@ -580,9 +576,10 @@ ToastContainerComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0"
580
576
  </button>
581
577
  </div>
582
578
  </div>
583
- `, 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"] }] });
584
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ToastContainerComponent, decorators: [{
585
- 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,
586
583
  args: [{ selector: 'ma-toast-container', standalone: true, imports: [CommonModule], template: `
587
584
  <div class="toast-container">
588
585
  <div
@@ -600,159 +597,161 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
600
597
  </button>
601
598
  </div>
602
599
  </div>
603
- `, 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"] }]
604
- }], ctorParameters: function () { return [{ type: ToastService }, { type: ThemeService }]; }, propDecorators: { themeClass: [{
605
- type: HostBinding,
606
- 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']
607
604
  }] } });
608
605
 
609
- class NotificationPanelComponent {
610
- constructor(authService, toastService, themeService) {
611
- this.authService = authService;
612
- this.toastService = toastService;
613
- this.themeService = themeService;
614
- this.notificationRead = new EventEmitter();
615
- this.isOpen = false;
616
- this.notifications = [];
617
- this.currentTheme = 'light';
618
- this.activeTab = 'unread'; // Default to unread tab
619
- this.destroy$ = new Subject();
620
- }
621
- get themeClass() {
622
- return `theme-${this.currentTheme}`;
623
- }
624
- get unreadNotifications() {
625
- return this.notifications.filter(n => !n.isRead);
626
- }
627
- get readNotifications() {
628
- return this.notifications.filter(n => n.isRead);
629
- }
630
- get currentNotifications() {
631
- return this.activeTab === 'unread' ? this.unreadNotifications : this.readNotifications;
632
- }
633
- getNotificationMessage(notification) {
634
- return notification.messageHtml || notification.message || '';
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
- }
754
- 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 });
755
- 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: `
756
755
  <div class="notification-panel" [class.open]="isOpen">
757
756
  <!-- Header -->
758
757
  <div class="panel-header">
@@ -836,9 +835,10 @@ NotificationPanelComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0
836
835
  </button>
837
836
  </div>
838
837
  </div>
839
- `, 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"] }] });
840
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: NotificationPanelComponent, decorators: [{
841
- 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,
842
842
  args: [{ selector: 'ma-notification-panel', standalone: true, imports: [NgIf, NgFor], template: `
843
843
  <div class="notification-panel" [class.open]="isOpen">
844
844
  <!-- Header -->
@@ -923,108 +923,119 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
923
923
  </button>
924
924
  </div>
925
925
  </div>
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: function () { return [{ type: MesAuthService }, { type: ToastService }, { type: ThemeService }]; }, propDecorators: { notificationRead: [{
928
- type: Output
929
- }], themeClass: [{
930
- type: HostBinding,
931
- 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']
932
932
  }] } });
933
933
 
934
- class MaUserComponent {
935
- onNotificationRead() {
936
- this.userProfile.loadUnreadCount();
937
- }
938
- }
939
- MaUserComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: MaUserComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
940
- 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: `
941
949
  <ma-toast-container></ma-toast-container>
942
950
  <div class="user-header">
943
951
  <ma-user-profile (notificationClick)="notificationPanel.open()"></ma-user-profile>
944
952
  </div>
945
953
  <ma-notification-panel #notificationPanel (notificationRead)="onNotificationRead()"></ma-notification-panel>
946
- `, 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"] }] });
947
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: MaUserComponent, decorators: [{
948
- 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,
949
958
  args: [{ selector: 'ma-user', standalone: true, imports: [ToastContainerComponent, UserProfileComponent, NotificationPanelComponent], template: `
950
959
  <ma-toast-container></ma-toast-container>
951
960
  <div class="user-header">
952
961
  <ma-user-profile (notificationClick)="notificationPanel.open()"></ma-user-profile>
953
962
  </div>
954
963
  <ma-notification-panel #notificationPanel (notificationRead)="onNotificationRead()"></ma-notification-panel>
955
- `, styles: [".user-header{display:flex;justify-content:flex-end}\n"] }]
956
- }], propDecorators: { userProfile: [{
957
- type: ViewChild,
958
- args: [UserProfileComponent]
964
+ `, styles: [".user-header{display:flex;justify-content:flex-end}\n"] }]
965
+ }], propDecorators: { userProfile: [{
966
+ type: ViewChild,
967
+ args: [UserProfileComponent]
959
968
  }] } });
960
969
 
961
- class NotificationBadgeComponent {
962
- constructor(authService, themeService) {
963
- this.authService = authService;
964
- this.themeService = themeService;
965
- this.notificationClick = new EventEmitter();
966
- this.unreadCount = 0;
967
- this.currentTheme = 'light';
968
- this.destroy$ = new Subject();
969
- }
970
- get themeClass() {
971
- return `theme-${this.currentTheme}`;
972
- }
973
- ngOnInit() {
974
- this.themeService.currentTheme$
975
- .pipe(takeUntil(this.destroy$))
976
- .subscribe(theme => {
977
- this.currentTheme = theme;
978
- });
979
- this.loadUnreadCount();
980
- // Listen for new notifications
981
- this.authService.notifications$
982
- .pipe(takeUntil(this.destroy$))
983
- .subscribe(() => {
984
- this.loadUnreadCount();
985
- });
986
- }
987
- ngOnDestroy() {
988
- this.destroy$.next();
989
- this.destroy$.complete();
990
- }
991
- loadUnreadCount() {
992
- this.authService.getUnreadCount().subscribe({
993
- next: (response) => {
994
- this.unreadCount = response.unreadCount || 0;
995
- },
996
- error: (err) => console.error('Error loading unread count:', err)
997
- });
998
- }
999
- onNotificationClick() {
1000
- this.notificationClick.emit();
1001
- }
1002
- }
1003
- 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 });
1004
- 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: `
1005
1015
  <button class="notification-btn" (click)="onNotificationClick()" title="Notifications">
1006
1016
  <span class="icon">🔔</span>
1007
1017
  <span class="badge" *ngIf="unreadCount > 0">{{ unreadCount }}</span>
1008
1018
  </button>
1009
- `, 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"] }] });
1010
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: NotificationBadgeComponent, decorators: [{
1011
- 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,
1012
1023
  args: [{ selector: 'ma-notification-badge', standalone: true, imports: [NgIf], template: `
1013
1024
  <button class="notification-btn" (click)="onNotificationClick()" title="Notifications">
1014
1025
  <span class="icon">🔔</span>
1015
1026
  <span class="badge" *ngIf="unreadCount > 0">{{ unreadCount }}</span>
1016
1027
  </button>
1017
- `, 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"] }]
1018
- }], ctorParameters: function () { return [{ type: MesAuthService }, { type: ThemeService }]; }, propDecorators: { notificationClick: [{
1019
- type: Output
1020
- }], themeClass: [{
1021
- type: HostBinding,
1022
- 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']
1023
1034
  }] } });
1024
1035
 
1025
- /**
1026
- * Generated bundle index. Do not edit.
1036
+ /**
1037
+ * Generated bundle index. Do not edit.
1027
1038
  */
1028
1039
 
1029
- export { MaUserComponent, MesAuthInterceptor, MesAuthModule, MesAuthService, NotificationBadgeComponent, NotificationPanelComponent, NotificationType, ThemeService, ToastContainerComponent, UserProfileComponent };
1040
+ export { MaUserComponent, MesAuthInterceptor, MesAuthModule, MesAuthService, NotificationBadgeComponent, NotificationPanelComponent, NotificationType, ThemeService, ToastContainerComponent, ToastService, UserProfileComponent };
1030
1041
  //# sourceMappingURL=mesauth-angular.mjs.map