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
@@ -3,12 +3,22 @@
3
3
  position="right"
4
4
  [transitionOptions]="'.3s cubic-bezier(0, 0, 0.2, 1)'"
5
5
  styleClass="layout-notifications-sidebar w-full sm:w-30rem"
6
+ (onShow)="onSidebarShow()"
6
7
  *transloco="let t; read: 'notifications'">
7
8
  <ng-template pTemplate="header">
8
9
  <div class="flex align-items-center justify-content-between w-full">
9
- <div class="flex align-items-center">
10
- <i class="pi pi-bell text-primary text-xl"></i>
10
+ <div class="flex align-items-center gap-2">
11
+ <i class="pi pi-bell text-xl"></i>
11
12
  <span class="font-bold text-lg">{{ t('title') }}</span>
13
+ @if (unreadCount > 0) {
14
+ <span class="unread-count-chip">{{ unreadCount }}</span>
15
+ }
16
+ </div>
17
+ <div class="flex align-items-center gap-2 header-actions">
18
+ <span class="only-unread-label">{{ t('onlyUnread') }}</span>
19
+ <aril-switch [ngModel]="onlyUnread()" (ngModelChange)="onOnlyUnreadChange($event)"></aril-switch>
20
+ <i class="pi pi-ellipsis-v header-menu-icon" (click)="toggleMenu($event, headerMenu)"></i>
21
+ <p-menu #headerMenu [popup]="true" [model]="menuItems" appendTo="body"></p-menu>
12
22
  </div>
13
23
  </div>
14
24
  </ng-template>
@@ -16,70 +26,70 @@
16
26
  <div class="notifications-content">
