mesauth-angular 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +6 -0
- package/dist/ma-user.component.d.ts +2 -0
- package/dist/ma-user.component.js +34 -0
- package/dist/mes-auth.service.d.ts +97 -0
- package/dist/mes-auth.service.js +111 -0
- package/dist/notification-badge.component.d.ts +13 -0
- package/dist/notification-badge.component.js +100 -0
- package/dist/notification-panel.component.d.ts +20 -0
- package/dist/notification-panel.component.js +327 -0
- package/dist/toast-container.component.d.ts +9 -0
- package/dist/toast-container.component.js +185 -0
- package/dist/toast.service.d.ts +15 -0
- package/dist/toast.service.js +43 -0
- package/dist/user-profile.component.d.ts +23 -0
- package/dist/user-profile.component.js +361 -0
- package/package.json +29 -0
package/README.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# mesauth-angular
|
|
2
|
+
|
|
3
|
+
Angular helper library to connect to a backend API and SignalR hub to surface the current logged-in user and incoming notifications.
|
|
4
|
+
|
|
5
|
+
Quick start
|
|
6
|
+
|
|
7
|
+
1. Install (from local folder during development):
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
cd /path/to/your/angular-app
|
|
11
|
+
npm install ../path/to/mesauth-angular
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
2. Initialize in your app (e.g. `AppComponent` constructor or `AppModule` bootstrap):
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
// in AppComponent
|
|
18
|
+
constructor(private mesAuth: MesAuthService) {
|
|
19
|
+
this.mesAuth.init({
|
|
20
|
+
signalrUrl: 'https://api.example.com/hubs/notify',
|
|
21
|
+
apiBaseUrl: 'https://api.example.com',
|
|
22
|
+
accessToken: () => localStorage.getItem('access_token')
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
this.mesAuth.currentUser$.subscribe(user => console.log('user', user));
|
|
26
|
+
this.mesAuth.notifications$.subscribe(n => console.log('notif', n));
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
3. Build the package for publishing:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
cd /path/to/mesauth-angular
|
|
34
|
+
npm install
|
|
35
|
+
npm run build
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Notes
|
|
39
|
+
- The service expects an endpoint `GET /api/user/current` that returns the current user.
|
|
40
|
+
- SignalR events used: `UserUpdated` and `NewNotification` (adjust to your backend).
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { Component } from '@angular/core';
|
|
8
|
+
import { CommonModule } from '@angular/common';
|
|
9
|
+
import { ToastContainerComponent } from './toast-container.component';
|
|
10
|
+
import { UserProfileComponent } from './user-profile.component';
|
|
11
|
+
import { NotificationPanelComponent } from './notification-panel.component';
|
|
12
|
+
let MaUserComponent = class MaUserComponent {
|
|
13
|
+
};
|
|
14
|
+
MaUserComponent = __decorate([
|
|
15
|
+
Component({
|
|
16
|
+
selector: 'ma-user',
|
|
17
|
+
standalone: true,
|
|
18
|
+
imports: [CommonModule, ToastContainerComponent, UserProfileComponent, NotificationPanelComponent],
|
|
19
|
+
template: `
|
|
20
|
+
<ma-toast-container></ma-toast-container>
|
|
21
|
+
<div class="user-header">
|
|
22
|
+
<ma-user-profile (notificationClick)="notificationPanel.open()"></ma-user-profile>
|
|
23
|
+
</div>
|
|
24
|
+
<ma-notification-panel #notificationPanel></ma-notification-panel>
|
|
25
|
+
`,
|
|
26
|
+
styles: [`
|
|
27
|
+
.user-header {
|
|
28
|
+
display: flex;
|
|
29
|
+
justify-content: flex-end;
|
|
30
|
+
}
|
|
31
|
+
`]
|
|
32
|
+
})
|
|
33
|
+
], MaUserComponent);
|
|
34
|
+
export { MaUserComponent };
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { HttpClient } from '@angular/common/http';
|
|
2
|
+
import { Observable } from 'rxjs';
|
|
3
|
+
export interface MesAuthConfig {
|
|
4
|
+
apiBaseUrl: string;
|
|
5
|
+
withCredentials?: boolean;
|
|
6
|
+
avatarUrl?: string;
|
|
7
|
+
userBaseUrl?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface IUser {
|
|
10
|
+
userId?: string;
|
|
11
|
+
userName?: string;
|
|
12
|
+
fullName?: string;
|
|
13
|
+
gender?: string;
|
|
14
|
+
email?: string;
|
|
15
|
+
phoneNumber?: string;
|
|
16
|
+
department?: string;
|
|
17
|
+
position?: string;
|
|
18
|
+
tokenVersion?: string;
|
|
19
|
+
permEndpoint?: string;
|
|
20
|
+
perms?: Set<string>;
|
|
21
|
+
employeeCode?: string;
|
|
22
|
+
hrFullNameVn?: string;
|
|
23
|
+
hrFullNameEn?: string;
|
|
24
|
+
hrPosition?: string;
|
|
25
|
+
hrJobTitle?: string;
|
|
26
|
+
hrGender?: string;
|
|
27
|
+
hrMobile?: string;
|
|
28
|
+
hrEmail?: string;
|
|
29
|
+
hrJoinDate?: string;
|
|
30
|
+
hrBirthDate?: string;
|
|
31
|
+
hrWorkStatus?: string;
|
|
32
|
+
hrDoiTuong?: string;
|
|
33
|
+
hrTeamCode?: string;
|
|
34
|
+
hrLineCode?: string;
|
|
35
|
+
}
|
|
36
|
+
export declare enum NotificationType {
|
|
37
|
+
Info = "Info",
|
|
38
|
+
Warning = "Warning",
|
|
39
|
+
Error = "Error",
|
|
40
|
+
Success = "Success"
|
|
41
|
+
}
|
|
42
|
+
export interface NotificationDto {
|
|
43
|
+
id: string;
|
|
44
|
+
title: string;
|
|
45
|
+
message: string;
|
|
46
|
+
messageHtml?: string;
|
|
47
|
+
url?: string;
|
|
48
|
+
type: NotificationType;
|
|
49
|
+
isRead: boolean;
|
|
50
|
+
createdAt: string;
|
|
51
|
+
sourceAppName: string;
|
|
52
|
+
sourceAppIconUrl?: string;
|
|
53
|
+
}
|
|
54
|
+
export interface PagedList<T> {
|
|
55
|
+
items: T[];
|
|
56
|
+
totalCount: number;
|
|
57
|
+
page: number;
|
|
58
|
+
pageSize: number;
|
|
59
|
+
totalPages: number;
|
|
60
|
+
hasNext: boolean;
|
|
61
|
+
hasPrevious: boolean;
|
|
62
|
+
}
|
|
63
|
+
export interface RealTimeNotificationDto {
|
|
64
|
+
id: string;
|
|
65
|
+
title: string;
|
|
66
|
+
message: string;
|
|
67
|
+
messageHtml?: string;
|
|
68
|
+
url?: string;
|
|
69
|
+
type: NotificationType;
|
|
70
|
+
createdAt: string;
|
|
71
|
+
sourceAppName: string;
|
|
72
|
+
sourceAppIconUrl?: string;
|
|
73
|
+
}
|
|
74
|
+
export declare class MesAuthService {
|
|
75
|
+
private http;
|
|
76
|
+
private hubConnection;
|
|
77
|
+
private _currentUser;
|
|
78
|
+
currentUser$: Observable<IUser | null>;
|
|
79
|
+
private _notifications;
|
|
80
|
+
notifications$: Observable<any>;
|
|
81
|
+
private apiBase;
|
|
82
|
+
private config;
|
|
83
|
+
constructor(http: HttpClient);
|
|
84
|
+
init(config: MesAuthConfig): void;
|
|
85
|
+
getConfig(): MesAuthConfig | null;
|
|
86
|
+
private fetchCurrentUser;
|
|
87
|
+
private fetchInitialNotifications;
|
|
88
|
+
getUnreadCount(): Observable<any>;
|
|
89
|
+
getNotifications(page?: number, pageSize?: number, includeRead?: boolean, type?: string): Observable<any>;
|
|
90
|
+
markAsRead(notificationId: string): Observable<any>;
|
|
91
|
+
markAllAsRead(): Observable<any>;
|
|
92
|
+
deleteNotification(notificationId: string): Observable<any>;
|
|
93
|
+
private startConnection;
|
|
94
|
+
stop(): void;
|
|
95
|
+
logout(): Observable<any>;
|
|
96
|
+
refreshUser(): void;
|
|
97
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
import { Injectable } from '@angular/core';
|
|
11
|
+
import { HttpClient } from '@angular/common/http';
|
|
12
|
+
import { HubConnectionBuilder, LogLevel } from '@microsoft/signalr';
|
|
13
|
+
import { BehaviorSubject, Subject } from 'rxjs';
|
|
14
|
+
export var NotificationType;
|
|
15
|
+
(function (NotificationType) {
|
|
16
|
+
NotificationType["Info"] = "Info";
|
|
17
|
+
NotificationType["Warning"] = "Warning";
|
|
18
|
+
NotificationType["Error"] = "Error";
|
|
19
|
+
NotificationType["Success"] = "Success";
|
|
20
|
+
})(NotificationType || (NotificationType = {}));
|
|
21
|
+
let MesAuthService = class MesAuthService {
|
|
22
|
+
constructor(http) {
|
|
23
|
+
this.http = http;
|
|
24
|
+
this.hubConnection = null;
|
|
25
|
+
this._currentUser = new BehaviorSubject(null);
|
|
26
|
+
this.currentUser$ = this._currentUser.asObservable();
|
|
27
|
+
this._notifications = new Subject();
|
|
28
|
+
this.notifications$ = this._notifications.asObservable();
|
|
29
|
+
this.apiBase = '';
|
|
30
|
+
this.config = null;
|
|
31
|
+
}
|
|
32
|
+
init(config) {
|
|
33
|
+
this.config = config;
|
|
34
|
+
this.apiBase = config.apiBaseUrl.replace(/\/$/, '');
|
|
35
|
+
this.fetchCurrentUser();
|
|
36
|
+
this.fetchInitialNotifications();
|
|
37
|
+
this.startConnection(config);
|
|
38
|
+
}
|
|
39
|
+
getConfig() {
|
|
40
|
+
return this.config;
|
|
41
|
+
}
|
|
42
|
+
fetchCurrentUser() {
|
|
43
|
+
if (!this.apiBase)
|
|
44
|
+
return;
|
|
45
|
+
this.http.get(`${this.apiBase}/auth/me`).subscribe({
|
|
46
|
+
next: (u) => this._currentUser.next(u),
|
|
47
|
+
error: (err) => console.error('fetchCurrentUser error', err)
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
fetchInitialNotifications() {
|
|
51
|
+
if (!this.apiBase)
|
|
52
|
+
return;
|
|
53
|
+
this.http.get(`${this.apiBase}/notif/me`).subscribe({
|
|
54
|
+
next: (notifications) => {
|
|
55
|
+
if (Array.isArray(notifications === null || notifications === void 0 ? void 0 : notifications.items)) {
|
|
56
|
+
notifications.items.forEach((n) => this._notifications.next(n));
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
error: (err) => console.error('fetchInitialNotifications error', err)
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
getUnreadCount() {
|
|
63
|
+
return this.http.get(`${this.apiBase}/notif/me/unread-count`);
|
|
64
|
+
}
|
|
65
|
+
getNotifications(page = 1, pageSize = 20, includeRead = false, type) {
|
|
66
|
+
let url = `${this.apiBase}/notif/me?page=${page}&pageSize=${pageSize}&includeRead=${includeRead}`;
|
|
67
|
+
if (type) {
|
|
68
|
+
url += `&type=${type}`;
|
|
69
|
+
}
|
|
70
|
+
return this.http.get(url);
|
|
71
|
+
}
|
|
72
|
+
markAsRead(notificationId) {
|
|
73
|
+
return this.http.patch(`${this.apiBase}/notif/${notificationId}/read`, {});
|
|
74
|
+
}
|
|
75
|
+
markAllAsRead() {
|
|
76
|
+
return this.http.patch(`${this.apiBase}/notif/me/read-all`, {});
|
|
77
|
+
}
|
|
78
|
+
deleteNotification(notificationId) {
|
|
79
|
+
return this.http.delete(`${this.apiBase}/notif/${notificationId}`);
|
|
80
|
+
}
|
|
81
|
+
startConnection(config) {
|
|
82
|
+
var _a;
|
|
83
|
+
if (this.hubConnection)
|
|
84
|
+
return;
|
|
85
|
+
const signalrUrl = config.apiBaseUrl.replace(/\/$/, '') + '/hub/notification';
|
|
86
|
+
const builder = new HubConnectionBuilder()
|
|
87
|
+
.withUrl(signalrUrl, { withCredentials: (_a = config.withCredentials) !== null && _a !== void 0 ? _a : true })
|
|
88
|
+
.withAutomaticReconnect()
|
|
89
|
+
.configureLogging(LogLevel.Warning);
|
|
90
|
+
this.hubConnection = builder.build();
|
|
91
|
+
this.hubConnection.on('ReceiveNotification', (n) => this._notifications.next(n));
|
|
92
|
+
this.hubConnection.start().catch((err) => console.error('SignalR start error', err));
|
|
93
|
+
}
|
|
94
|
+
stop() {
|
|
95
|
+
if (!this.hubConnection)
|
|
96
|
+
return;
|
|
97
|
+
this.hubConnection.stop().catch(() => { });
|
|
98
|
+
this.hubConnection = null;
|
|
99
|
+
}
|
|
100
|
+
logout() {
|
|
101
|
+
return this.http.post(`${this.apiBase}/auth/logout`, {});
|
|
102
|
+
}
|
|
103
|
+
refreshUser() {
|
|
104
|
+
this.fetchCurrentUser();
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
MesAuthService = __decorate([
|
|
108
|
+
Injectable({ providedIn: 'root' }),
|
|
109
|
+
__metadata("design:paramtypes", [HttpClient])
|
|
110
|
+
], MesAuthService);
|
|
111
|
+
export { MesAuthService };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { OnInit, OnDestroy, EventEmitter } from '@angular/core';
|
|
2
|
+
import { MesAuthService } from './mes-auth.service';
|
|
3
|
+
export declare class NotificationBadgeComponent implements OnInit, OnDestroy {
|
|
4
|
+
private authService;
|
|
5
|
+
notificationClick: EventEmitter<void>;
|
|
6
|
+
unreadCount: number;
|
|
7
|
+
private destroy$;
|
|
8
|
+
constructor(authService: MesAuthService);
|
|
9
|
+
ngOnInit(): void;
|
|
10
|
+
ngOnDestroy(): void;
|
|
11
|
+
private loadUnreadCount;
|
|
12
|
+
onNotificationClick(): void;
|
|
13
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
import { Component, Output, EventEmitter } from '@angular/core';
|
|
11
|
+
import { CommonModule } from '@angular/common';
|
|
12
|
+
import { MesAuthService } from './mes-auth.service';
|
|
13
|
+
import { Subject } from 'rxjs';
|
|
14
|
+
import { takeUntil } from 'rxjs/operators';
|
|
15
|
+
let NotificationBadgeComponent = class NotificationBadgeComponent {
|
|
16
|
+
constructor(authService) {
|
|
17
|
+
this.authService = authService;
|
|
18
|
+
this.notificationClick = new EventEmitter();
|
|
19
|
+
this.unreadCount = 0;
|
|
20
|
+
this.destroy$ = new Subject();
|
|
21
|
+
}
|
|
22
|
+
ngOnInit() {
|
|
23
|
+
this.loadUnreadCount();
|
|
24
|
+
// Listen for new notifications
|
|
25
|
+
this.authService.notifications$
|
|
26
|
+
.pipe(takeUntil(this.destroy$))
|
|
27
|
+
.subscribe(() => {
|
|
28
|
+
this.loadUnreadCount();
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
ngOnDestroy() {
|
|
32
|
+
this.destroy$.next();
|
|
33
|
+
this.destroy$.complete();
|
|
34
|
+
}
|
|
35
|
+
loadUnreadCount() {
|
|
36
|
+
this.authService.getUnreadCount().subscribe({
|
|
37
|
+
next: (response) => {
|
|
38
|
+
this.unreadCount = response.unreadCount || 0;
|
|
39
|
+
},
|
|
40
|
+
error: (err) => console.error('Error loading unread count:', err)
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
onNotificationClick() {
|
|
44
|
+
this.notificationClick.emit();
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
__decorate([
|
|
48
|
+
Output(),
|
|
49
|
+
__metadata("design:type", Object)
|
|
50
|
+
], NotificationBadgeComponent.prototype, "notificationClick", void 0);
|
|
51
|
+
NotificationBadgeComponent = __decorate([
|
|
52
|
+
Component({
|
|
53
|
+
selector: 'ma-notification-badge',
|
|
54
|
+
standalone: true,
|
|
55
|
+
imports: [CommonModule],
|
|
56
|
+
template: `
|
|
57
|
+
<button class="notification-btn" (click)="onNotificationClick()" title="Notifications">
|
|
58
|
+
<span class="icon">🔔</span>
|
|
59
|
+
<span class="badge" *ngIf="unreadCount > 0">{{ unreadCount }}</span>
|
|
60
|
+
</button>
|
|
61
|
+
`,
|
|
62
|
+
styles: [`
|
|
63
|
+
.notification-btn {
|
|
64
|
+
position: relative;
|
|
65
|
+
background: none;
|
|
66
|
+
border: none;
|
|
67
|
+
font-size: 24px;
|
|
68
|
+
cursor: pointer;
|
|
69
|
+
padding: 8px;
|
|
70
|
+
transition: opacity 0.2s;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.notification-btn:hover {
|
|
74
|
+
opacity: 0.7;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.icon {
|
|
78
|
+
display: inline-block;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.badge {
|
|
82
|
+
position: absolute;
|
|
83
|
+
top: 0;
|
|
84
|
+
right: 0;
|
|
85
|
+
background-color: #f44336;
|
|
86
|
+
color: white;
|
|
87
|
+
border-radius: 50%;
|
|
88
|
+
width: 20px;
|
|
89
|
+
height: 20px;
|
|
90
|
+
display: flex;
|
|
91
|
+
align-items: center;
|
|
92
|
+
justify-content: center;
|
|
93
|
+
font-size: 12px;
|
|
94
|
+
font-weight: bold;
|
|
95
|
+
}
|
|
96
|
+
`]
|
|
97
|
+
}),
|
|
98
|
+
__metadata("design:paramtypes", [MesAuthService])
|
|
99
|
+
], NotificationBadgeComponent);
|
|
100
|
+
export { NotificationBadgeComponent };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { OnInit, OnDestroy } from '@angular/core';
|
|
2
|
+
import { MesAuthService, NotificationDto } from './mes-auth.service';
|
|
3
|
+
import { ToastService } from './toast.service';
|
|
4
|
+
export declare class NotificationPanelComponent implements OnInit, OnDestroy {
|
|
5
|
+
private authService;
|
|
6
|
+
private toastService;
|
|
7
|
+
isOpen: boolean;
|
|
8
|
+
notifications: NotificationDto[];
|
|
9
|
+
private destroy$;
|
|
10
|
+
constructor(authService: MesAuthService, toastService: ToastService);
|
|
11
|
+
ngOnInit(): void;
|
|
12
|
+
ngOnDestroy(): void;
|
|
13
|
+
private loadNotifications;
|
|
14
|
+
open(): void;
|
|
15
|
+
close(): void;
|
|
16
|
+
markAsRead(notificationId: string): void;
|
|
17
|
+
markAllAsRead(): void;
|
|
18
|
+
delete(notificationId: string, event: Event): void;
|
|
19
|
+
formatDate(dateString: string): string;
|
|
20
|
+
}
|