mesauth-angular 0.2.15 → 0.2.26

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 +796 -691
  4. package/dist/fesm2022/mesauth-angular.mjs.map +1 -0
  5. package/dist/index.d.ts +355 -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,485 @@
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
+ /**
120
+ * Get frontend routes assigned to the current user
121
+ * Returns routes grouped by application
122
+ */
123
+ getFrontEndRoutes() {
124
+ if (!this.apiBase)
125
+ throw new Error('MesAuth not initialized');
126
+ return this.http.get(`${this.apiBase}/fe-routes/me`, { withCredentials: this.config?.withCredentials ?? true });
127
+ }
128
+ /**
129
+ * Get master routes for a specific application
130
+ * @param appId - The application ID
131
+ */
132
+ getRouteMasters(appId) {
133
+ if (!this.apiBase)
134
+ throw new Error('MesAuth not initialized');
135
+ return this.http.get(`${this.apiBase}/fe-routes/masters/${appId}`, { withCredentials: this.config?.withCredentials ?? true });
136
+ }
137
+ /**
138
+ * Register/sync frontend routes for an application
139
+ * This is typically called on app startup to sync routes from the frontend app
140
+ * @param appId - The application ID (passed via X-App-Id header)
141
+ * @param routes - Array of route definitions
142
+ */
143
+ registerFrontEndRoutes(appId, routes) {
144
+ if (!this.apiBase)
145
+ throw new Error('MesAuth not initialized');
146
+ const headers = { 'X-App-Id': appId };
147
+ return this.http.post(`${this.apiBase}/fe-routes/register`, routes, {
148
+ headers,
149
+ withCredentials: this.config?.withCredentials ?? true
150
+ });
151
+ }
152
+ /**
153
+ * Create a new route master
154
+ * @param appId - The application ID
155
+ * @param route - Route details
156
+ */
157
+ createRouteMaster(appId, route) {
158
+ if (!this.apiBase)
159
+ throw new Error('MesAuth not initialized');
160
+ return this.http.post(`${this.apiBase}/fe-routes/masters`, {
161
+ appId,
162
+ ...route
163
+ }, { withCredentials: this.config?.withCredentials ?? true });
164
+ }
165
+ /**
166
+ * Update an existing route master
167
+ * @param routeId - The route master ID
168
+ * @param route - Updated route details
169
+ */
170
+ updateRouteMaster(routeId, route) {
171
+ if (!this.apiBase)
172
+ throw new Error('MesAuth not initialized');
173
+ return this.http.put(`${this.apiBase}/fe-routes/masters/${routeId}`, route, {
174
+ withCredentials: this.config?.withCredentials ?? true
175
+ });
176
+ }
177
+ /**
178
+ * Delete a route master
179
+ * @param routeId - The route master ID
180
+ */
181
+ deleteRouteMaster(routeId) {
182
+ if (!this.apiBase)
183
+ throw new Error('MesAuth not initialized');
184
+ return this.http.delete(`${this.apiBase}/fe-routes/masters/${routeId}`, {
185
+ withCredentials: this.config?.withCredentials ?? true
186
+ });
187
+ }
188
+ /**
189
+ * Assign a route to a role
190
+ * @param routeMasterId - The route master ID
191
+ * @param roleId - The role ID (GUID)
192
+ */
193
+ assignRouteToRole(routeMasterId, roleId) {
194
+ if (!this.apiBase)
195
+ throw new Error('MesAuth not initialized');
196
+ return this.http.post(`${this.apiBase}/fe-routes/mappings`, {
197
+ routeMasterId,
198
+ roleId
199
+ }, { withCredentials: this.config?.withCredentials ?? true });
200
+ }
201
+ /**
202
+ * Remove a route assignment from a role
203
+ * @param mappingId - The mapping ID
204
+ */
205
+ removeRouteFromRole(mappingId) {
206
+ if (!this.apiBase)
207
+ throw new Error('MesAuth not initialized');
208
+ return this.http.delete(`${this.apiBase}/fe-routes/mappings/${mappingId}`, {
209
+ withCredentials: this.config?.withCredentials ?? true
210
+ });
211
+ }
212
+ /**
213
+ * Get route-to-role mappings for a specific role
214
+ * @param roleId - The role ID (GUID)
215
+ */
216
+ getRouteMappingsByRole(roleId) {
217
+ if (!this.apiBase)
218
+ throw new Error('MesAuth not initialized');
219
+ return this.http.get(`${this.apiBase}/fe-routes/mappings?roleId=${roleId}`, {
220
+ withCredentials: this.config?.withCredentials ?? true
221
+ });
222
+ }
223
+ startConnection(config) {
224
+ if (this.hubConnection)
225
+ return;
226
+ const signalrUrl = config.apiBaseUrl.replace(/\/$/, '') + '/hub/notification';
227
+ const builder = new HubConnectionBuilder()
228
+ .withUrl(signalrUrl, { withCredentials: config.withCredentials ?? true })
229
+ .withAutomaticReconnect()
230
+ .configureLogging(LogLevel.Warning);
231
+ this.hubConnection = builder.build();
232
+ this.hubConnection.on('ReceiveNotification', (n) => {
233
+ this._notifications.next(n);
234
+ });
235
+ this.hubConnection.start().then(() => { }).catch((err) => { });
236
+ this.hubConnection.onclose(() => { });
237
+ this.hubConnection.onreconnecting(() => { });
238
+ this.hubConnection.onreconnected(() => { });
239
+ }
240
+ stop() {
241
+ if (!this.hubConnection)
242
+ return;
243
+ this.hubConnection.stop().catch(() => { });
244
+ this.hubConnection = null;
245
+ }
246
+ logout() {
247
+ const url = `${this.apiBase}/auth/logout`;
248
+ return this.http.post(url, {}, { withCredentials: this.config?.withCredentials ?? true }).pipe(tap(() => {
249
+ this._currentUser.next(null);
250
+ this.stop();
251
+ }));
252
+ }
253
+ refreshUser() {
254
+ this.fetchCurrentUser();
255
+ }
256
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: MesAuthService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
257
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: MesAuthService });
258
+ }
259
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: MesAuthService, decorators: [{
260
+ type: Injectable
261
+ }], ctorParameters: () => [] });
179
262
 
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 }]; } });
263
+ class MesAuthInterceptor {
264
+ authService;
265
+ router;
266
+ constructor(authService, router) {
267
+ this.authService = authService;
268
+ this.router = router;
269
+ }
270
+ intercept(req, next) {
271
+ return next.handle(req).pipe(catchError((error) => {
272
+ if (error.status === 403) {
273
+ const config = this.authService.getConfig();
274
+ const baseUrl = config?.userBaseUrl || '';
275
+ // Use router URL for internal navigation (cleaner URLs)
276
+ // Falls back to window.location for full URL if needed
277
+ const currentUrl = this.router.url || window.location.pathname + window.location.search;
278
+ const returnUrl = encodeURIComponent(currentUrl);
279
+ window.location.href = `${baseUrl}/403?returnUrl=${returnUrl}`;
280
+ }
281
+ return throwError(error);
282
+ }));
283
+ }
284
+ 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 });
285
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: MesAuthInterceptor });
286
+ }
287
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: MesAuthInterceptor, decorators: [{
288
+ type: Injectable
289
+ }], ctorParameters: () => [{ type: MesAuthService }, { type: i2.Router }] });
202
290
 
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
- }]
291
+ class MesAuthModule {
292
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: MesAuthModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
293
+ static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.16", ngImport: i0, type: MesAuthModule });
294
+ static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: MesAuthModule, providers: [
295
+ MesAuthService,
296
+ { provide: HTTP_INTERCEPTORS, useClass: MesAuthInterceptor, multi: true }
297
+ ] });
298
+ }
299
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: MesAuthModule, decorators: [{
300
+ type: NgModule,
301
+ args: [{
302
+ providers: [
303
+ MesAuthService,
304
+ { provide: HTTP_INTERCEPTORS, useClass: MesAuthInterceptor, multi: true }
305
+ ]
306
+ }]
219
307
  }] });
