aril 1.2.36 → 1.2.38

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 (43) hide show
  1. package/boot/config/api/src/api.service.d.ts +2 -1
  2. package/esm2022/boot/config/api/src/api.service.mjs +24 -7
  3. package/esm2022/boot/host/src/bootstrap.mjs +13 -4
  4. package/esm2022/permission/aril-permission.mjs +5 -0
  5. package/esm2022/permission/index.mjs +4 -0
  6. package/esm2022/permission/src/constants.mjs +4 -0
  7. package/esm2022/permission/src/has-permission.directive.mjs +49 -0
  8. package/esm2022/permission/src/permission.service.mjs +57 -0
  9. package/esm2022/provider/src/index.mjs +2 -1
  10. package/esm2022/provider/src/prodiveHost.mjs +3 -2
  11. package/esm2022/provider/src/provideHostPermissions.mjs +43 -0
  12. package/esm2022/theme/layout/app/notifications/notifications-sidebar.component.mjs +146 -49
  13. package/esm2022/theme/layout/service/interfaces/interfaces.mjs +1 -1
  14. package/esm2022/theme/layout/service/notifications.service.mjs +8 -2
  15. package/fesm2022/{aril-app.component-W3wwVSp1.mjs → aril-app.component-BUjucZ0J.mjs} +2 -1
  16. package/fesm2022/{aril-app.component-W3wwVSp1.mjs.map → aril-app.component-BUjucZ0J.mjs.map} +1 -1
  17. package/fesm2022/aril-boot-config-api.mjs +23 -6
  18. package/fesm2022/aril-boot-config-api.mjs.map +1 -1
  19. package/fesm2022/aril-boot-host.mjs +12 -3
  20. package/fesm2022/aril-boot-host.mjs.map +1 -1
  21. package/fesm2022/aril-permission.mjs +114 -0
  22. package/fesm2022/aril-permission.mjs.map +1 -0
  23. package/fesm2022/aril-provider.mjs +44 -2
  24. package/fesm2022/aril-provider.mjs.map +1 -1
  25. package/fesm2022/aril-theme-layout.mjs +145 -39
  26. package/fesm2022/aril-theme-layout.mjs.map +1 -1
  27. package/fesm2022/aril.mjs +13 -4
  28. package/fesm2022/aril.mjs.map +1 -1
  29. package/package.json +87 -81
  30. package/permission/index.d.ts +3 -0
  31. package/permission/src/constants.d.ts +1 -0
  32. package/permission/src/has-permission.directive.d.ts +16 -0
  33. package/permission/src/permission.service.d.ts +15 -0
  34. package/provider/src/index.d.ts +1 -0
  35. package/provider/src/provideHostPermissions.d.ts +6 -0
  36. package/theme/layout/app/notifications/notifications-sidebar.component.d.ts +2288 -2267
  37. package/theme/layout/app/notifications/notifications-sidebar.component.html +66 -68
  38. package/theme/layout/app/notifications/notifications-sidebar.component.scss +234 -120
  39. package/theme/layout/app/notifications/notifications-sidebar.component.ts +182 -56
  40. package/theme/layout/service/interfaces/interfaces.d.ts +5 -1
  41. package/theme/layout/service/interfaces/interfaces.ts +6 -1
  42. package/theme/layout/service/notifications.service.d.ts +1 -0
  43. package/theme/layout/service/notifications.service.ts +5 -0
@@ -1,21 +1,22 @@
1
1
  import { CommonModule } from '@angular/common';
2
- import { Component, OnInit, effect, signal } from '@angular/core';
2
+ import { Component, OnInit, computed, effect, signal, untracked } from '@angular/core';
3
3
  import { toSignal } from '@angular/core/rxjs-interop';
4
+ import { FormsModule } from '@angular/forms';
4
5
  import { Router } from '@angular/router';
5
6
 
6
- import { BadgeModule } from 'primeng/badge';
7
- import { ConfirmDialogModule } from 'primeng/confirmdialog';
8
- import { PaginatorModule } from 'primeng/paginator';
9
- import { ScrollPanelModule } from 'primeng/scrollpanel';
7
+ import { MenuItem } from 'primeng/api';
8
+ import { Menu, MenuModule } from 'primeng/menu';
10
9
  import { SidebarModule } from 'primeng/sidebar';