17
27
  @if (notifications().length > 0) {
18
28
  <div class="notifications-list">
19
- <p-scrollPanel [style]="{ width: '100%', height: '100%' }">
20
- <div class="notifications-items">
21
- @for (notification of notifications(); track trackByNotificationId($index, notification)) {
22
- <div
23
- class="notification-item"
24
- [class.unread-item]="!notification.read"
25
- (click)="
26
- (
27
- notification?.detailNotificationType === DetailNotificationTypeEnum.TODO_ITEM ||
28
- notification?.detailNotificationType === DetailNotificationTypeEnum.BATCH_JOB
29
- ) ?
30
- markNotificationAsRead(notification)
31
- : null
32
- ">
33
- <div class="item-content">
34
- <div class="item-header">
35
- <div class="item-title">
36
- @if (notification.type === ActivityTypesEnum.SMS) {
37
- <div class="notification-icon sms-icon">
38
- <fa-icon [icon]="regularIcons.faMessage" />
39
- </div>
40
- } @else {
41
- @if (!notification.read) {
42
- <div class="notification-icon email-icon unread">
43
- <fa-icon [icon]="regularIcons.faEnvelope" />
44
- </div>
45
- } @else {
46
- <div class="notification-icon email-icon read">
47
- <fa-icon [icon]="regularIcons.faEnvelopeOpen" />
48
- </div>
49
- }
50
- }
51
- <p class="title-text ml-2">{{ getNotificationTitle(notification) }}</p>
52
- </div>
53
- <span class="notification-date">{{ formatNotificationDate(notification.sendingDate) }}</span>
54
- @if (!notification.read) {
29
+ <div class="notifications-items">
30
+ @for (notification of notifications(); track notification.id) {
31
+ <div
32
+ class="notification-item"
33
+ [class.unread-item]="!notification.read"
34
+ [class.navigable]="isNavigable(notification)"
35
+ (click)="openNotification(notification)">
36
+ <div class="notification-icon" [ngClass]="getNotificationIconClass(notification)">
37
+ <fa-icon [icon]="getNotificationIcon(notification)" />
38
+ </div>
39
+ <div class="item-body">
40
+ <div class="item-header">
41
+ <p class="item-title">{{ getNotificationTitle(notification) }}</p>
42
+ <span class="item-time">{{ formatNotificationDate(notification.sendingDate) }}</span>
43
+ @if (!notification.read) {
44
+ <span class="item-actions">
55
45
  <fa-icon
56
46
  [icon]="solidIcons.faCircle"
57
47
  class="unread-dot"
58
48
  [pTooltip]="t('markAsRead')"
59
49
  tooltipPosition="left"
60
- (click)="markNotificationAsReadOnDotIcon(notification); $event.stopPropagation()">
50
+ (click)="markNotificationAsRead(notification); $event.stopPropagation()">
61
51
  </fa-icon>
62
- }
63
- </div>
64
- <div class="item-meta"></div>
65
- @if (getNotificationContent(notification)) {
66
- <div class="item-content-preview">
67
- @if (notification.type === ActivityTypesEnum.EMAIL) {
68
- <div [innerHTML]="notification.content | safe: 'html'" class="html-preview line-clamp-3"></div>
69
- } @else {
70
- <p class="text-content">
71
- {{ getNotificationContent(notification) }}
72
- </p>
73
- }
74
- </div>
52
+ <button
53
+ type="button"
54
+ class="mark-read-action"
55
+ [pTooltip]="t('markAsRead')"
56
+ tooltipPosition="left"
57
+ (click)="markNotificationAsRead(notification); $event.stopPropagation()">
58
+ <i class="pi pi-check"></i>
59
+ </button>
60
+ </span>
75
61
  }
76
62
  </div>
63
+ @if (getNotificationContent(notification)) {
64
+ @if (notification.type === ActivityTypesEnum.EMAIL) {
65
+ <div [innerHTML]="notification.content | safe: 'html'" class="item-preview html-preview"></div>
66
+ } @else {
67
+ <p class="item-preview">{{ getNotificationContent(notification) }}</p>
68
+ }
69
+ }
70
+ @if (notification.detailNotificationType === DetailNotificationTypeEnum.TODO_ITEM) {
71
+ <span class="nav-chip">{{ t('goToTaskDetail') }} <i class="pi pi-arrow-right"></i></span>
72
+ } @else if (notification.detailNotificationType === DetailNotificationTypeEnum.BATCH_JOB) {
73
+ <span class="nav-chip">{{ t('goToBulkOperations') }} <i class="pi pi-arrow-right"></i></span>
74
+ }
77
75
  </div>
78
- }
79
- </div>
80
- </p-scrollPanel>
76
+ </div>
77
+ }
78
+ @if (remainingCount() > 0) {
79
+ <div class="show-more">
80
+ <aril-button
81
+ [label]="t('showMore', { count: remainingCount() })"
82
+ color="primary"
83
+ size="sm"
84
+ [outlined]="true"
85
+ [loading]="loading"
86
+ (clickEvent)="showMore()">
87
+ </aril-button>
88
+ </div>
89
+ }
90
+ </div>
81
91
  </div>
82
- } @else {
92
+ } @else if (!loading) {
83
93
  <div class="empty-state">
84
94
  <div class="empty-content">
85
95
  <i class="pi pi-bell empty-icon"></i>
@@ -88,17 +98,5 @@
88
98
  </div>
89
99
  </div>
90
100
  }
91
-
92
- <div class="flex justify-content-center p-2 border-top-1 surface-border">
93
- <aril-button
94
- [label]="t('allShow')"
95
- icon="EXTERNAL_LINK"
96
- color="primary"
97
- size="md"
98
- (clickEvent)="navigateToAllNotifications()">
99
- </aril-button>
100
- </div>
101
101
  </div>
102
-
103
- <p-confirmDialog></p-confirmDialog>
104
102
  </p-sidebar>
@@ -14,6 +14,69 @@
14
14
  font-size: 1.1rem;
15
15
  font-weight: 600;
16
16
  }