220
308
 
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 []; } });
309
+ class ThemeService {
310
+ _currentTheme = new BehaviorSubject('light');
311
+ currentTheme$ = this._currentTheme.asObservable();
312
+ observer = null;
313
+ constructor() {
314
+ this.detectTheme();
315
+ this.startWatching();
316
+ }
317
+ ngOnDestroy() {
318
+ this.stopWatching();
319
+ }
320
+ detectTheme() {
321
+ const html = document.documentElement;
322
+ const isDark = html.classList.contains('dark') ||
323
+ html.getAttribute('data-theme') === 'dark' ||
324
+ html.getAttribute('theme') === 'dark' ||
325
+ html.getAttribute('data-coreui-theme') === 'dark';
326
+ this._currentTheme.next(isDark ? 'dark' : 'light');
327
+ }
328
+ startWatching() {
329
+ if (typeof MutationObserver === 'undefined') {
330
+ // Fallback for older browsers - check periodically
331
+ setInterval(() => this.detectTheme(), 1000);
332
+ return;
333
+ }
334
+ this.observer = new MutationObserver(() => {
335
+ this.detectTheme();
336
+ });
337
+ this.observer.observe(document.documentElement, {
338
+ attributes: true,
339
+ attributeFilter: ['class', 'data-theme', 'theme', 'data-coreui-theme']
340
+ });
341
+ }
342
+ stopWatching() {
343
+ if (this.observer) {
344
+ this.observer.disconnect();
345
+ this.observer = null;
346
+ }
347
+ }
348
+ get currentTheme() {
349
+ return this._currentTheme.value;
350
+ }
351
+ // Method to manually set theme if needed
352
+ setTheme(theme) {
353
+ this._currentTheme.next(theme);
354
+ }
355
+ // Re-detect theme from DOM
356
+ refreshTheme() {
357
+ this.detectTheme();
358
+ }
359
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: ThemeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
360
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: ThemeService, providedIn: 'root' });
361
+ }
362
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: ThemeService, decorators: [{
363
+ type: Injectable,
364
+ args: [{
365
+ providedIn: 'root'
366
+ }]
367
+ }], ctorParameters: () => [] });
280
368
 
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: `
369
+ class UserProfileComponent {
370
+ authService;
371
+ router;
372
+ themeService;
373
+ notificationClick = new EventEmitter();
374
+ get themeClass() {
375
+ return `theme-${this.currentTheme}`;
376
+ }
377
+ currentUser = null;
378
+ currentTheme = 'light';
379
+ unreadCount = 0;
380
+ dropdownOpen = false;
381
+ destroy$ = new Subject();
382
+ constructor(authService, router, themeService) {
383
+ this.authService = authService;
384
+ this.router = router;
385
+ this.themeService = themeService;
386
+ }
387
+ ngOnInit() {
388
+ this.authService.currentUser$
389
+ .pipe(takeUntil(this.destroy$))
390
+ .subscribe(user => {
391
+ this.currentUser = user;
392
+ });
393
+ this.themeService.currentTheme$
394
+ .pipe(takeUntil(this.destroy$))
395
+ .subscribe(theme => {
396
+ this.currentTheme = theme;
397
+ });
398
+ this.loadUnreadCount();
399
+ // Listen for new notifications
400
+ this.authService.notifications$
401
+ .pipe(takeUntil(this.destroy$))
402
+ .subscribe(() => {
403
+ console.log('Notification received, updating unread count');
404
+ this.loadUnreadCount();
405
+ });
406
+ }
407
+ ngOnDestroy() {
408
+ this.destroy$.next();
409
+ this.destroy$.complete();
410
+ }
411
+ loadUnreadCount() {
412
+ this.authService.getUnreadCount().subscribe({
413
+ next: (response) => {
414
+ this.unreadCount = response.unreadCount || 0;
415
+ },
416
+ error: (err) => { }
417
+ });
418
+ }
419
+ getAvatarUrl(user) {
420
+ const config = this.authService.getConfig();
421
+ const baseUrl = config?.apiBaseUrl || '';
422
+ // Use userId for the avatar endpoint
423
+ const userId = user.userId;
424
+ if (userId && baseUrl) {
425
+ return `${baseUrl.replace(/\/$/, '')}/auth/${userId}/avatar`;
426
+ }
427
+ // Fallback to UI avatars service if no userId or baseUrl
428
+ const displayName = user.userName || user.userId || 'User';
429
+ return `https://ui-avatars.com/api/?name=${encodeURIComponent(displayName)}&background=1976d2&color=fff`;
430
+ }
431
+ getLastNameInitial(user) {
432
+ const fullName = user.fullName || user.userName || 'U';
433
+ const parts = fullName.split(' ');
434
+ const lastPart = parts[parts.length - 1];
435
+ return lastPart.charAt(0).toUpperCase();
436
+ }
437
+ toggleDropdown() {
438
+ this.dropdownOpen = !this.dropdownOpen;
439
+ }
440
+ onDocumentClick(event) {
441
+ const target = event.target;
442
+ const clickedInside = target.closest('.user-menu-wrapper');
443
+ if (!clickedInside) {
444
+ this.dropdownOpen = false;
445
+ }
446
+ }
447
+ onLogin() {
448
+ const config = this.authService.getConfig();
449
+ const baseUrl = config?.userBaseUrl || '';
450
+ const returnUrl = encodeURIComponent(this.router.url);
451
+ window.location.href = `${baseUrl}/login?returnUrl=${returnUrl}`;
452
+ }
453
+ onViewProfile() {
454
+ const config = this.authService.getConfig();
455
+ const baseUrl = config?.userBaseUrl || '';
456
+ window.location.href = `${baseUrl}/profile`;
457
+ this.dropdownOpen = false;
458
+ }
459
+ onLogout() {
460
+ this.authService.logout().subscribe({
461
+ next: () => {
462
+ // Clear current user after successful logout
463
+ this.dropdownOpen = false;
464
+ // Navigate to login with return URL
465
+ const config = this.authService.getConfig();
466
+ const baseUrl = config?.userBaseUrl || '';
467
+ const returnUrl = encodeURIComponent(window.location.href);
468
+ window.location.href = `${baseUrl}/login?returnUrl=${returnUrl}`;
469
+ },
470
+ error: (err) => {
471
+ // Still navigate to login even if logout fails
472
+ const config = this.authService.getConfig();
473
+ const baseUrl = config?.userBaseUrl || '';
474
+ window.location.href = `${baseUrl}/login`;
475
+ }
476
+ });
477
+ }
478
+ onNotificationClick() {
479
+ this.notificationClick.emit();
480
+ }
481
+ 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 });
482
+ 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
483
  <div class="user-profile-container">