11
10
  import { TooltipModule } from 'primeng/tooltip';
12
11
 
12
+ import { IconProp } from '@fortawesome/fontawesome-svg-core';
13
13
  import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
14
14
  import { TranslocoModule, TranslocoService } from '@ngneat/transloco';
15
15
  import { KeycloakService } from 'keycloak-angular';
16
- import { Subject, switchMap } from 'rxjs';
16
+ import { map, mergeMap, Subject, switchMap } from 'rxjs';
17
17
 
18
18
  import { ButtonComponent } from 'aril/ui/button';
19
+ import { SwitchComponent } from 'aril/ui/switch';
19
20
  import { regularIcons, solidIcons } from 'aril/util/lib';
20
21
  import { SafePipe } from 'aril/util/pipes';
21
22
 
@@ -29,14 +30,13 @@ import { NotificationsService } from '../../service/notifications.service';
29
30
  selector: 'app-notifications-sidebar',
30
31
  imports: [
31
32
  CommonModule,
33
+ FormsModule,
32
34
  SidebarModule,
33
- ScrollPanelModule,
34
- ConfirmDialogModule,
35
- PaginatorModule,
35
+ MenuModule,
36
36
  ButtonComponent,
37
+ SwitchComponent,
37
38
  TranslocoModule,
38
39
  TooltipModule,
39
- BadgeModule,
40
40
  FontAwesomeModule,
41
41
  SafePipe
42
42
  ],
@@ -44,8 +44,16 @@ import { NotificationsService } from '../../service/notifications.service';
44
44
  styleUrls: ['./notifications-sidebar.component.scss']
45
45
  })