17
+
18
+ .p-sidebar-close {
19
+ color: var(--primary-color-text);
20
+
21
+ &:enabled:hover {
22
+ background: rgba(255, 255, 255, 0.15);
23
+ color: var(--primary-color-text);
24
+ }
25
+ }
26
+ }
27
+
28
+ .header-actions {
29
+ .p-inputswitch {
30
+ transform: scale(0.8);
31
+
32
+ .p-inputswitch-slider {
33
+ background: rgba(255, 255, 255, 0.3);
34
+ }
35
+
36
+ &.p-inputswitch-checked .p-inputswitch-slider {
37
+ background: var(--green-500);
38
+ }
39
+
40
+ &.p-inputswitch-checked:not(.p-disabled):hover .p-inputswitch-slider {
41
+ background: var(--green-600);
42
+ }
43
+ }
44
+
45
+ .only-unread-label {
46
+ font-size: 0.78rem;
47
+ font-weight: 500;
48
+ }
49
+ }
50
+ }
51
+
52
+ .item-preview.html-preview {
53
+ * {
54
+ font-family: inherit !important;
55
+ font-size: inherit !important;
56
+ font-weight: inherit !important;
57
+ font-style: inherit !important;
58
+ color: inherit !important;
59
+ line-height: inherit !important;
60
+ letter-spacing: inherit !important;
61
+ text-align: left !important;
62
+ margin: 0 !important;
63
+ padding: 0 !important;
64
+ border: none !important;
65
+ background: transparent !important;
66
+ display: inline !important;
67
+ }
68
+
69
+ p::after,
70
+ div::after,
71
+ li::after {
72
+ content: ' ';
73
+ }
74
+
75
+ img,
76
+ table,
77
+ hr,
78
+ br {
79
+ display: none !important;
17
80
  }
18
81
  }
19
82
  }
@@ -22,167 +85,230 @@
22
85
  display: flex;
23
86
  flex-direction: column;
24
87
  height: 100%;
25
- background: var(--surface-0);
88
+ background: var(--surface-ground);
26
89
  }
27
90
 