394
484
  <!-- Not logged in -->
395
485
  <ng-container *ngIf="!currentUser">
@@ -434,9 +524,10 @@ UserProfileComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", v
434
524
  </div>
435
525
  </ng-container>
436
526
  </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,
527
+ `, 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"] }] });
528
+ }
529
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: UserProfileComponent, decorators: [{
530
+ type: Component,
440
531
  args: [{ selector: 'ma-user-profile', standalone: true, imports: [NgIf], template: `
441
532
  <div class="user-profile-container">
442
533
  <!-- Not logged in -->
@@ -482,88 +573,87 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
482
573
  </div>
483
574
  </ng-container>
484
575
  </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']]
576
+ `, 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"] }]
577
+ }], ctorParameters: () => [{ type: MesAuthService }, { type: i2.Router }, { type: ThemeService }], propDecorators: { notificationClick: [{
578
+ type: Output
579
+ }], themeClass: [{
580
+ type: HostBinding,
581
+ args: ['class']
582
+ }], onDocumentClick: [{
583
+ type: HostListener,
584
+ args: ['document:click', ['$event']]
494
585
  }] } });
495
586
 
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' }]
587
+ class ToastService {
588
+ toasts$ = new BehaviorSubject([]);
589
+ toasts = this.toasts$.asObservable();
590
+ show(message, title, type = 'info', duration = 5000) {
591
+ const id = Math.random().toString(36).substr(2, 9);
592
+ const toast = {
593
+ id,
594
+ message,
595
+ title,
596
+ type,
597
+ duration
598
+ };
599
+ const currentToasts = this.toasts$.value;
600
+ this.toasts$.next([...currentToasts, toast]);
601
+ if (duration > 0) {
602
+ setTimeout(() => {
603
+ this.remove(id);
604
+ }, duration);
605
+ }
606
+ return id;
607
+ }
608
+ remove(id) {
609
+ const currentToasts = this.toasts$.value;
610
+ this.toasts$.next(currentToasts.filter(t => t.id !== id));
611
+ }
612
+ clear() {
613
+ this.toasts$.next([]);
614
+ }
615
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: ToastService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
616
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: ToastService, providedIn: 'root' });
617
+ }
618
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: ToastService, decorators: [{
619
+ type: Injectable,
620
+ args: [{ providedIn: 'root' }]
532
621
  }] });
533
622
 
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: `
623
+ class ToastContainerComponent {
624
+ toastService;
625
+ themeService;
626
+ get themeClass() {
627
+ return `theme-${this.currentTheme}`;
628
+ }
629
+ toasts = [];
630
+ currentTheme = 'light';
631
+ destroy$ = new Subject();
632
+ constructor(toastService, themeService) {
633
+ this.toastService = toastService;
634
+ this.themeService = themeService;
635
+ }
636
+ ngOnInit() {
637
+ this.toastService.toasts
638
+ .pipe(takeUntil(this.destroy$))
639
+ .subscribe(toasts => {
640
+ this.toasts = toasts;
641
+ });
642
+ this.themeService.currentTheme$
643
+ .pipe(takeUntil(this.destroy$))
644
+ .subscribe(theme => {
645
+ this.currentTheme = theme;
646
+ });
647
+ }
648
+ ngOnDestroy() {
649
+ this.destroy$.next();
650
+ this.destroy$.complete();
651
+ }
652
+ close(id) {
653
+ this.toastService.remove(id);
654
+ }
655
+ 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 });
656
+ 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
657
  <div class="toast-container">