46
46
  export class NotificationsSidebarComponent implements OnInit {
47
+ private readonly pageSize = 10;
48
+
47
49
  notifications = signal<SmsEmailAuditResponseDTO[]>([]);
48
- selectedNotification: SmsEmailAuditResponseDTO | null = null;
50
+ totalRecords = signal(0);
51
+ onlyUnread = signal(true);
52
+ pageNumber = signal(1);
53
+
54
+ remainingCount = computed(() => Math.max(this.totalRecords() - this.notifications().length, 0));
55
+
56
+ menuItems: MenuItem[] = [];
49
57
 
50
58
  ActivityTypesEnum = ActivityTypesEnum;
51
59
  DetailNotificationTypeEnum = DetailNotificationTypeEnum;
@@ -57,11 +65,42 @@ export class NotificationsSidebarComponent implements OnInit {
57
65
 
58
66
  private subjects = {
59
67
  markAsRead: new Subject<string>(),
68
+ markAllAsRead: new Subject<void>(),
60
69
  loadNotifications: new Subject<void>()
61
70
  };
62
71
 
63
72
  markAsReadService = toSignal(
64
- this.subjects.markAsRead.pipe(switchMap((id) => this.notificationsService.markAsReadNotification(id)))
73
+ this.subjects.markAsRead.pipe(
74
+ mergeMap((id) => this.notificationsService.markAsReadNotification(id).pipe(map((result) => ({ result, id }))))
75
+ )
76
+ );
77
+
78
+ markAllAsReadService = toSignal(
79
+ this.subjects.markAllAsRead.pipe(
80
+ switchMap(() =>
81
+ this.notificationsService.markAllAsReadNotification({
82
+ toList: [this.getUserMail, ...this.getUserPhones],
83
+ endDate: new Date().dateToLong('time') || 0
84
+ })
85
+ )
86
+ )
87
+ );
88
+
89
+ loadNotificationsService = toSignal(
90
+ this.subjects.loadNotifications.pipe(
91
+ switchMap(() =>
92
+ this.notificationsService.getNotifications({
93
+ pager: {
94
+ pageSize: this.pageSize,
95
+ pageNumber: this.pageNumber()
96
+ },
97
+ startDate: this.oneMonthAgo.dateToLong('time') || 0,
98
+ endDate: new Date().dateToLong('time') || 0,
99
+ ...(this.onlyUnread() ? { read: false } : {}),
100
+ toList: [this.getUserMail, ...this.getUserPhones]
101
+ })
102
+ )
103
+ )
65
104
  );
66
105
 
67
106
  get getUserPhones(): string[] {
@@ -78,22 +117,13 @@ export class NotificationsSidebarComponent implements OnInit {
78
117
  return this.keycloak.getKeycloakInstance().profile?.email as string;
79
118
  }
80
119
 
81
- loadNotificationsService = toSignal(
82
- this.subjects.loadNotifications.pipe(
83
- switchMap(() =>
84
- this.notificationsService.getNotifications({
85
- pager: {
86
- pageSize: 10,
87
- pageNumber: 1
88
- },
89
- startDate: this.oneMonthAgo.dateToLong('time') || 0,
90
- endDate: new Date().dateToLong('time') || 0,
91
- read: false,
92
- toList: [this.getUserMail, ...this.getUserPhones],
93
- })
94
- )
95
- )
96
- );
120
+ get unreadCount(): number {
121
+ return this.layoutService.getUnreadNotificationCount();
122
+ }
123
+
124
+ get loading(): boolean {
125
+ return !!this.notificationsService.states.getNotifications?.waitingForResponse;
126
+ }
97
127
 
98
128
  constructor(
99
129
  public layoutService: LayoutService,
@@ -106,10 +136,6 @@ export class NotificationsSidebarComponent implements OnInit {
106
136
  }
107
137
 
108
138
  ngOnInit(): void {
109
- this.loadNotifications();
110
- }
111
-
112
- private loadNotifications(): void {
113
139
  this.subjects.loadNotifications.next();
114
140
  }
115
141
 
@@ -118,23 +144,57 @@ export class NotificationsSidebarComponent implements OnInit {
118
144
  () => {
119
145
  const service = this.loadNotificationsService();
120
146
  if (service?.response) {
121
- this.notifications.set(service.response.result);
122
- // Update unread count in layoutService
123
- this.layoutService.setUnreadNotificationCount(service?.response.resultInfo.totalItem || 0);
147
+ const result = service.response.result || [];
148
+ const isFirstPage = untracked(this.pageNumber) === 1;
149
+ this.notifications.update((current) =>
150
+ isFirstPage ? result : [...current, ...result.filter((item) => !current.some((existing) => existing.id === item.id))]
151
+ );
152
+ this.totalRecords.set(service.response.resultInfo.totalItem || 0);
153
+ if (untracked(this.onlyUnread)) {
154
+ this.layoutService.setUnreadNotificationCount(service.response.resultInfo.totalItem || 0);
155
+ }
124
156
  }
125
157
  },
126
158
  { allowSignalWrites: true }
127
159
  );
128
- effect(() => {
129
- const service = this.markAsReadService();
130
- if (service?.response) {
131
- this.loadNotifications();
132
-
133
- if (this.selectedNotification?.detailNotificationType === DetailNotificationTypeEnum.TODO_ITEM) {
134
- this.router.navigate([`mng/todo-tasks/detail/${this.selectedNotification?.referenceId}`]);
160
+ effect(
161
+ () => {
162
+ const marked = this.markAsReadService();
163
+ if (marked?.result?.response) {
164
+ this.applyMarkedAsRead(marked.id);
135
165
  }
136
- }
137
- });
166
+ },
167
+ { allowSignalWrites: true }
168
+ );
169
+ effect(
170
+ () => {
171
+ const service = this.markAllAsReadService();
172
+ if (service?.response) {
173
+ this.applyAllMarkedAsRead();
174
+ }
175
+ },
176
+ { allowSignalWrites: true }
177
+ );
178
+ }
179
+
180
+ private applyMarkedAsRead(id: string): void {
181
+ if (untracked(this.onlyUnread)) {
182
+ this.notifications.update((items) => items.filter((item) => item.id !== id));
183
+ this.totalRecords.update((total) => Math.max(total - 1, 0));
184
+ } else {
185
+ this.notifications.update((items) => items.map((item) => (item.id === id ? { ...item, read: true } : item)));
186
+ }
187
+ this.layoutService.setUnreadNotificationCount(Math.max(this.layoutService.getUnreadNotificationCount() - 1, 0));
188
+ }
189
+
190
+ private applyAllMarkedAsRead(): void {
191
+ if (untracked(this.onlyUnread)) {
192
+ this.notifications.set([]);
193
+ this.totalRecords.set(0);
194
+ } else {
195
+ this.notifications.update((items) => items.map((item) => (item.read ? item : { ...item, read: true })));
196
+ }
197
+ this.layoutService.setUnreadNotificationCount(0);
138
198
  }
139
199
 
140
200
  get visible(): boolean {
@@ -145,26 +205,92 @@ export class NotificationsSidebarComponent implements OnInit {
145
205
  this.layoutService.state.notificationsSidebarVisible = value;
146
206
  }
147
207
 
148
- markNotificationAsRead(notification: SmsEmailAuditResponseDTO): void {
149
- this.selectedNotification = notification;
150
- this.subjects.markAsRead.next(notification.id);
151
- this.visible = false;
208
+ onOnlyUnreadChange(value: boolean): void {
209
+ this.onlyUnread.set(value);
210
+ this.pageNumber.set(1);
211
+ this.notifications.set([]);
212
+ this.subjects.loadNotifications.next();
213
+ }
152
214
 
153
- if (notification?.detailNotificationType === DetailNotificationTypeEnum.TODO_ITEM) {
154
- this.router.navigate([`mng/todo-tasks/detail/${notification?.referenceId}`]);
155
- } else if (notification?.detailNotificationType === DetailNotificationTypeEnum.BATCH_JOB) {
156
- this.router.navigate([`mng/bulk-operations/${notification?.referenceId}`]);
157
- }
215
+ onSidebarShow(): void {
216
+ this.pageNumber.set(1);
217
+ this.subjects.loadNotifications.next();
218
+ }
219
+
220
+ showMore(): void {
221
+ this.pageNumber.set(Math.floor(this.notifications().length / this.pageSize) + 1);
222
+ this.subjects.loadNotifications.next();
223
+ }
224
+
225
+ toggleMenu(event: Event, menu: Menu): void {
226
+ this.menuItems = [
227
+ ...(this.unreadCount > 0 ?
228
+ [
229
+ {
230
+ label: this.translocoService.translate('notifications.markAllAsRead'),
231
+ icon: 'pi pi-check-circle',
232
+ command: () => this.subjects.markAllAsRead.next()
233
+ }
234
+ ]
235
+ : []),
236
+ {
237
+ label: this.translocoService.translate('notifications.allNotificationsPage'),
238
+ icon: 'pi pi-external-link',
239
+ command: () => this.navigateToAllNotifications()
240
+ }
241
+ ];
242
+ menu.toggle(event);
158
243
  }
159
244
 
160
- markNotificationAsReadOnDotIcon(notification: SmsEmailAuditResponseDTO): void {
245
+ markNotificationAsRead(notification: SmsEmailAuditResponseDTO): void {
161
246
  if (!notification.read) {
162
247
  this.subjects.markAsRead.next(notification.id);
163
248
  }
164
249
  }
165
250
 
166
- trackByNotificationId(index: number, notification: SmsEmailAuditResponseDTO): string {
167
- return notification.id;
251
+ openNotification(notification: SmsEmailAuditResponseDTO): void {
252
+ const isTodo = notification.detailNotificationType === DetailNotificationTypeEnum.TODO_ITEM;
253
+ const isBatch = notification.detailNotificationType === DetailNotificationTypeEnum.BATCH_JOB;
254
+ if (!isTodo && !isBatch) {
255
+ return;
256
+ }
257
+ this.markNotificationAsRead(notification);
258
+ this.visible = false;
259
+ const route = isTodo ? `mng/todo-tasks/detail/${notification.referenceId}` : `mng/bulk-operations/${notification.referenceId}`;
260
+ this.router.navigate([route]);
261
+ }
262
+
263
+ isNavigable(notification: SmsEmailAuditResponseDTO): boolean {
264
+ return (
265
+ notification.detailNotificationType === DetailNotificationTypeEnum.TODO_ITEM ||
266
+ notification.detailNotificationType === DetailNotificationTypeEnum.BATCH_JOB
267
+ );
268
+ }
269
+
270
+ getNotificationIcon(notification: SmsEmailAuditResponseDTO): IconProp {
271
+ if (notification.detailNotificationType === DetailNotificationTypeEnum.TODO_ITEM) {
272
+ return solidIcons.faListCheck;
273
+ }
274
+ if (notification.detailNotificationType === DetailNotificationTypeEnum.BATCH_JOB) {
275
+ return solidIcons.faGear;
276
+ }
277
+ if (notification.type === ActivityTypesEnum.SMS) {
278
+ return regularIcons.faMessage;
279
+ }
280
+ return notification.read ? regularIcons.faEnvelopeOpen : regularIcons.faEnvelope;
281
+ }
282
+
283
+ getNotificationIconClass(notification: SmsEmailAuditResponseDTO): string {
284
+ if (notification.detailNotificationType === DetailNotificationTypeEnum.TODO_ITEM) {
285
+ return 'todo';
286
+ }
287
+ if (notification.detailNotificationType === DetailNotificationTypeEnum.BATCH_JOB) {
288
+ return 'batch';
289
+ }
290
+ if (notification.type === ActivityTypesEnum.SMS) {
291
+ return 'sms';
292
+ }
293
+ return notification.read ? 'email-read' : 'email-unread';
168
294
  }
169
295
 
170
296
  getNotificationTitle(notification: SmsEmailAuditResponseDTO): string {
@@ -88,7 +88,7 @@ export interface SmsEmailAuditRequestDto {
88
88
  refMdId?: number;
89
89
  referenceId?: string;
90
90
  traceId?: string;
91
- read: boolean;
91
+ read?: boolean;
92
92
  pager?: Pager;
93
93
  username?: string;
94
94
  startReadTime?: number;
@@ -116,6 +116,10 @@ export interface SmsEmailAuditResponseDTO {
116
116
  traceId: string;
117
117
  detailNotificationType: string;
118
118
  }
119
+ export interface MarkAllReadNotificationRequestDTO {
120
+ toList: string[];
121
+ endDate: number;
122
+ }
119
123
  export interface Pager {
120
124
  pageSize: number;
121
125
  pageNumber: number;
@@ -105,7 +105,7 @@ export interface SmsEmailAuditRequestDto {
105
105
  refMdId?: number;
106
106
  referenceId?: string;
107
107
  traceId?: string;
108
- read: boolean;
108
+ read?: boolean;
109
109
  pager?: Pager;
110
110
  username?: string;
111
111
  startReadTime?: number;
@@ -135,6 +135,11 @@ export interface SmsEmailAuditResponseDTO {
135
135
  detailNotificationType: string;
136
136
  }
137
137
 
138
+ export interface MarkAllReadNotificationRequestDTO {
139
+ toList: string[];
140
+ endDate: number;
141
+ }
142
+
138
143
  export interface Pager {
139
144
  pageSize: number;
140
145
  pageNumber: number;
@@ -7,6 +7,7 @@ export declare class NotificationsService extends RestClient {
7
7
  constructor();
8
8
  getNotifications(post: Interfaces.SmsEmailAuditRequestDto): Observable<ServiceResponse<Interfaces.Response<Interfaces.SmsEmailAuditResponseDTO>>>;
9
9
  markAsReadNotification(pluginId: string): Observable<ServiceResponse<boolean>>;
10
+ markAllAsReadNotification(post: Interfaces.MarkAllReadNotificationRequestDTO): Observable<ServiceResponse<boolean>>;
10
11
  static ɵfac: i0.ɵɵFactoryDeclaration<NotificationsService, never>;
11
12
  static ɵprov: i0.ɵɵInjectableDeclaration<NotificationsService>;
12
13
  }
@@ -23,4 +23,9 @@ export class NotificationsService extends RestClient {
23
23
  markAsReadNotification(pluginId: string) {
24
24
  return new Observable<ServiceResponse<boolean>>();
25
25
  }
26
+
27
+ @ServiceCall(HTTPMethods.PUTBYPARAMS, ProxyTypes.ClientSide, 'audits/read-all', { success: 'notifications.markedAllAsRead' })
28
+ markAllAsReadNotification(post: Interfaces.MarkAllReadNotificationRequestDTO) {
29
+ return new Observable<ServiceResponse<boolean>>();
30
+ }
26
31
  }