28
- .add-section {
29
- display: flex;
30
- align-items: center;
31
- gap: 0.5rem;
32
- padding: 1rem;
33
- border-bottom: 1px solid var(--surface-border);
34
- background: var(--surface-0);
91
+ .unread-count-chip {
92
+ background: rgba(255, 255, 255, 0.25);
93
+ border-radius: 999px;
94
+ font-size: 0.72rem !important;
95
+ font-weight: 600 !important;
96
+ padding: 0.1rem 0.55rem;
97
+ }
98
+
99
+ .header-menu-icon {
100
+ cursor: pointer;
101
+ font-size: 1.1rem;
102
+ padding: 0.25rem;
103
+ color: var(--primary-color-text);
104
+ border-radius: 6px;
105
+ transition: background-color 0.15s ease;
106
+
107
+ &:hover {
108
+ background: rgba(255, 255, 255, 0.15);
109
+ }
35
110
  }
36
111
 
37
112
  .notifications-list {
38
113
  flex: 1;
39
- overflow: hidden;
114
+ overflow-y: auto;
40
115
  }
41
116
 
42
117
  .notifications-items {
43
- padding: 0;
118
+ display: flex;
119
+ flex-direction: column;
120
+ gap: 0.625rem;
121
+ padding: 0.75rem;
44
122
  }
45
123
 
46
124
  .notification-item {
47
- background: transparent;
48
- border-bottom: 1px solid var(--surface-border);
49
- cursor: pointer;
50
- transition: background-color 0.2s ease;
51
- position: relative;
125
+ display: flex;
126
+ gap: 0.75rem;
127
+ padding: 0.875rem 1rem 0.875rem calc(1rem + 4px);
128
+ background: var(--surface-card);
129
+ border: 1px solid var(--surface-border);
130
+ border-radius: 8px;
131
+ transition:
132
+ background-color 0.2s ease,
133
+ border-color 0.2s ease;
52
134
 
53
135
  &:hover {
54
136
  background: var(--surface-hover);
137
+
138
+ .unread-dot {
139
+ display: none;
140
+ }
141
+
142
+ .mark-read-action {
143
+ display: flex;
144
+ }
55
145
  }
56
146
 
57
- &:last-child {
58
- border-bottom: none;
147
+ &.navigable {
148
+ cursor: pointer;
59
149
  }
60
150
 
61
151
  &.unread-item {
62
- background: var(--surface-50);
152
+ border-left-width: 5px;
153
+ border-left-color: var(--primary-color);
154
+ padding-left: 1rem;
63
155
 
64
- &:hover {
65
- background: var(--surface-100);
156
+ .item-title {
157
+ font-weight: 600;
158
+ color: var(--text-color);
66
159
  }
160
+ }
161
+ }
67
162
 
68
- .title-text {
69
- font-weight: 500;
70
- // color: var(--primary-color);
71
- }
163
+ .notification-icon {
164
+ width: 2.25rem;
165
+ height: 2.25rem;
166
+ border-radius: 50%;
167
+ display: flex;
168
+ align-items: center;
169
+ justify-content: center;
170
+ flex-shrink: 0;
171
+
172
+ fa-icon {
173
+ font-size: 0.9rem;
174
+ }
175
+
176
+ &.todo {
177
+ background: var(--purple-100);
178
+ color: var(--purple-600);
179
+ }
180
+
181
+ &.batch {
182
+ background: var(--orange-100);
183
+ color: var(--orange-600);
184
+ }
185
+
186
+ &.sms {
187
+ background: var(--green-100);
188
+ color: var(--green-600);
189
+ }
190
+
191
+ &.email-unread {
192
+ background: var(--blue-100);
193
+ color: var(--blue-600);
194
+ }
195
+
196
+ &.email-read {
197
+ background: var(--surface-200);
198
+ color: var(--text-color-secondary);
72
199
  }
73
200
  }
74
201
 
75
- .item-content {
76
- padding: 1.125rem 1rem;
202
+ .item-body {
203
+ flex: 1;
204
+ min-width: 0;
77
205
  }
78
206
 
79
207
  .item-header {
80
208
  display: flex;
81
209
  align-items: center;
82
- justify-content: space-between;
83
- margin-bottom: 0.625rem;
84
- gap: 1rem;
210
+ gap: 0.5rem;
211
+ margin-bottom: 0.3rem;
212
+ min-height: 1.5rem;
85
213
  }
86
214
 
87
215
  .item-title {
88
- display: flex;
89
- align-items: center;
90
216
  flex: 1;
91
217
  min-width: 0;
92
- gap: 0.4em;
93
-
94
- .notification-icon {
95
- width: 1.75rem;
96
- height: 1.75rem;
97
- border-radius: 50%;
98
- display: flex;
99
- align-items: center;
100
- justify-content: center;
101
- // margin-top: 0.125rem;
102
- flex-shrink: 0;
103
- transition: all 0.2s ease;
104
-
105
- fa-icon {
106
- font-size: 0.875rem;
107
- transition: all 0.2s ease;
108
- }
218
+ margin: 0;
219
+ font-size: 0.875rem;
220
+ font-weight: 500;
221
+ color: var(--text-color-secondary);
222
+ line-height: 1.4;
223
+ white-space: nowrap;
224
+ text-overflow: ellipsis;
225
+ overflow: hidden;
226
+ }
109
227
 
110
- &.email-icon {
111
- &.unread {
112
- background-color: #3b82f6;
113
- color: white;
114
- box-shadow: 0 1px 4px rgba(0, 0, 0, 0.15);
228
+ .item-time {
229
+ font-size: 0.72rem;
230
+ color: var(--text-color-secondary);
231
+ white-space: nowrap;
232
+ }
115
233
 
116
- fa-icon {
117
- color: white;
118
- }
119
- }
234
+ .item-actions {
235
+ display: inline-flex;
236
+ align-items: center;
237
+ justify-content: center;
238
+ width: 1.5rem;
239
+ flex-shrink: 0;
240
+ }
120
241
 
121
- &.read {
122
- background: var(--surface-200);
123
- color: var(--text-color-secondary);
242
+ .unread-dot {
243
+ color: var(--primary-color);
244
+ font-size: 0.5rem;
245
+ cursor: pointer;
246
+ opacity: 0.85;
124
247
 
125
- fa-icon {
126
- color: var(--text-color-secondary);
127
- }
128
- }
129
- }
248
+ &:hover {
249
+ opacity: 1;
250
+ }
251
+ }
130
252
 
131
- &.sms-icon {
132
- background: linear-gradient(135deg, var(--green-500), var(--green-600));
133
- color: white;
134
- box-shadow: 0 2px 8px rgba(34, 197, 94, 0.25);
253
+ .mark-read-action {
254
+ display: none;
255
+ width: 1.5rem;
256
+ height: 1.5rem;
257
+ border-radius: 6px;
258
+ border: 1px solid var(--surface-border);
259
+ background: var(--surface-card);
260
+ color: var(--text-color-secondary);
261
+ align-items: center;
262
+ justify-content: center;
263
+ cursor: pointer;
264
+ padding: 0;
135
265
 
136
- fa-icon {
137
- color: white;
138
- }
139
- }
266
+ &:hover {
267
+ color: var(--primary-color);
268
+ border-color: var(--primary-color);
140
269
  }
141
270
 
142
- .title-text {
143
- font-weight: 500;
144
- line-height: 1.5;
145
- font-size: 0.975rem;
146
- margin: 0;
271
+ i {
272
+ font-size: 0.7rem;
147
273
  }
148
274
  }
149
275
 
150
- .notification-date {
276
+ .item-preview {
277
+ margin: 0;
151
278
  font-size: 0.8rem;
152
279
  color: var(--text-color-secondary);
153
- font-weight: 500;
154
- white-space: nowrap;
155
- background: var(--surface-100);
156
- padding: 0.2rem 0.4rem;
157
- border-radius: 0.25rem;
280
+ line-height: 1.5;
281
+ display: -webkit-box;
282
+ -webkit-box-orient: vertical;
283
+ -webkit-line-clamp: 2;
284
+ line-clamp: 2;
285
+ overflow: hidden;
286
+ word-break: break-word;
287
+ white-space: normal;
158
288
  }
159
289
 
160
- .item-content-preview {
161
- margin-top: 0.625rem;
162
- color: var(--text-color-secondary);
163
- padding-left: 1.5rem;
164
- border-left: 2px solid var(--surface-200);
165
-
166
- .text-content {
167
- font-size: 0.875rem;
168
- line-height: 1.5;
169
- margin: 0;
170
- font-style: italic;
290
+ .nav-chip {
291
+ display: inline-flex;
292
+ align-items: center;
293
+ gap: 0.3rem;
294
+ margin-top: 0.4rem;
295
+ font-size: 0.75rem;
296
+ font-weight: 600;
297
+ color: var(--primary-color);
298
+
299
+ i {
300
+ font-size: 0.65rem;
171
301
  }
302
+ }
172
303
 
173
- .html-preview {
174
- font-size: 0.875rem;
175
- line-height: 1.5;
176
- max-height: 75px;
177
- display: -webkit-box;
178
- -webkit-box-orient: vertical;
179
- -webkit-line-clamp: 3;
180
- line-clamp: 3;
181
- overflow: hidden;
182
- word-break: break-word;
183
- white-space: normal;
184
- font-style: italic;
185
- }
304
+ .notification-item.navigable:hover .nav-chip {
305
+ text-decoration: underline;
306
+ }
307
+
308
+ .show-more {
309
+ display: flex;
310
+ justify-content: center;
311
+ padding: 0.25rem 0 0.5rem;
186
312
  }
187
313
 
188
314
  .empty-state {
@@ -216,18 +342,6 @@
216
342
  }
217
343
  }
218
344
 
219
- .unread-dot {
220
- color: #3b82f6;
221
- font-size: 0.625rem;
222
- cursor: pointer;
223
- transition: opacity 0.2s ease;
224
- opacity: 0.8;
225
-
226
- &:hover {
227
- opacity: 1;
228
- }
229
- }
230
-
231
345
  @media (max-width: 768px) {
232
346
  .layout-notifications-sidebar {
233
347
  ::ng-deep .p-sidebar {