568
658
  <div
569
659
  *ngFor="let toast of toasts"
@@ -580,9 +670,10 @@ ToastContainerComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0"
580
670
  </button>
581
671
  </div>
582
672
  </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,
673
+ `, 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"] }] });
674
+ }
675
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: ToastContainerComponent, decorators: [{
676
+ type: Component,
586
677
  args: [{ selector: 'ma-toast-container', standalone: true, imports: [CommonModule], template: `
587
678
  <div class="toast-container">
588
679
  <div
@@ -600,159 +691,161 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
600
691
  </button>
601
692
  </div>
602
693
  </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']
694
+ `, 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"] }]
695
+ }], ctorParameters: () => [{ type: ToastService }, { type: ThemeService }], propDecorators: { themeClass: [{
696
+ type: HostBinding,
697
+ args: ['class']
607
698
  }] } });
608
699
 
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: `
700
+ class NotificationPanelComponent {
701
+ authService;
702
+ toastService;
703
+ themeService;
704
+ notificationRead = new EventEmitter();
705
+ get themeClass() {
706
+ return `theme-${this.currentTheme}`;
707
+ }
708
+ isOpen = false;
709
+ notifications = [];
710
+ currentTheme = 'light';
711
+ activeTab = 'unread'; // Default to unread tab
712
+ destroy$ = new Subject();
713
+ get unreadNotifications() {
714
+ return this.notifications.filter(n => !n.isRead);
715
+ }
716
+ get readNotifications() {
717
+ return this.notifications.filter(n => n.isRead);
718
+ }
719
+ get currentNotifications() {
720
+ return this.activeTab === 'unread' ? this.unreadNotifications : this.readNotifications;
721
+ }
722
+ getNotificationMessage(notification) {
723
+ return notification.messageHtml || notification.message || '';
724
+ }
725
+ constructor(authService, toastService, themeService) {
726
+ this.authService = authService;
727
+ this.toastService = toastService;
728
+ this.themeService = themeService;
729
+ }
730
+ ngOnInit() {
731
+ this.themeService.currentTheme$
732
+ .pipe(takeUntil(this.destroy$))
733
+ .subscribe(theme => {
734
+ this.currentTheme = theme;
735
+ });
736
+ this.loadNotifications();
737
+ // Listen for new real-time notifications
738
+ this.authService.notifications$
739
+ .pipe(takeUntil(this.destroy$))
740
+ .subscribe((notification) => {
741
+ // Show toast for new notification
742
+ this.toastService.show(notification.messageHtml || notification.message || '', notification.title, 'info', 5000);
743
+ // Reload notifications list
744
+ this.loadNotifications();
745
+ });
746
+ }
747
+ ngOnDestroy() {
748
+ this.destroy$.next();
749
+ this.destroy$.complete();
750
+ }
751
+ loadNotifications() {
752
+ this.authService.getNotifications(1, 50, true).subscribe({
753
+ next: (response) => {
754
+ this.notifications = response.items || [];
755
+ },
756
+ error: (err) => { }
757
+ });
758
+ }
759
+ open() {
760
+ this.isOpen = true;
761
+ this.activeTab = 'unread'; // Reset to unread tab when opening
762
+ }
763
+ close() {
764
+ this.isOpen = false;
765
+ }
766
+ switchTab(tab) {
767
+ this.activeTab = tab;
768
+ }
769
+ markAsRead(notificationId, event) {
770
+ if (event) {
771
+ event.stopPropagation();
772
+ }
773
+ this.authService.markAsRead(notificationId).subscribe({
774
+ next: () => {
775
+ const notification = this.notifications.find(n => n.id === notificationId);
776
+ if (notification) {
777
+ notification.isRead = true;
778
+ this.notificationRead.emit();
779
+ }
780
+ },
781
+ error: (err) => { }
782
+ });
783
+ }
784
+ markAllAsRead() {
785
+ this.authService.markAllAsRead().subscribe({
786
+ next: () => {
787
+ this.notifications.forEach(n => n.isRead = true);
788
+ this.notificationRead.emit();
789
+ },
790
+ error: (err) => { }
791
+ });
792
+ }
793
+ deleteAllRead() {
794
+ const readNotificationIds = this.notifications
795
+ .filter(n => n.isRead)
796
+ .map(n => n.id);
797
+ // Delete all read notifications
798
+ const deletePromises = readNotificationIds.map(id => this.authService.deleteNotification(id).toPromise());
799
+ Promise.all(deletePromises).then(() => {
800
+ // Remove all read notifications from the local array
801
+ this.notifications = this.notifications.filter(n => !n.isRead);
802
+ }).catch((err) => {
803
+ // If bulk delete fails, reload notifications to get current state
804
+ this.loadNotifications();
805
+ });
806
+ }
807
+ deleteAllUnread() {
808
+ const unreadNotificationIds = this.notifications
809
+ .filter(n => !n.isRead)
810
+ .map(n => n.id);
811
+ // Delete all unread notifications
812
+ const deletePromises = unreadNotificationIds.map(id => this.authService.deleteNotification(id).toPromise());
813
+ Promise.all(deletePromises).then(() => {
814
+ // Remove all unread notifications from the local array
815
+ this.notifications = this.notifications.filter(n => n.isRead);
816
+ }).catch((err) => {
817
+ // If bulk delete fails, reload notifications to get current state
818
+ this.loadNotifications();
819
+ });
820
+ }
821
+ delete(notificationId, event) {
822
+ event.stopPropagation();
823
+ this.authService.deleteNotification(notificationId).subscribe({
824
+ next: () => {
825
+ this.notifications = this.notifications.filter(n => n.id !== notificationId);
826
+ },
827
+ error: (err) => { }
828
+ });
829
+ }
830
+ formatDate(dateString) {
831
+ const date = new Date(dateString);
832
+ const now = new Date();
833
+ const diffMs = now.getTime() - date.getTime();
834
+ const diffMins = Math.floor(diffMs / 60000);
835
+ const diffHours = Math.floor(diffMs / 3600000);
836
+ const diffDays = Math.floor(diffMs / 86400000);
837
+ if (diffMins < 1)
838
+ return 'Now';
839
+ if (diffMins < 60)
840
+ return `${diffMins}m ago`;
841
+ if (diffHours < 24)
842
+ return `${diffHours}h ago`;
843
+ if (diffDays < 7)
844
+ return `${diffDays}d ago`;
845
+ return date.toLocaleDateString();
846
+ }
847
+ 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 });
848
+ 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
849
  <div class="notification-panel" [class.open]="isOpen">
757
850
  <!-- Header -->
758
851
  <div class="panel-header">
@@ -836,9 +929,10 @@ NotificationPanelComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0
836
929
  </button>
837
930
  </div>
838
931
  </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,
932
+ `, 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"] }] });
933
+ }
934
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: NotificationPanelComponent, decorators: [{
935
+ type: Component,
842
936
  args: [{ selector: 'ma-notification-panel', standalone: true, imports: [NgIf, NgFor], template: `
843
937
  <div class="notification-panel" [class.open]="isOpen">
844
938
  <!-- Header -->
@@ -923,108 +1017,119 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
923
1017
  </button>
924
1018
  </div>
925
1019
  </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']
1020
+ `, 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"] }]
1021
+ }], ctorParameters: () => [{ type: MesAuthService }, { type: ToastService }, { type: ThemeService }], propDecorators: { notificationRead: [{
1022
+ type: Output
1023
+ }], themeClass: [{
1024
+ type: HostBinding,
1025
+ args: ['class']
932
1026
  }] } });
933
1027
 
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: `
1028
+ class MaUserComponent {
1029
+ userProfile;
1030
+ ngAfterViewInit() {
1031
+ // Ensure proper initialization
1032
+ if (this.userProfile) {
1033
+ this.userProfile.loadUnreadCount();
1034
+ }
1035
+ }
1036
+ onNotificationRead() {
1037
+ if (this.userProfile) {
1038
+ this.userProfile.loadUnreadCount();
1039
+ }
1040
+ }
1041
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: MaUserComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1042
+ 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
1043
  <ma-toast-container></ma-toast-container>
942
1044
  <div class="user-header">
943
1045
  <ma-user-profile (notificationClick)="notificationPanel.open()"></ma-user-profile>
944
1046
  </div>
945
1047
  <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,
1048
+ `, 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"] }] });
1049
+ }
1050
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: MaUserComponent, decorators: [{
1051
+ type: Component,
949
1052
  args: [{ selector: 'ma-user', standalone: true, imports: [ToastContainerComponent, UserProfileComponent, NotificationPanelComponent], template: `
950
1053
  <ma-toast-container></ma-toast-container>
951
1054
  <div class="user-header">
952
1055
  <ma-user-profile (notificationClick)="notificationPanel.open()"></ma-user-profile>
953
1056
  </div>
954
1057
  <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]
1058
+ `, styles: [".user-header{display:flex;justify-content:flex-end}\n"] }]
1059
+ }], propDecorators: { userProfile: [{
1060
+ type: ViewChild,
1061
+ args: [UserProfileComponent]
959
1062
  }] } });
960
1063
 
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: `
1064
+ class NotificationBadgeComponent {
1065
+ authService;
1066
+ themeService;
1067
+ notificationClick = new EventEmitter();
1068
+ get themeClass() {
1069
+ return `theme-${this.currentTheme}`;
1070
+ }
1071
+ unreadCount = 0;
1072
+ currentTheme = 'light';
1073
+ destroy$ = new Subject();
1074
+ constructor(authService, themeService) {
1075
+ this.authService = authService;
1076
+ this.themeService = themeService;
1077
+ }
1078
+ ngOnInit() {
1079
+ this.themeService.currentTheme$
1080
+ .pipe(takeUntil(this.destroy$))
1081
+ .subscribe(theme => {
1082
+ this.currentTheme = theme;
1083
+ });
1084
+ this.loadUnreadCount();
1085
+ // Listen for new notifications
1086
+ this.authService.notifications$
1087
+ .pipe(takeUntil(this.destroy$))
1088
+ .subscribe(() => {
1089
+ this.loadUnreadCount();
1090
+ });
1091
+ }
1092
+ ngOnDestroy() {
1093
+ this.destroy$.next();
1094
+ this.destroy$.complete();
1095
+ }
1096
+ loadUnreadCount() {
1097
+ this.authService.getUnreadCount().subscribe({
1098
+ next: (response) => {
1099
+ this.unreadCount = response.unreadCount || 0;
1100
+ },
1101
+ error: (err) => console.error('Error loading unread count:', err)
1102
+ });
1103
+ }
1104
+ onNotificationClick() {
1105
+ this.notificationClick.emit();
1106
+ }
1107
+ 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 });
1108
+ 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
1109
  <button class="notification-btn" (click)="onNotificationClick()" title="Notifications">
1006
1110
  <span class="icon">🔔</span>
1007
1111
  <span class="badge" *ngIf="unreadCount > 0">{{ unreadCount }}</span>
1008
1112
  </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,
1113
+ `, 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"] }] });
1114
+ }
1115
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: NotificationBadgeComponent, decorators: [{
1116
+ type: Component,
1012
1117
  args: [{ selector: 'ma-notification-badge', standalone: true, imports: [NgIf], template: `
1013
1118
  <button class="notification-btn" (click)="onNotificationClick()" title="Notifications">
1014
1119
  <span class="icon">🔔</span>
1015
1120
  <span class="badge" *ngIf="unreadCount > 0">{{ unreadCount }}</span>
1016
1121
  </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']
1122
+ `, 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"] }]
1123
+ }], ctorParameters: () => [{ type: MesAuthService }, { type: ThemeService }], propDecorators: { notificationClick: [{
1124
+ type: Output
1125
+ }], themeClass: [{
1126
+ type: HostBinding,
1127
+ args: ['class']
1023
1128
  }] } });
1024
1129
 
1025
- /**
1026
- * Generated bundle index. Do not edit.
1130
+ /**
1131
+ * Generated bundle index. Do not edit.
1027
1132
  */
1028
1133
 
1029
- export { MaUserComponent, MesAuthInterceptor, MesAuthModule, MesAuthService, NotificationBadgeComponent, NotificationPanelComponent, NotificationType, ThemeService, ToastContainerComponent, UserProfileComponent };
1134
+ export { MaUserComponent, MesAuthInterceptor, MesAuthModule, MesAuthService, NotificationBadgeComponent, NotificationPanelComponent, NotificationType, ThemeService, ToastContainerComponent, ToastService, UserProfileComponent };
1030
1135
  //# sourceMappingURL=mesauth-angular.mjs.map