@zambon-dev/shared 1.0.2 → 1.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/CHANGELOG.md +95 -0
- package/fesm2022/zambon-dev-shared.mjs +331 -56
- package/fesm2022/zambon-dev-shared.mjs.map +1 -1
- package/i18n/top-bar/en.json +7 -0
- package/i18n/top-bar/pt.json +7 -0
- package/lib/auth/components/language-selector/language-selector.component.d.ts +3 -1
- package/lib/layouts/index.d.ts +1 -0
- package/lib/layouts/main-layout/main-layout.component.d.ts +2 -0
- package/lib/layouts/top-bar/brand/brand.component.d.ts +9 -0
- package/lib/layouts/top-bar/environment-badge/environment-badge.component.d.ts +12 -0
- package/lib/layouts/top-bar/index.d.ts +5 -0
- package/lib/layouts/top-bar/notifications/notifications.component.d.ts +21 -0
- package/lib/layouts/top-bar/top-bar.component.d.ts +9 -0
- package/lib/layouts/top-bar/user-profile/user-profile.component.d.ts +15 -0
- package/lib/models/current-user-info.d.ts +4 -0
- package/lib/models/index.d.ts +1 -0
- package/lib/models/notification.d.ts +12 -0
- package/lib/services/index.d.ts +1 -0
- package/lib/services/notifications.service.d.ts +33 -0
- package/package.json +2 -1
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { Component, inject, Injectable, ViewChild, Input,
|
|
2
|
+
import { Component, inject, Injectable, ViewChild, HostListener, Input, EventEmitter, Output, NgModule, Pipe } from '@angular/core';
|
|
3
3
|
import * as i1 from '@angular/router';
|
|
4
4
|
import { RouterModule, Router, ActivatedRoute } from '@angular/router';
|
|
5
5
|
import { AuthService, APP_CONFIG, TabService, Tab, TabsComponent, ButtonFiltersComponent, ModalBase, ChildList, ViewBase } from '@zambon-dev/framework';
|
|
6
6
|
import { SidebarService, ModalComponent, SidebarComponent, DataGridDataset, DataGridComponent, DataProviderService } from '@zambon-dev/library';
|
|
7
7
|
import { TranslatePipe, TranslateService, TranslateLoader, provideTranslateService } from '@ngx-translate/core';
|
|
8
|
-
import { map, catchError, interval, mergeMap, tap, Subject, take, takeUntil, finalize, shareReplay, switchMap, of } from 'rxjs';
|
|
8
|
+
import { map, catchError, interval, mergeMap, tap, BehaviorSubject, Subject, take, takeUntil, finalize, shareReplay, switchMap, of } from 'rxjs';
|
|
9
9
|
import { HttpClient, HttpBackend } from '@angular/common/http';
|
|
10
10
|
import { JwtHelperService, JwtInterceptor } from '@auth0/angular-jwt';
|
|
11
|
-
import {
|
|
11
|
+
import { HubConnectionState, HubConnectionBuilder } from '@microsoft/signalr';
|
|
12
|
+
import { AsyncPipe, NgIf, JsonPipe, DatePipe } from '@angular/common';
|
|
12
13
|
import * as i1$1 from '@angular/forms';
|
|
13
14
|
import { FormBuilder, Validators, ReactiveFormsModule } from '@angular/forms';
|
|
14
15
|
import { DomSanitizer } from '@angular/platform-browser';
|
|
@@ -94,6 +95,84 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImpor
|
|
|
94
95
|
}]
|
|
95
96
|
}], ctorParameters: () => [] });
|
|
96
97
|
|
|
98
|
+
/**
|
|
99
|
+
* Streams top-bar notifications from a SignalR hub.
|
|
100
|
+
*
|
|
101
|
+
* The hub is configured via `AppConfig.notificationsEnabled` and `AppConfig.notificationsUrl`.
|
|
102
|
+
* When enabled, {@link start} opens a connection (authenticated with the current JWT) and
|
|
103
|
+
* listens for the server to push the notification list via the `ReceiveNotifications` client
|
|
104
|
+
* method. Consuming apps do not interact with this service directly — the top bar drives it.
|
|
105
|
+
*/
|
|
106
|
+
class NotificationsService {
|
|
107
|
+
//#region Variables
|
|
108
|
+
authService = inject(AuthService);
|
|
109
|
+
config = inject(APP_CONFIG);
|
|
110
|
+
connection;
|
|
111
|
+
notifications$ = new BehaviorSubject([]);
|
|
112
|
+
//#endregion
|
|
113
|
+
//#region Properties
|
|
114
|
+
/** Whether the notifications feature is enabled (toggled on and given a hub URL). */
|
|
115
|
+
get isEnabled() {
|
|
116
|
+
return this.config.notificationsEnabled && this.config.notificationsUrl.length > 0;
|
|
117
|
+
}
|
|
118
|
+
//#endregion
|
|
119
|
+
//#region Public methods
|
|
120
|
+
/** Emits the current list of notifications. */
|
|
121
|
+
getNotifications() {
|
|
122
|
+
return this.notifications$.asObservable();
|
|
123
|
+
}
|
|
124
|
+
/** Emits the number of unread notifications. */
|
|
125
|
+
getUnreadCount() {
|
|
126
|
+
return this.notifications$.pipe(map((notifications) => notifications.filter((n) => !n.isRead).length));
|
|
127
|
+
}
|
|
128
|
+
/** Marks every notification as read and notifies the hub. */
|
|
129
|
+
markAllAsRead() {
|
|
130
|
+
this.notifications$.next(this.notifications$.value.map((n) => ({ ...n, isRead: true })));
|
|
131
|
+
if (this.connection?.state === HubConnectionState.Connected) {
|
|
132
|
+
this.connection.invoke('MarkAllAsRead').catch((error) => {
|
|
133
|
+
console.error('Failed to mark notifications as read on the hub.', error);
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/** Marks a single notification as read (optimistically; the server reconciles on the next push). */
|
|
138
|
+
markAsRead(notification) {
|
|
139
|
+
this.notifications$.next(this.notifications$.value.map((n) => (n === notification ? { ...n, isRead: true } : n)));
|
|
140
|
+
}
|
|
141
|
+
/** Opens the SignalR connection and starts receiving notifications. Idempotent and a no-op when disabled. */
|
|
142
|
+
start() {
|
|
143
|
+
if (!this.isEnabled || !!this.connection) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
this.connection = new HubConnectionBuilder()
|
|
147
|
+
.withUrl(this.config.notificationsUrl, {
|
|
148
|
+
accessTokenFactory: () => this.authService.token ?? '',
|
|
149
|
+
})
|
|
150
|
+
.withAutomaticReconnect()
|
|
151
|
+
.build();
|
|
152
|
+
this.connection.on('ReceiveNotifications', (notifications) => {
|
|
153
|
+
this.notifications$.next(notifications ?? []);
|
|
154
|
+
});
|
|
155
|
+
this.connection.start().catch((error) => {
|
|
156
|
+
console.error('Failed to connect to the notifications hub.', error);
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
/** Closes the SignalR connection. */
|
|
160
|
+
async stop() {
|
|
161
|
+
if (this.connection) {
|
|
162
|
+
await this.connection.stop();
|
|
163
|
+
this.connection = undefined;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: NotificationsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
167
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: NotificationsService, providedIn: 'root' });
|
|
168
|
+
}
|
|
169
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: NotificationsService, decorators: [{
|
|
170
|
+
type: Injectable,
|
|
171
|
+
args: [{
|
|
172
|
+
providedIn: 'root',
|
|
173
|
+
}]
|
|
174
|
+
}] });
|
|
175
|
+
|
|
97
176
|
class OperationsHistoryService {
|
|
98
177
|
//#region ViewChilds, Inputs, Outputs
|
|
99
178
|
//#endregion
|
|
@@ -148,76 +227,137 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImpor
|
|
|
148
227
|
}]
|
|
149
228
|
}] });
|
|
150
229
|
|
|
151
|
-
class
|
|
230
|
+
class BrandComponent {
|
|
231
|
+
//#region Variables
|
|
232
|
+
config = inject(APP_CONFIG);
|
|
233
|
+
//#endregion
|
|
234
|
+
//#region Properties
|
|
235
|
+
get appName() {
|
|
236
|
+
return this.config.appName;
|
|
237
|
+
}
|
|
238
|
+
get companyName() {
|
|
239
|
+
return this.config.companyName;
|
|
240
|
+
}
|
|
241
|
+
get logoUrl() {
|
|
242
|
+
return this.config.logoUrl;
|
|
243
|
+
}
|
|
244
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: BrandComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
245
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.1.6", type: BrandComponent, isStandalone: true, selector: "shared-brand", ngImport: i0, template: "<div class=\"brand\">\n @if (logoUrl) {\n <img class=\"brand-logo\" [src]=\"logoUrl\" [alt]=\"appName\" />\n }\n\n <div class=\"brand-text\">\n <span class=\"brand-app\">{{ appName }}</span>\n @if (companyName) {\n <span class=\"brand-company\">{{ companyName }}</span>\n }\n </div>\n</div>\n", styles: ["@reference \"tailwindcss\";.brand{display:flex;min-width:0px;align-items:center;gap:.5rem}.brand .brand-app{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:.875rem;line-height:1.25rem;font-weight:600}.brand .brand-company{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:.75rem;line-height:1rem;--tw-text-opacity: 1;color:rgb(120 113 108 / var(--tw-text-opacity, 1))}.brand .brand-logo{height:2rem;width:2rem;flex-shrink:0;object-fit:contain}.brand .brand-text{display:flex;min-width:0px;flex-direction:column;line-height:1.25}@media (max-width: 767px){.brand .brand-text{display:none}}\n"] });
|
|
246
|
+
}
|
|
247
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: BrandComponent, decorators: [{
|
|
248
|
+
type: Component,
|
|
249
|
+
args: [{ selector: 'shared-brand', template: "<div class=\"brand\">\n @if (logoUrl) {\n <img class=\"brand-logo\" [src]=\"logoUrl\" [alt]=\"appName\" />\n }\n\n <div class=\"brand-text\">\n <span class=\"brand-app\">{{ appName }}</span>\n @if (companyName) {\n <span class=\"brand-company\">{{ companyName }}</span>\n }\n </div>\n</div>\n", styles: ["@reference \"tailwindcss\";.brand{display:flex;min-width:0px;align-items:center;gap:.5rem}.brand .brand-app{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:.875rem;line-height:1.25rem;font-weight:600}.brand .brand-company{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:.75rem;line-height:1rem;--tw-text-opacity: 1;color:rgb(120 113 108 / var(--tw-text-opacity, 1))}.brand .brand-logo{height:2rem;width:2rem;flex-shrink:0;object-fit:contain}.brand .brand-text{display:flex;min-width:0px;flex-direction:column;line-height:1.25}@media (max-width: 767px){.brand .brand-text{display:none}}\n"] }]
|
|
250
|
+
}] });
|
|
251
|
+
|
|
252
|
+
class EnvironmentBadgeComponent {
|
|
253
|
+
//#region Variables
|
|
254
|
+
config = inject(APP_CONFIG);
|
|
255
|
+
//#endregion
|
|
256
|
+
//#region Properties
|
|
257
|
+
/** CSS modifier class for the current environment, falling back to a neutral style. */
|
|
258
|
+
get badgeClass() {
|
|
259
|
+
const colorByEnvironment = {
|
|
260
|
+
DEV: 'env-dev',
|
|
261
|
+
QA: 'env-qa',
|
|
262
|
+
STG: 'env-stg',
|
|
263
|
+
};
|
|
264
|
+
return colorByEnvironment[this.environment] ?? 'env-neutral';
|
|
265
|
+
}
|
|
266
|
+
/** Normalized environment key (uppercase, trimmed). */
|
|
267
|
+
get environment() {
|
|
268
|
+
return (this.config.environment ?? '').trim().toUpperCase();
|
|
269
|
+
}
|
|
270
|
+
/** The badge is hidden in production and when no environment is configured. */
|
|
271
|
+
get isVisible() {
|
|
272
|
+
return this.environment.length > 0 && this.environment !== 'PROD';
|
|
273
|
+
}
|
|
274
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: EnvironmentBadgeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
275
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.1.6", type: EnvironmentBadgeComponent, isStandalone: true, selector: "shared-environment-badge", ngImport: i0, template: "@if (isVisible) {\n <span class=\"env-badge {{ badgeClass }}\">\n <span class=\"env-dot\"></span>\n <span class=\"env-label\">{{ environment }}</span>\n </span>\n}\n", styles: ["@reference \"tailwindcss\";.env-badge{display:inline-flex;align-items:center;gap:.375rem;border-radius:9999px;padding:.325rem .625rem;font-size:.875rem;line-height:1.25rem;font-weight:600;text-transform:uppercase;letter-spacing:.025em;--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000);--tw-ring-inset: inset }.env-badge .env-dot{height:.375rem;width:.375rem;flex-shrink:0;border-radius:9999px}.env-badge.env-dev{--tw-bg-opacity: 1;background-color:rgb(239 246 255 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(29 78 216 / var(--tw-text-opacity, 1));--tw-ring-color: rgb(37 99 235 / .2) }.env-badge.env-dev .env-dot{--tw-bg-opacity: 1;background-color:rgb(59 130 246 / var(--tw-bg-opacity, 1))}.env-badge.env-neutral{--tw-bg-opacity: 1;background-color:rgb(245 245 244 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(87 83 78 / var(--tw-text-opacity, 1));--tw-ring-color: rgb(120 113 108 / .2) }.env-badge.env-neutral .env-dot{--tw-bg-opacity: 1;background-color:rgb(120 113 108 / var(--tw-bg-opacity, 1))}.env-badge.env-qa{--tw-bg-opacity: 1;background-color:rgb(255 251 235 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(180 83 9 / var(--tw-text-opacity, 1));--tw-ring-color: rgb(217 119 6 / .2) }.env-badge.env-qa .env-dot{--tw-bg-opacity: 1;background-color:rgb(245 158 11 / var(--tw-bg-opacity, 1))}.env-badge.env-stg{--tw-bg-opacity: 1;background-color:rgb(245 243 255 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(109 40 217 / var(--tw-text-opacity, 1));--tw-ring-color: rgb(124 58 237 / .2) }.env-badge.env-stg .env-dot{--tw-bg-opacity: 1;background-color:rgb(139 92 246 / var(--tw-bg-opacity, 1))}\n"] });
|
|
276
|
+
}
|
|
277
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: EnvironmentBadgeComponent, decorators: [{
|
|
278
|
+
type: Component,
|
|
279
|
+
args: [{ selector: 'shared-environment-badge', template: "@if (isVisible) {\n <span class=\"env-badge {{ badgeClass }}\">\n <span class=\"env-dot\"></span>\n <span class=\"env-label\">{{ environment }}</span>\n </span>\n}\n", styles: ["@reference \"tailwindcss\";.env-badge{display:inline-flex;align-items:center;gap:.375rem;border-radius:9999px;padding:.325rem .625rem;font-size:.875rem;line-height:1.25rem;font-weight:600;text-transform:uppercase;letter-spacing:.025em;--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000);--tw-ring-inset: inset }.env-badge .env-dot{height:.375rem;width:.375rem;flex-shrink:0;border-radius:9999px}.env-badge.env-dev{--tw-bg-opacity: 1;background-color:rgb(239 246 255 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(29 78 216 / var(--tw-text-opacity, 1));--tw-ring-color: rgb(37 99 235 / .2) }.env-badge.env-dev .env-dot{--tw-bg-opacity: 1;background-color:rgb(59 130 246 / var(--tw-bg-opacity, 1))}.env-badge.env-neutral{--tw-bg-opacity: 1;background-color:rgb(245 245 244 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(87 83 78 / var(--tw-text-opacity, 1));--tw-ring-color: rgb(120 113 108 / .2) }.env-badge.env-neutral .env-dot{--tw-bg-opacity: 1;background-color:rgb(120 113 108 / var(--tw-bg-opacity, 1))}.env-badge.env-qa{--tw-bg-opacity: 1;background-color:rgb(255 251 235 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(180 83 9 / var(--tw-text-opacity, 1));--tw-ring-color: rgb(217 119 6 / .2) }.env-badge.env-qa .env-dot{--tw-bg-opacity: 1;background-color:rgb(245 158 11 / var(--tw-bg-opacity, 1))}.env-badge.env-stg{--tw-bg-opacity: 1;background-color:rgb(245 243 255 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(109 40 217 / var(--tw-text-opacity, 1));--tw-ring-color: rgb(124 58 237 / .2) }.env-badge.env-stg .env-dot{--tw-bg-opacity: 1;background-color:rgb(139 92 246 / var(--tw-bg-opacity, 1))}\n"] }]
|
|
280
|
+
}] });
|
|
281
|
+
|
|
282
|
+
class NotificationsComponent {
|
|
152
283
|
//#region ViewChilds, Inputs, Outputs
|
|
153
|
-
|
|
284
|
+
dropdown;
|
|
154
285
|
//#endregion
|
|
155
286
|
//#region Variables
|
|
156
|
-
|
|
157
|
-
destroy$ = new Subject();
|
|
287
|
+
notificationsService = inject(NotificationsService);
|
|
158
288
|
router = inject(Router);
|
|
159
|
-
|
|
160
|
-
|
|
289
|
+
notifications$ = this.notificationsService.getNotifications();
|
|
290
|
+
showDropdown = false;
|
|
291
|
+
unreadCount$ = this.notificationsService.getUnreadCount();
|
|
161
292
|
//#endregion
|
|
162
293
|
//#region Properties
|
|
294
|
+
/** Whether the notifications feature is enabled; when false the bell is not rendered. */
|
|
295
|
+
get isEnabled() {
|
|
296
|
+
return this.notificationsService.isEnabled;
|
|
297
|
+
}
|
|
163
298
|
//#endregion
|
|
164
299
|
//#region Constructor and Angular life cycle methods
|
|
165
|
-
ngOnDestroy() {
|
|
166
|
-
this.destroy$.next(true);
|
|
167
|
-
this.destroy$.complete();
|
|
168
|
-
}
|
|
169
300
|
ngOnInit() {
|
|
170
|
-
|
|
171
|
-
if (url !== '/' && !this.tabService.isUrlOpen(url)) {
|
|
172
|
-
this.sidebarService.getMenuFromUrl(url)
|
|
173
|
-
.pipe(take(1))
|
|
174
|
-
.subscribe((item) => {
|
|
175
|
-
if (!!item) {
|
|
176
|
-
this.tabService.updateTabTitle(url, item.label);
|
|
177
|
-
}
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
this.sidebarService.menuUrlSelected
|
|
181
|
-
.pipe(takeUntil(this.destroy$))
|
|
182
|
-
.subscribe((item) => {
|
|
183
|
-
const tab = new Tab({
|
|
184
|
-
title: item.label,
|
|
185
|
-
url: item.url,
|
|
186
|
-
});
|
|
187
|
-
this.tabService.openTab(tab);
|
|
188
|
-
});
|
|
301
|
+
this.notificationsService.start();
|
|
189
302
|
}
|
|
190
303
|
//#endregion
|
|
191
304
|
//#region Event handlers
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
onLogoutClick() {
|
|
195
|
-
this.logoutModal.toggleModal();
|
|
305
|
+
onDropdownClick() {
|
|
306
|
+
this.showDropdown = !this.showDropdown;
|
|
196
307
|
}
|
|
197
|
-
|
|
198
|
-
this.
|
|
199
|
-
this.router.navigate(['/login']);
|
|
308
|
+
onMarkAllAsRead() {
|
|
309
|
+
this.notificationsService.markAllAsRead();
|
|
200
310
|
}
|
|
201
|
-
|
|
202
|
-
|
|
311
|
+
onNotificationClick(notification) {
|
|
312
|
+
this.notificationsService.markAsRead(notification);
|
|
313
|
+
this.showDropdown = false;
|
|
314
|
+
const url = notification.callToActionUrl;
|
|
315
|
+
if (!url) {
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
if (/^https?:\/\//i.test(url)) {
|
|
319
|
+
window.open(url, '_blank', 'noopener');
|
|
320
|
+
}
|
|
321
|
+
else {
|
|
322
|
+
this.router.navigateByUrl(url);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
//#endregion
|
|
326
|
+
//#region Private methods
|
|
327
|
+
documentClick(event) {
|
|
328
|
+
if (this.showDropdown) {
|
|
329
|
+
let target = event.target;
|
|
330
|
+
while (target !== null && target.tagName?.toUpperCase() !== 'BODY') {
|
|
331
|
+
if (target === this.dropdown.nativeElement) {
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
target = target.parentElement;
|
|
335
|
+
}
|
|
336
|
+
this.showDropdown = false;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: NotificationsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
340
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.1.6", type: NotificationsComponent, isStandalone: true, selector: "shared-notifications", host: { listeners: { "window:click": "documentClick($event)" } }, viewQueries: [{ propertyName: "dropdown", first: true, predicate: ["dropdown"], descendants: true }], ngImport: i0, template: "@if (isEnabled) {\n <div #dropdown class=\"notifications\">\n <button class=\"btn gray-300 notifications-toggle\" type=\"button\" (click)=\"onDropdownClick()\"\n [attr.aria-label]=\"'TopBar-Notifications-Title' | translate\">\n <i class=\"fa-solid fa-bell\"></i>\n @if (unreadCount$ | async; as count) {\n <span class=\"notifications-badge\">{{ count }}</span>\n }\n </button>\n\n @if (showDropdown) {\n <div class=\"notifications-panel\">\n @let items = (notifications$ | async) ?? [];\n\n <div class=\"notifications-header\">\n <span>{{ 'TopBar-Notifications-Title' | translate }}</span>\n @if (items.length > 0) {\n <button type=\"button\" class=\"notifications-mark-all\" (click)=\"onMarkAllAsRead()\">\n {{ 'TopBar-Notifications-MarkAllRead' | translate }}\n </button>\n }\n </div>\n\n @if (items.length > 0) {\n <ul class=\"notifications-list\">\n @for (item of items; track $index) {\n <li class=\"notifications-item\" [class.unread]=\"!item.isRead\">\n <button type=\"button\" class=\"notifications-item-btn\"\n [class.has-cta]=\"!!item.callToActionUrl\" (click)=\"onNotificationClick(item)\">\n <span class=\"notifications-item-icon\"><i class=\"{{ item.icon }}\"></i></span>\n <span class=\"notifications-item-body\">\n <span class=\"notifications-item-title\">{{ item.title }}</span>\n <span class=\"notifications-item-description\">{{ item.description }}</span>\n </span>\n @if (item.callToActionUrl) {\n <i class=\"notifications-item-cta fa-solid fa-chevron-right\"></i>\n }\n </button>\n </li>\n }\n </ul>\n } @else {\n <div class=\"notifications-empty\">{{ 'TopBar-Notifications-Empty' | translate }}</div>\n }\n </div>\n }\n </div>\n}\n", styles: ["@reference \"tailwindcss\";.notifications{position:relative}.notifications .notifications-badge{position:absolute;top:-.25rem;right:-.25rem;height:1rem;min-width:1rem;border-radius:9999px;padding-left:.25rem;padding-right:.25rem;display:flex;align-items:center;justify-content:center;font-size:10px;font-weight:700;line-height:1;--tw-bg-opacity: 1;background-color:rgb(220 38 38 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.notifications .notifications-empty{padding:1.5rem .75rem;text-align:center;font-size:.875rem;line-height:1.25rem;--tw-text-opacity: 1;color:rgb(120 113 108 / var(--tw-text-opacity, 1))}.notifications .notifications-header{display:flex;align-items:center;justify-content:space-between;border-bottom-width:1px;--tw-border-opacity: 1;border-color:rgb(231 229 228 / var(--tw-border-opacity, 1));padding:.5rem .75rem;font-size:.875rem;line-height:1.25rem;font-weight:600}.notifications .notifications-list{max-height:20rem;overflow-y:auto}.notifications .notifications-list .notifications-item{border-bottom-width:1px;--tw-border-opacity: 1;border-color:rgb(245 245 244 / var(--tw-border-opacity, 1))}.notifications .notifications-list .notifications-item:last-child{border-bottom-width:0px}.notifications .notifications-list .notifications-item.unread{background-color:#e6f4ff99}.notifications .notifications-list .notifications-item .notifications-item-body{display:flex;min-width:0px;flex:1 1 0%;flex-direction:column}.notifications .notifications-list .notifications-item .notifications-item-btn{display:flex;width:100%;cursor:pointer;align-items:flex-start;gap:.625rem;padding:.5rem .75rem;text-align:left}.notifications .notifications-list .notifications-item .notifications-item-btn:hover{--tw-bg-opacity: 1;background-color:rgb(250 250 249 / var(--tw-bg-opacity, 1))}.notifications .notifications-list .notifications-item .notifications-item-cta{margin-top:.25rem;flex-shrink:0;font-size:.75rem;line-height:1rem;--tw-text-opacity: 1;color:rgb(168 162 158 / var(--tw-text-opacity, 1))}.notifications .notifications-list .notifications-item .notifications-item-description{font-size:.75rem;line-height:1rem;--tw-text-opacity: 1;color:rgb(120 113 108 / var(--tw-text-opacity, 1))}.notifications .notifications-list .notifications-item .notifications-item-icon{margin-top:.125rem;display:flex;height:1.5rem;width:1.5rem;flex-shrink:0;align-items:center;justify-content:center;border-radius:9999px;--tw-bg-opacity: 1;background-color:rgb(245 245 244 / var(--tw-bg-opacity, 1));font-size:.75rem;line-height:1rem;--tw-text-opacity: 1;color:rgb(87 83 78 / var(--tw-text-opacity, 1))}.notifications .notifications-list .notifications-item .notifications-item-title{font-size:.875rem;line-height:1.25rem;font-weight:500;--tw-text-opacity: 1;color:rgb(41 37 36 / var(--tw-text-opacity, 1))}.notifications .notifications-mark-all{font-size:.75rem;line-height:1rem;font-weight:500;--tw-text-opacity: 1;color:rgb(0 107 182 / var(--tw-text-opacity, 1))}.notifications .notifications-mark-all:hover{--tw-text-opacity: 1;color:rgb(0 87 148 / var(--tw-text-opacity, 1))}.notifications .notifications-panel{position:absolute;right:0;top:100%;z-index:50;margin-top:.25rem;width:20rem;max-width:90vw;border-radius:.375rem;border-width:1px;--tw-border-opacity: 1;border-color:rgb(231 229 228 / var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1));--tw-shadow: 0 0 4px 0 rgba(0,0,0,.2), 0 3px 20px 0 rgba(0,0,0,.19);--tw-shadow-colored: 0 0 4px 0 var(--tw-shadow-color), 0 3px 20px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.notifications .notifications-toggle{position:relative}\n"], dependencies: [{ kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
|
|
203
341
|
}
|
|
204
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type:
|
|
342
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: NotificationsComponent, decorators: [{
|
|
205
343
|
type: Component,
|
|
206
|
-
args: [{ selector: 'shared-
|
|
207
|
-
|
|
208
|
-
SidebarComponent,
|
|
209
|
-
RouterModule,
|
|
210
|
-
TabsComponent,
|
|
344
|
+
args: [{ selector: 'shared-notifications', imports: [
|
|
345
|
+
AsyncPipe,
|
|
211
346
|
TranslatePipe,
|
|
212
|
-
], template: "
|
|
213
|
-
}], propDecorators: {
|
|
347
|
+
], template: "@if (isEnabled) {\n <div #dropdown class=\"notifications\">\n <button class=\"btn gray-300 notifications-toggle\" type=\"button\" (click)=\"onDropdownClick()\"\n [attr.aria-label]=\"'TopBar-Notifications-Title' | translate\">\n <i class=\"fa-solid fa-bell\"></i>\n @if (unreadCount$ | async; as count) {\n <span class=\"notifications-badge\">{{ count }}</span>\n }\n </button>\n\n @if (showDropdown) {\n <div class=\"notifications-panel\">\n @let items = (notifications$ | async) ?? [];\n\n <div class=\"notifications-header\">\n <span>{{ 'TopBar-Notifications-Title' | translate }}</span>\n @if (items.length > 0) {\n <button type=\"button\" class=\"notifications-mark-all\" (click)=\"onMarkAllAsRead()\">\n {{ 'TopBar-Notifications-MarkAllRead' | translate }}\n </button>\n }\n </div>\n\n @if (items.length > 0) {\n <ul class=\"notifications-list\">\n @for (item of items; track $index) {\n <li class=\"notifications-item\" [class.unread]=\"!item.isRead\">\n <button type=\"button\" class=\"notifications-item-btn\"\n [class.has-cta]=\"!!item.callToActionUrl\" (click)=\"onNotificationClick(item)\">\n <span class=\"notifications-item-icon\"><i class=\"{{ item.icon }}\"></i></span>\n <span class=\"notifications-item-body\">\n <span class=\"notifications-item-title\">{{ item.title }}</span>\n <span class=\"notifications-item-description\">{{ item.description }}</span>\n </span>\n @if (item.callToActionUrl) {\n <i class=\"notifications-item-cta fa-solid fa-chevron-right\"></i>\n }\n </button>\n </li>\n }\n </ul>\n } @else {\n <div class=\"notifications-empty\">{{ 'TopBar-Notifications-Empty' | translate }}</div>\n }\n </div>\n }\n </div>\n}\n", styles: ["@reference \"tailwindcss\";.notifications{position:relative}.notifications .notifications-badge{position:absolute;top:-.25rem;right:-.25rem;height:1rem;min-width:1rem;border-radius:9999px;padding-left:.25rem;padding-right:.25rem;display:flex;align-items:center;justify-content:center;font-size:10px;font-weight:700;line-height:1;--tw-bg-opacity: 1;background-color:rgb(220 38 38 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.notifications .notifications-empty{padding:1.5rem .75rem;text-align:center;font-size:.875rem;line-height:1.25rem;--tw-text-opacity: 1;color:rgb(120 113 108 / var(--tw-text-opacity, 1))}.notifications .notifications-header{display:flex;align-items:center;justify-content:space-between;border-bottom-width:1px;--tw-border-opacity: 1;border-color:rgb(231 229 228 / var(--tw-border-opacity, 1));padding:.5rem .75rem;font-size:.875rem;line-height:1.25rem;font-weight:600}.notifications .notifications-list{max-height:20rem;overflow-y:auto}.notifications .notifications-list .notifications-item{border-bottom-width:1px;--tw-border-opacity: 1;border-color:rgb(245 245 244 / var(--tw-border-opacity, 1))}.notifications .notifications-list .notifications-item:last-child{border-bottom-width:0px}.notifications .notifications-list .notifications-item.unread{background-color:#e6f4ff99}.notifications .notifications-list .notifications-item .notifications-item-body{display:flex;min-width:0px;flex:1 1 0%;flex-direction:column}.notifications .notifications-list .notifications-item .notifications-item-btn{display:flex;width:100%;cursor:pointer;align-items:flex-start;gap:.625rem;padding:.5rem .75rem;text-align:left}.notifications .notifications-list .notifications-item .notifications-item-btn:hover{--tw-bg-opacity: 1;background-color:rgb(250 250 249 / var(--tw-bg-opacity, 1))}.notifications .notifications-list .notifications-item .notifications-item-cta{margin-top:.25rem;flex-shrink:0;font-size:.75rem;line-height:1rem;--tw-text-opacity: 1;color:rgb(168 162 158 / var(--tw-text-opacity, 1))}.notifications .notifications-list .notifications-item .notifications-item-description{font-size:.75rem;line-height:1rem;--tw-text-opacity: 1;color:rgb(120 113 108 / var(--tw-text-opacity, 1))}.notifications .notifications-list .notifications-item .notifications-item-icon{margin-top:.125rem;display:flex;height:1.5rem;width:1.5rem;flex-shrink:0;align-items:center;justify-content:center;border-radius:9999px;--tw-bg-opacity: 1;background-color:rgb(245 245 244 / var(--tw-bg-opacity, 1));font-size:.75rem;line-height:1rem;--tw-text-opacity: 1;color:rgb(87 83 78 / var(--tw-text-opacity, 1))}.notifications .notifications-list .notifications-item .notifications-item-title{font-size:.875rem;line-height:1.25rem;font-weight:500;--tw-text-opacity: 1;color:rgb(41 37 36 / var(--tw-text-opacity, 1))}.notifications .notifications-mark-all{font-size:.75rem;line-height:1rem;font-weight:500;--tw-text-opacity: 1;color:rgb(0 107 182 / var(--tw-text-opacity, 1))}.notifications .notifications-mark-all:hover{--tw-text-opacity: 1;color:rgb(0 87 148 / var(--tw-text-opacity, 1))}.notifications .notifications-panel{position:absolute;right:0;top:100%;z-index:50;margin-top:.25rem;width:20rem;max-width:90vw;border-radius:.375rem;border-width:1px;--tw-border-opacity: 1;border-color:rgb(231 229 228 / var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1));--tw-shadow: 0 0 4px 0 rgba(0,0,0,.2), 0 3px 20px 0 rgba(0,0,0,.19);--tw-shadow-colored: 0 0 4px 0 var(--tw-shadow-color), 0 3px 20px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.notifications .notifications-toggle{position:relative}\n"] }]
|
|
348
|
+
}], propDecorators: { dropdown: [{
|
|
214
349
|
type: ViewChild,
|
|
215
|
-
args: ['
|
|
350
|
+
args: ['dropdown']
|
|
351
|
+
}], documentClick: [{
|
|
352
|
+
type: HostListener,
|
|
353
|
+
args: ['window:click', ['$event']]
|
|
216
354
|
}] } });
|
|
217
355
|
|
|
218
356
|
class LanguageSelectorComponent {
|
|
219
357
|
//#region ViewChilds, Inputs, Outputs
|
|
220
358
|
dropdown;
|
|
359
|
+
/** When true, the toggle shows the current language's flag (light-gray button) instead of text. */
|
|
360
|
+
showFlag = false;
|
|
221
361
|
title = 'LanguageSelector-Title';
|
|
222
362
|
//#endregion
|
|
223
363
|
//#region Variables
|
|
@@ -258,18 +398,18 @@ class LanguageSelectorComponent {
|
|
|
258
398
|
}
|
|
259
399
|
}
|
|
260
400
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: LanguageSelectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
261
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "
|
|
401
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.1.6", type: LanguageSelectorComponent, isStandalone: true, selector: "shared-language-selector", inputs: { showFlag: "showFlag", title: "title" }, host: { listeners: { "window:click": "documentClick($event)" } }, viewQueries: [{ propertyName: "dropdown", first: true, predicate: ["dropdown"], descendants: true }], ngImport: i0, template: "<div #dropdown class=\"dropdown right\" [class.show]=\"showDropdown\">\n <button class=\"btn\" type=\"button\" (click)=\"onDropdownClick()\"\n [class.gray-300]=\"showFlag\" [class.lang-flag-toggle]=\"showFlag\"\n [attr.aria-label]=\"showFlag ? (title | translate) : null\">\n @if (showFlag) {\n <span class=\"flag flag-{{ selectedLanguage }}\"></span>\n } @else {\n {{ title | translate }}\n }\n </button>\n\n <ul>\n @for (lang of languages; track lang) {\n <li>\n <button type=\"button\" class=\"dropdown-item\" [class.selected]=\"lang === selectedLanguage\"\n (click)=\"onSelectLanguage(lang)\">\n <span class=\"flag flag-{{ lang }}\"></span>\n <span class=\"dropdown-label\">{{ 'Language-' + lang | translate }}</span>\n </button>\n </li>\n }\n </ul>\n</div>\n", styles: [".dropdown .btn{width:100%;font-weight:500}.dropdown .flag{display:inline-block;height:1rem;width:1.5rem;flex-shrink:0;border-radius:.125rem;background-size:cover;background-position:center;--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000);--tw-ring-color: rgb(0 0 0 / .1)}.dropdown .flag.flag-en{background-image:url('data:image/svg+xml,%3Csvg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 76 40\"%3E%3Crect width=\"76\" height=\"40\" fill=\"%23b22234\"/%3E%3Cg fill=\"%23fff\"%3E%3Crect y=\"3\" width=\"76\" height=\"3\"/%3E%3Crect y=\"9\" width=\"76\" height=\"3\"/%3E%3Crect y=\"15\" width=\"76\" height=\"3\"/%3E%3Crect y=\"22\" width=\"76\" height=\"3\"/%3E%3Crect y=\"28\" width=\"76\" height=\"3\"/%3E%3Crect y=\"34\" width=\"76\" height=\"3\"/%3E%3C/g%3E%3Crect width=\"30\" height=\"22\" fill=\"%233c3b6e\"/%3E%3C/svg%3E')}.dropdown .flag.flag-pt{background-image:url('data:image/svg+xml,%3Csvg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 70 49\"%3E%3Crect width=\"70\" height=\"49\" fill=\"%23009c3b\"/%3E%3Cpath d=\"M35 6L64 24.5L35 43L6 24.5Z\" fill=\"%23ffdf00\"/%3E%3Ccircle cx=\"35\" cy=\"24.5\" r=\"9.8\" fill=\"%23002776\"/%3E%3C/svg%3E')}.dropdown .lang-flag-toggle{display:flex;height:2.25rem;width:auto;align-items:center;justify-content:center;border-width:0px;padding-left:.625rem;padding-right:.625rem}.dropdown .lang-flag-toggle:after{border-top-color:currentColor;margin-left:.325rem}.dropdown ul{z-index:50;margin-top:.25rem;min-width:11rem;padding:.25rem;border-radius:.375rem;border-width:1px;--tw-border-opacity: 1;border-color:rgb(231 229 228 / var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1));--tw-backdrop-blur: ;-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);--tw-shadow: 0 0 4px 0 rgba(0,0,0,.2), 0 3px 20px 0 rgba(0,0,0,.19);--tw-shadow-colored: 0 0 4px 0 var(--tw-shadow-color), 0 3px 20px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.dropdown ul .dropdown-item{display:flex;width:100%;cursor:pointer;-webkit-user-select:none;user-select:none;align-items:center;gap:.5rem;border-radius:.25rem;padding:.5rem .75rem;text-align:left;font-size:.875rem;line-height:1.25rem;--tw-text-opacity: 1;color:rgb(68 64 60 / var(--tw-text-opacity, 1))}.dropdown ul .dropdown-item:hover{--tw-bg-opacity: 1;background-color:rgb(245 245 244 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(68 64 60 / var(--tw-text-opacity, 1))}.dropdown ul .dropdown-item:focus{--tw-bg-opacity: 1;background-color:rgb(245 245 244 / var(--tw-bg-opacity, 1));outline:2px solid transparent;outline-offset:2px}.dropdown ul .dropdown-item:active{--tw-bg-opacity: 1;background-color:rgb(231 229 228 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(68 64 60 / var(--tw-text-opacity, 1))}.dropdown ul .dropdown-item.selected{--tw-bg-opacity: 1;background-color:rgb(230 244 255 / var(--tw-bg-opacity, 1));font-weight:600;--tw-text-opacity: 1;color:rgb(0 47 80 / var(--tw-text-opacity, 1))}.dropdown ul .dropdown-item.selected:hover{--tw-bg-opacity: 1;background-color:rgb(154 202 237 / var(--tw-bg-opacity, 1))}\n"], dependencies: [{ kind: "pipe", type: TranslatePipe, name: "translate" }] });
|
|
262
402
|
}
|
|
263
403
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: LanguageSelectorComponent, decorators: [{
|
|
264
404
|
type: Component,
|
|
265
405
|
args: [{ selector: 'shared-language-selector', imports: [
|
|
266
|
-
NgClass,
|
|
267
|
-
NgFor,
|
|
268
406
|
TranslatePipe,
|
|
269
|
-
], template: "<div #dropdown class=\"dropdown right\" [class.show]=\"showDropdown\">\n <button class=\"btn\" type=\"button\" (click)=\"onDropdownClick()\">\n {{ title | translate }}\n </button>\n\n <ul>\n
|
|
407
|
+
], template: "<div #dropdown class=\"dropdown right\" [class.show]=\"showDropdown\">\n <button class=\"btn\" type=\"button\" (click)=\"onDropdownClick()\"\n [class.gray-300]=\"showFlag\" [class.lang-flag-toggle]=\"showFlag\"\n [attr.aria-label]=\"showFlag ? (title | translate) : null\">\n @if (showFlag) {\n <span class=\"flag flag-{{ selectedLanguage }}\"></span>\n } @else {\n {{ title | translate }}\n }\n </button>\n\n <ul>\n @for (lang of languages; track lang) {\n <li>\n <button type=\"button\" class=\"dropdown-item\" [class.selected]=\"lang === selectedLanguage\"\n (click)=\"onSelectLanguage(lang)\">\n <span class=\"flag flag-{{ lang }}\"></span>\n <span class=\"dropdown-label\">{{ 'Language-' + lang | translate }}</span>\n </button>\n </li>\n }\n </ul>\n</div>\n", styles: [".dropdown .btn{width:100%;font-weight:500}.dropdown .flag{display:inline-block;height:1rem;width:1.5rem;flex-shrink:0;border-radius:.125rem;background-size:cover;background-position:center;--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000);--tw-ring-color: rgb(0 0 0 / .1)}.dropdown .flag.flag-en{background-image:url('data:image/svg+xml,%3Csvg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 76 40\"%3E%3Crect width=\"76\" height=\"40\" fill=\"%23b22234\"/%3E%3Cg fill=\"%23fff\"%3E%3Crect y=\"3\" width=\"76\" height=\"3\"/%3E%3Crect y=\"9\" width=\"76\" height=\"3\"/%3E%3Crect y=\"15\" width=\"76\" height=\"3\"/%3E%3Crect y=\"22\" width=\"76\" height=\"3\"/%3E%3Crect y=\"28\" width=\"76\" height=\"3\"/%3E%3Crect y=\"34\" width=\"76\" height=\"3\"/%3E%3C/g%3E%3Crect width=\"30\" height=\"22\" fill=\"%233c3b6e\"/%3E%3C/svg%3E')}.dropdown .flag.flag-pt{background-image:url('data:image/svg+xml,%3Csvg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 70 49\"%3E%3Crect width=\"70\" height=\"49\" fill=\"%23009c3b\"/%3E%3Cpath d=\"M35 6L64 24.5L35 43L6 24.5Z\" fill=\"%23ffdf00\"/%3E%3Ccircle cx=\"35\" cy=\"24.5\" r=\"9.8\" fill=\"%23002776\"/%3E%3C/svg%3E')}.dropdown .lang-flag-toggle{display:flex;height:2.25rem;width:auto;align-items:center;justify-content:center;border-width:0px;padding-left:.625rem;padding-right:.625rem}.dropdown .lang-flag-toggle:after{border-top-color:currentColor;margin-left:.325rem}.dropdown ul{z-index:50;margin-top:.25rem;min-width:11rem;padding:.25rem;border-radius:.375rem;border-width:1px;--tw-border-opacity: 1;border-color:rgb(231 229 228 / var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1));--tw-backdrop-blur: ;-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);--tw-shadow: 0 0 4px 0 rgba(0,0,0,.2), 0 3px 20px 0 rgba(0,0,0,.19);--tw-shadow-colored: 0 0 4px 0 var(--tw-shadow-color), 0 3px 20px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.dropdown ul .dropdown-item{display:flex;width:100%;cursor:pointer;-webkit-user-select:none;user-select:none;align-items:center;gap:.5rem;border-radius:.25rem;padding:.5rem .75rem;text-align:left;font-size:.875rem;line-height:1.25rem;--tw-text-opacity: 1;color:rgb(68 64 60 / var(--tw-text-opacity, 1))}.dropdown ul .dropdown-item:hover{--tw-bg-opacity: 1;background-color:rgb(245 245 244 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(68 64 60 / var(--tw-text-opacity, 1))}.dropdown ul .dropdown-item:focus{--tw-bg-opacity: 1;background-color:rgb(245 245 244 / var(--tw-bg-opacity, 1));outline:2px solid transparent;outline-offset:2px}.dropdown ul .dropdown-item:active{--tw-bg-opacity: 1;background-color:rgb(231 229 228 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(68 64 60 / var(--tw-text-opacity, 1))}.dropdown ul .dropdown-item.selected{--tw-bg-opacity: 1;background-color:rgb(230 244 255 / var(--tw-bg-opacity, 1));font-weight:600;--tw-text-opacity: 1;color:rgb(0 47 80 / var(--tw-text-opacity, 1))}.dropdown ul .dropdown-item.selected:hover{--tw-bg-opacity: 1;background-color:rgb(154 202 237 / var(--tw-bg-opacity, 1))}\n"] }]
|
|
270
408
|
}], propDecorators: { dropdown: [{
|
|
271
409
|
type: ViewChild,
|
|
272
410
|
args: ['dropdown']
|
|
411
|
+
}], showFlag: [{
|
|
412
|
+
type: Input
|
|
273
413
|
}], title: [{
|
|
274
414
|
type: Input
|
|
275
415
|
}], documentClick: [{
|
|
@@ -277,6 +417,140 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImpor
|
|
|
277
417
|
args: ['window:click', ['$event']]
|
|
278
418
|
}] } });
|
|
279
419
|
|
|
420
|
+
class UserProfileComponent {
|
|
421
|
+
//#region Variables
|
|
422
|
+
authenticationService = inject(AuthenticationService);
|
|
423
|
+
user = null;
|
|
424
|
+
//#endregion
|
|
425
|
+
//#region Properties
|
|
426
|
+
/** Up to two uppercase initials derived from the user's name, used when no picture is set. */
|
|
427
|
+
get initials() {
|
|
428
|
+
const parts = this.name.trim().split(/\s+/).filter((p) => p.length > 0);
|
|
429
|
+
if (parts.length === 0) {
|
|
430
|
+
return '';
|
|
431
|
+
}
|
|
432
|
+
const first = parts[0].charAt(0);
|
|
433
|
+
const last = parts.length > 1 ? parts[parts.length - 1].charAt(0) : '';
|
|
434
|
+
return (first + last).toUpperCase();
|
|
435
|
+
}
|
|
436
|
+
get name() {
|
|
437
|
+
return this.user?.name ?? '';
|
|
438
|
+
}
|
|
439
|
+
get pictureUrl() {
|
|
440
|
+
return this.user?.pictureUrl;
|
|
441
|
+
}
|
|
442
|
+
get position() {
|
|
443
|
+
return this.user?.position ?? '';
|
|
444
|
+
}
|
|
445
|
+
//#endregion
|
|
446
|
+
//#region Constructor and Angular life cycle methods
|
|
447
|
+
ngOnInit() {
|
|
448
|
+
this.user = this.authenticationService.getUserInfo();
|
|
449
|
+
}
|
|
450
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: UserProfileComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
451
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.1.6", type: UserProfileComponent, isStandalone: true, selector: "shared-user-profile", ngImport: i0, template: "<div class=\"user-profile\">\n <div class=\"user-text\">\n <span class=\"user-name\">{{ name }}</span>\n @if (position) {\n <span class=\"user-position\">{{ position }}</span>\n }\n </div>\n\n <div class=\"user-avatar\">\n @if (pictureUrl) {\n <img class=\"user-avatar-img\" [src]=\"pictureUrl\" [alt]=\"name\" />\n } @else {\n <span class=\"user-avatar-initials\">{{ initials }}</span>\n }\n </div>\n</div>\n", styles: ["@reference \"tailwindcss\";@media (max-width: 767px){:host{display:none}}.user-profile{display:flex;height:2.25rem;min-width:0px;-webkit-user-select:none;user-select:none;align-items:center;gap:.5rem;border-radius:.25rem;padding-left:.5rem;padding-right:.5rem;background-color:#0000000d;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.user-profile:hover{background-color:#0000001a}.user-profile .user-avatar{display:flex;height:1.75rem;width:1.75rem;flex-shrink:0;align-items:center;justify-content:center;overflow:hidden;border-radius:9999px;--tw-bg-opacity: 1;background-color:rgb(0 87 148 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.user-profile .user-avatar-img{height:100%;width:100%;object-fit:cover}.user-profile .user-avatar-initials{font-size:.75rem;line-height:1rem;font-weight:600;text-transform:uppercase}.user-profile .user-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:.875rem;font-weight:500;line-height:1rem;--tw-text-opacity: 1;color:rgb(41 37 36 / var(--tw-text-opacity, 1))}.user-profile .user-position{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:.75rem;line-height:1rem;--tw-text-opacity: 1;color:rgb(120 113 108 / var(--tw-text-opacity, 1))}.user-profile .user-text{display:flex;min-width:0px;flex-direction:column;text-align:right}\n"] });
|
|
452
|
+
}
|
|
453
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: UserProfileComponent, decorators: [{
|
|
454
|
+
type: Component,
|
|
455
|
+
args: [{ selector: 'shared-user-profile', template: "<div class=\"user-profile\">\n <div class=\"user-text\">\n <span class=\"user-name\">{{ name }}</span>\n @if (position) {\n <span class=\"user-position\">{{ position }}</span>\n }\n </div>\n\n <div class=\"user-avatar\">\n @if (pictureUrl) {\n <img class=\"user-avatar-img\" [src]=\"pictureUrl\" [alt]=\"name\" />\n } @else {\n <span class=\"user-avatar-initials\">{{ initials }}</span>\n }\n </div>\n</div>\n", styles: ["@reference \"tailwindcss\";@media (max-width: 767px){:host{display:none}}.user-profile{display:flex;height:2.25rem;min-width:0px;-webkit-user-select:none;user-select:none;align-items:center;gap:.5rem;border-radius:.25rem;padding-left:.5rem;padding-right:.5rem;background-color:#0000000d;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.user-profile:hover{background-color:#0000001a}.user-profile .user-avatar{display:flex;height:1.75rem;width:1.75rem;flex-shrink:0;align-items:center;justify-content:center;overflow:hidden;border-radius:9999px;--tw-bg-opacity: 1;background-color:rgb(0 87 148 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.user-profile .user-avatar-img{height:100%;width:100%;object-fit:cover}.user-profile .user-avatar-initials{font-size:.75rem;line-height:1rem;font-weight:600;text-transform:uppercase}.user-profile .user-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:.875rem;font-weight:500;line-height:1rem;--tw-text-opacity: 1;color:rgb(41 37 36 / var(--tw-text-opacity, 1))}.user-profile .user-position{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:.75rem;line-height:1rem;--tw-text-opacity: 1;color:rgb(120 113 108 / var(--tw-text-opacity, 1))}.user-profile .user-text{display:flex;min-width:0px;flex-direction:column;text-align:right}\n"] }]
|
|
456
|
+
}] });
|
|
457
|
+
|
|
458
|
+
class TopBarComponent {
|
|
459
|
+
//#region ViewChilds, Inputs, Outputs
|
|
460
|
+
/** Emitted when the logout button is clicked. */
|
|
461
|
+
logout = new EventEmitter();
|
|
462
|
+
//#endregion
|
|
463
|
+
//#region Event handlers
|
|
464
|
+
onLogoutClick() {
|
|
465
|
+
this.logout.emit();
|
|
466
|
+
}
|
|
467
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: TopBarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
468
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.6", type: TopBarComponent, isStandalone: true, selector: "shared-top-bar", outputs: { logout: "logout" }, ngImport: i0, template: "<div class=\"top-bar\">\n <div class=\"top-bar-section top-bar-start\">\n <shared-brand></shared-brand>\n <shared-environment-badge></shared-environment-badge>\n </div>\n\n <div class=\"top-bar-section top-bar-end\">\n <shared-notifications></shared-notifications>\n <shared-language-selector [showFlag]=\"true\"></shared-language-selector>\n\n <!-- Theme switch slot (deferred \u2014 see 2026-07-23-top-bar-update-design.md) -->\n\n <shared-user-profile></shared-user-profile>\n\n <button class=\"btn gray-300 top-bar-logout\" type=\"button\" (click)=\"onLogoutClick()\"\n [attr.aria-label]=\"'Main-Logout' | translate\">\n <i class=\"fa-solid fa-arrow-right-from-bracket\"></i>\n </button>\n </div>\n</div>\n", styles: ["@reference \"tailwindcss\";.top-bar{display:flex;width:100%;align-items:center;justify-content:space-between;gap:.75rem}.top-bar .top-bar-end{flex-shrink:0}.top-bar .top-bar-section{display:flex;min-width:0px;align-items:center;gap:.75rem}.top-bar .top-bar-start{min-width:0px}\n"], dependencies: [{ kind: "component", type: BrandComponent, selector: "shared-brand" }, { kind: "component", type: EnvironmentBadgeComponent, selector: "shared-environment-badge" }, { kind: "component", type: LanguageSelectorComponent, selector: "shared-language-selector", inputs: ["showFlag", "title"] }, { kind: "component", type: NotificationsComponent, selector: "shared-notifications" }, { kind: "pipe", type: TranslatePipe, name: "translate" }, { kind: "component", type: UserProfileComponent, selector: "shared-user-profile" }] });
|
|
469
|
+
}
|
|
470
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: TopBarComponent, decorators: [{
|
|
471
|
+
type: Component,
|
|
472
|
+
args: [{ selector: 'shared-top-bar', imports: [
|
|
473
|
+
BrandComponent,
|
|
474
|
+
EnvironmentBadgeComponent,
|
|
475
|
+
LanguageSelectorComponent,
|
|
476
|
+
NotificationsComponent,
|
|
477
|
+
TranslatePipe,
|
|
478
|
+
UserProfileComponent,
|
|
479
|
+
], template: "<div class=\"top-bar\">\n <div class=\"top-bar-section top-bar-start\">\n <shared-brand></shared-brand>\n <shared-environment-badge></shared-environment-badge>\n </div>\n\n <div class=\"top-bar-section top-bar-end\">\n <shared-notifications></shared-notifications>\n <shared-language-selector [showFlag]=\"true\"></shared-language-selector>\n\n <!-- Theme switch slot (deferred \u2014 see 2026-07-23-top-bar-update-design.md) -->\n\n <shared-user-profile></shared-user-profile>\n\n <button class=\"btn gray-300 top-bar-logout\" type=\"button\" (click)=\"onLogoutClick()\"\n [attr.aria-label]=\"'Main-Logout' | translate\">\n <i class=\"fa-solid fa-arrow-right-from-bracket\"></i>\n </button>\n </div>\n</div>\n", styles: ["@reference \"tailwindcss\";.top-bar{display:flex;width:100%;align-items:center;justify-content:space-between;gap:.75rem}.top-bar .top-bar-end{flex-shrink:0}.top-bar .top-bar-section{display:flex;min-width:0px;align-items:center;gap:.75rem}.top-bar .top-bar-start{min-width:0px}\n"] }]
|
|
480
|
+
}], propDecorators: { logout: [{
|
|
481
|
+
type: Output
|
|
482
|
+
}] } });
|
|
483
|
+
|
|
484
|
+
class MainLayoutComponent {
|
|
485
|
+
//#region ViewChilds, Inputs, Outputs
|
|
486
|
+
logoutModal;
|
|
487
|
+
//#endregion
|
|
488
|
+
//#region Variables
|
|
489
|
+
authenticationService = inject(AuthenticationService);
|
|
490
|
+
destroy$ = new Subject();
|
|
491
|
+
router = inject(Router);
|
|
492
|
+
sidebarService = inject(SidebarService);
|
|
493
|
+
tabService = inject(TabService);
|
|
494
|
+
/** Application version (from AppConfig) projected into the sidebar footer. */
|
|
495
|
+
appVersion = inject(APP_CONFIG).version;
|
|
496
|
+
//#endregion
|
|
497
|
+
//#region Properties
|
|
498
|
+
//#endregion
|
|
499
|
+
//#region Constructor and Angular life cycle methods
|
|
500
|
+
ngOnDestroy() {
|
|
501
|
+
this.destroy$.next(true);
|
|
502
|
+
this.destroy$.complete();
|
|
503
|
+
}
|
|
504
|
+
ngOnInit() {
|
|
505
|
+
const url = this.router.url;
|
|
506
|
+
if (url !== '/' && !this.tabService.isUrlOpen(url)) {
|
|
507
|
+
this.sidebarService.getMenuFromUrl(url)
|
|
508
|
+
.pipe(take(1))
|
|
509
|
+
.subscribe((item) => {
|
|
510
|
+
if (!!item) {
|
|
511
|
+
this.tabService.updateTabTitle(url, item.label);
|
|
512
|
+
}
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
this.sidebarService.menuUrlSelected
|
|
516
|
+
.pipe(takeUntil(this.destroy$))
|
|
517
|
+
.subscribe((item) => {
|
|
518
|
+
const tab = new Tab({
|
|
519
|
+
title: item.label,
|
|
520
|
+
url: item.url,
|
|
521
|
+
});
|
|
522
|
+
this.tabService.openTab(tab);
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
//#endregion
|
|
526
|
+
//#region Event handlers
|
|
527
|
+
//#endregion
|
|
528
|
+
//#region Public methods
|
|
529
|
+
onLogoutClick() {
|
|
530
|
+
this.logoutModal.toggleModal();
|
|
531
|
+
}
|
|
532
|
+
onLogoutConfirm() {
|
|
533
|
+
this.authenticationService.signOut();
|
|
534
|
+
this.router.navigate(['/login']);
|
|
535
|
+
}
|
|
536
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: MainLayoutComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
537
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.1.6", type: MainLayoutComponent, isStandalone: true, selector: "shared-main-layout", viewQueries: [{ propertyName: "logoutModal", first: true, predicate: ["logoutModal"], descendants: true }], ngImport: i0, template: "<div class=\"main-container\">\n <lib-sidebar>\n @if (appVersion) {\n <span>v{{ appVersion }}</span>\n }\n </lib-sidebar>\n\n <shared-top-bar class=\"toolbar\" (logout)=\"onLogoutClick()\"></shared-top-bar>\n\n <div class=\"content\">\n <framework-tabs>\n <router-outlet></router-outlet>\n </framework-tabs>\n </div>\n</div>\n\n<lib-modal #logoutModal position=\"top\" size=\"xl\" [closeButtonText]=\"'Main-Logout-Modal-Message-Cancel' | translate\">\n <div body>\n <h5>{{ 'Main-Logout-Modal-Message' | translate }}</h5>\n </div>\n <div footer>\n <button type=\"button\" class=\"btn red-600\" (click)=\"onLogoutConfirm()\">{{ 'Main-Logout-Modal-Message-Confirm' | translate }}</button>\n </div>\n</lib-modal>", styles: ["@reference \"tailwindcss\";.main-container{display:grid;height:100vh;max-height:100vh;width:100vw;max-width:100%;--tw-bg-opacity: 1;background-color:rgb(209 213 219 / var(--tw-bg-opacity, 1));grid-template-columns:auto minmax(0,1fr);grid-template-rows:auto minmax(0,1fr);grid-template-areas:\"toolbar toolbar\" \"sidebar content\"}.main-container lib-sidebar{--tw-shadow: 0 0 4px 0 rgba(0,0,0,.2), 0 3px 20px 0 rgba(0,0,0,.19);--tw-shadow-colored: 0 0 4px 0 var(--tw-shadow-color), 0 3px 20px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow);grid-area:sidebar}.main-container .toolbar{display:flex;justify-content:space-between;border-bottom-width:1px;--tw-border-opacity: 1;border-color:rgb(214 211 209 / var(--tw-border-opacity, 1));background-color:#ffffffe6;padding:.5rem;--tw-shadow: 0 0 4px 0 rgba(0,0,0,.2), 0 3px 20px 0 rgba(0,0,0,.19);--tw-shadow-colored: 0 0 4px 0 var(--tw-shadow-color), 0 3px 20px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow);grid-area:toolbar}.main-container .content{overflow-y:auto;padding:.75rem .75rem .5rem;grid-area:content}.main-container framework-tabs{height:100%}lib-modal h5{margin-bottom:.5rem;font-size:1.125rem;font-weight:500;line-height:1.25rem}\n"], dependencies: [{ kind: "component", type: ModalComponent, selector: "lib-modal", inputs: ["closeButtonText", "dialog", "modalProcessing", "position", "size", "title"], outputs: ["closed"] }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1.RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "component", type: SidebarComponent, selector: "lib-sidebar" }, { kind: "component", type: TabsComponent, selector: "framework-tabs" }, { kind: "component", type: TopBarComponent, selector: "shared-top-bar", outputs: ["logout"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
|
|
538
|
+
}
|
|
539
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: MainLayoutComponent, decorators: [{
|
|
540
|
+
type: Component,
|
|
541
|
+
args: [{ selector: 'shared-main-layout', imports: [
|
|
542
|
+
ModalComponent,
|
|
543
|
+
RouterModule,
|
|
544
|
+
SidebarComponent,
|
|
545
|
+
TabsComponent,
|
|
546
|
+
TopBarComponent,
|
|
547
|
+
TranslatePipe,
|
|
548
|
+
], template: "<div class=\"main-container\">\n <lib-sidebar>\n @if (appVersion) {\n <span>v{{ appVersion }}</span>\n }\n </lib-sidebar>\n\n <shared-top-bar class=\"toolbar\" (logout)=\"onLogoutClick()\"></shared-top-bar>\n\n <div class=\"content\">\n <framework-tabs>\n <router-outlet></router-outlet>\n </framework-tabs>\n </div>\n</div>\n\n<lib-modal #logoutModal position=\"top\" size=\"xl\" [closeButtonText]=\"'Main-Logout-Modal-Message-Cancel' | translate\">\n <div body>\n <h5>{{ 'Main-Logout-Modal-Message' | translate }}</h5>\n </div>\n <div footer>\n <button type=\"button\" class=\"btn red-600\" (click)=\"onLogoutConfirm()\">{{ 'Main-Logout-Modal-Message-Confirm' | translate }}</button>\n </div>\n</lib-modal>", styles: ["@reference \"tailwindcss\";.main-container{display:grid;height:100vh;max-height:100vh;width:100vw;max-width:100%;--tw-bg-opacity: 1;background-color:rgb(209 213 219 / var(--tw-bg-opacity, 1));grid-template-columns:auto minmax(0,1fr);grid-template-rows:auto minmax(0,1fr);grid-template-areas:\"toolbar toolbar\" \"sidebar content\"}.main-container lib-sidebar{--tw-shadow: 0 0 4px 0 rgba(0,0,0,.2), 0 3px 20px 0 rgba(0,0,0,.19);--tw-shadow-colored: 0 0 4px 0 var(--tw-shadow-color), 0 3px 20px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow);grid-area:sidebar}.main-container .toolbar{display:flex;justify-content:space-between;border-bottom-width:1px;--tw-border-opacity: 1;border-color:rgb(214 211 209 / var(--tw-border-opacity, 1));background-color:#ffffffe6;padding:.5rem;--tw-shadow: 0 0 4px 0 rgba(0,0,0,.2), 0 3px 20px 0 rgba(0,0,0,.19);--tw-shadow-colored: 0 0 4px 0 var(--tw-shadow-color), 0 3px 20px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow);grid-area:toolbar}.main-container .content{overflow-y:auto;padding:.75rem .75rem .5rem;grid-area:content}.main-container framework-tabs{height:100%}lib-modal h5{margin-bottom:.5rem;font-size:1.125rem;font-weight:500;line-height:1.25rem}\n"] }]
|
|
549
|
+
}], propDecorators: { logoutModal: [{
|
|
550
|
+
type: ViewChild,
|
|
551
|
+
args: ['logoutModal']
|
|
552
|
+
}] } });
|
|
553
|
+
|
|
280
554
|
class LoginComponent {
|
|
281
555
|
//#region ViewChilds, Inputs, Outputs
|
|
282
556
|
username;
|
|
@@ -341,7 +615,7 @@ class LoginComponent {
|
|
|
341
615
|
}
|
|
342
616
|
}
|
|
343
617
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: LoginComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
344
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.6", type: LoginComponent, isStandalone: true, selector: "shared-login", viewQueries: [{ propertyName: "username", first: true, predicate: ["username"], descendants: true }], ngImport: i0, template: "<div class=\"content\">\n <div class=\"login\">\n <div class=\"logo-container\">\n <div class=\"logo\">\n <img src=\"/logo-login.png\" alt=\"Logo\" />\n </div>\n </div>\n \n <h3 class=\"title\">{{ 'Login-Title' | translate }}</h3>\n\n <form [formGroup]=\"form\" (ngSubmit)=\"onSubmit()\">\n\n <div class=\"validations\" [class.hidden]=\"!formState.error\">\n \n <div *ngIf=\"formState.error === 'invalid'\">\n {{'Login-Errors-Invalid' | translate}}\n </div>\n \n <div *ngIf=\"formState.error === 'internalServerError'\">{{'Login-Errors-InternalServerError' | translate}}</div>\n </div>\n\n <div class=\"input-group inline\">\n <input type=\"text\" #username id=\"username\" formControlName=\"username\" class=\"form-input peer\" [placeholder]=\"'Login-Username' | translate\" autofocus />\n\n <div class=\"input-group-text\">\n <label for=\"username\">\n <i class=\"fas fa-user fa-fw\"></i>\n </label>\n </div>\n\n <div class=\"validations\">\n <span *ngIf=\"form.get('username')?.hasError('required')\">{{ 'Login-Username-Required' | translate }}</span>\n </div>\n </div>\n \n <div class=\"input-group inline\">\n <input type=\"password\" id=\"password\" formControlName=\"password\" class=\"form-input\" [placeholder]=\"'Login-Password' | translate\" />\n\n <div class=\"input-group-text\">\n <label for=\"password\">\n <i class=\"fas fa-key fa-fw\"></i>\n </label>\n </div>\n \n <div class=\"validations\">\n <span *ngIf=\"form.get('password')?.hasError('required')\">{{ 'Login-Password-Required' | translate }}</span>\n </div>\n </div>\n \n <div class=\"options\">\n <div>\n <input class=\"form-input\" id=\"rememberMe\" formControlName=\"rememberMe\" type=\"checkbox\">\n <label for=\"rememberMe\">{{ 'Login-RememberMe' | translate }}</label>\n </div>\n \n <shared-language-selector></shared-language-selector>\n </div>\n \n <div class=\"buttons\">\n <button type=\"submit\" class=\"btn\"\n [class.loading]=\"formState.loading && !formState.success && !formState.error\"\n [class.error]=\"formState.error\"\n [class.success]=\"formState.success\">\n <i *ngIf=\"!formState.loading\" class=\"fas fa-sign-in-alt fa-fw\"></i>\n {{ 'Login-SignIn' | translate }}\n </button>\n </div>\n </form>\n </div>\n\n <div class=\"footer\">\n <span [innerHTML]=\"'Login-Copyright' | translate\"></span>\n <span class=\"design\">{{ 'Login-DesignBy' | translate }}</span>\n </div>\n</div>", styles: [":host{display:block}.content{margin-left:auto;margin-right:auto;width:83.333333%;border-radius:.5rem}@media (min-width: 768px){.content{width:66.666667%}}@media (min-width: 1280px){.content{width:41.666667%}}.content>:not([hidden])~:not([hidden]){--tw-divide-y-reverse: 0;border-top-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px * var(--tw-divide-y-reverse));--tw-divide-opacity: 1;border-color:rgb(161 161 170 / var(--tw-divide-opacity, 1))}.content{--tw-shadow: 0 4px 8px 0 rgba(0,0,0,.2),0 6px 20px 0 rgba(0,0,0,.19);--tw-shadow-colored: 0 4px 8px 0 var(--tw-shadow-color), 0 6px 20px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.content .login{position:relative;z-index:10;margin-top:5rem;border-top-left-radius:.5rem;border-top-right-radius:.5rem;background-color:#fff9;padding:5rem 1.5rem 1.5rem;--tw-backdrop-blur: blur(12px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.content .login .logo-container{margin-top:-10rem;margin-left:auto;margin-right:auto;margin-bottom:1rem;width:fit-content;border-radius:1.5rem;padding:.5rem;--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1));--tw-shadow: 0 4px 8px 0 rgba(0,0,0,.2),0 6px 20px 0 rgba(0,0,0,.19);--tw-shadow-colored: 0 4px 8px 0 var(--tw-shadow-color), 0 6px 20px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.content .login .logo-container .logo{width:fit-content;border-radius:1.5rem;padding:.5rem;border-width:2px;--tw-border-opacity: 1;border-color:rgb(252 211 77 / var(--tw-border-opacity, 1))}.content .login .logo-container .logo img{height:10rem;min-width:fit-content}.content .login .title{padding-bottom:1.25rem;text-align:center;font-size:1.875rem;line-height:2.25rem;font-weight:700}.content .login form>.validations{text-align:center;font-weight:700;--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity, 1))}.content .login form>.validations:after{margin-left:auto;margin-right:auto;margin-bottom:.5rem;display:block;border-bottom-width:1px;--tw-border-opacity: 1;border-color:rgb(239 68 68 / var(--tw-border-opacity, 1));--tw-content: \"\";content:var(--tw-content);animation:separator 1s ease-in-out forwards}.content .login form .options{margin-bottom:.75rem;display:flex;flex-direction:row;flex-wrap:wrap-reverse;justify-content:space-between}.content .login form .options shared-language-selector{margin-bottom:.75rem;width:100%}@media (min-width: 640px){.content .login form .options shared-language-selector{margin-bottom:0;width:auto}}.content .login form .buttons{display:flex;justify-content:center;gap:1rem}.content .footer{border-bottom-right-radius:.5rem;border-bottom-left-radius:.5rem;padding:.125rem .5rem;background-color:#e2e8f099;--tw-backdrop-blur: blur(12px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);font-size:.875rem;line-height:1.25rem;font-weight:600;letter-spacing:-.025em;display:flex;flex-wrap:wrap;justify-content:space-between}@keyframes separator{0%{width:0}to{width:100%}}\n"], dependencies: [{ kind: "component", type: LanguageSelectorComponent, selector: "shared-language-selector", inputs: ["title"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: RouterModule }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
|
|
618
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.6", type: LoginComponent, isStandalone: true, selector: "shared-login", viewQueries: [{ propertyName: "username", first: true, predicate: ["username"], descendants: true }], ngImport: i0, template: "<div class=\"content\">\n <div class=\"login\">\n <div class=\"logo-container\">\n <div class=\"logo\">\n <img src=\"/logo-login.png\" alt=\"Logo\" />\n </div>\n </div>\n \n <h3 class=\"title\">{{ 'Login-Title' | translate }}</h3>\n\n <form [formGroup]=\"form\" (ngSubmit)=\"onSubmit()\">\n\n <div class=\"validations\" [class.hidden]=\"!formState.error\">\n \n <div *ngIf=\"formState.error === 'invalid'\">\n {{'Login-Errors-Invalid' | translate}}\n </div>\n \n <div *ngIf=\"formState.error === 'internalServerError'\">{{'Login-Errors-InternalServerError' | translate}}</div>\n </div>\n\n <div class=\"input-group inline\">\n <input type=\"text\" #username id=\"username\" formControlName=\"username\" class=\"form-input peer\" [placeholder]=\"'Login-Username' | translate\" autofocus />\n\n <div class=\"input-group-text\">\n <label for=\"username\">\n <i class=\"fas fa-user fa-fw\"></i>\n </label>\n </div>\n\n <div class=\"validations\">\n <span *ngIf=\"form.get('username')?.hasError('required')\">{{ 'Login-Username-Required' | translate }}</span>\n </div>\n </div>\n \n <div class=\"input-group inline\">\n <input type=\"password\" id=\"password\" formControlName=\"password\" class=\"form-input\" [placeholder]=\"'Login-Password' | translate\" />\n\n <div class=\"input-group-text\">\n <label for=\"password\">\n <i class=\"fas fa-key fa-fw\"></i>\n </label>\n </div>\n \n <div class=\"validations\">\n <span *ngIf=\"form.get('password')?.hasError('required')\">{{ 'Login-Password-Required' | translate }}</span>\n </div>\n </div>\n \n <div class=\"options\">\n <div>\n <input class=\"form-input\" id=\"rememberMe\" formControlName=\"rememberMe\" type=\"checkbox\">\n <label for=\"rememberMe\">{{ 'Login-RememberMe' | translate }}</label>\n </div>\n \n <shared-language-selector></shared-language-selector>\n </div>\n \n <div class=\"buttons\">\n <button type=\"submit\" class=\"btn\"\n [class.loading]=\"formState.loading && !formState.success && !formState.error\"\n [class.error]=\"formState.error\"\n [class.success]=\"formState.success\">\n <i *ngIf=\"!formState.loading\" class=\"fas fa-sign-in-alt fa-fw\"></i>\n {{ 'Login-SignIn' | translate }}\n </button>\n </div>\n </form>\n </div>\n\n <div class=\"footer\">\n <span [innerHTML]=\"'Login-Copyright' | translate\"></span>\n <span class=\"design\">{{ 'Login-DesignBy' | translate }}</span>\n </div>\n</div>", styles: [":host{display:block}.content{margin-left:auto;margin-right:auto;width:83.333333%;border-radius:.5rem}@media (min-width: 768px){.content{width:66.666667%}}@media (min-width: 1280px){.content{width:41.666667%}}.content>:not([hidden])~:not([hidden]){--tw-divide-y-reverse: 0;border-top-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px * var(--tw-divide-y-reverse));--tw-divide-opacity: 1;border-color:rgb(161 161 170 / var(--tw-divide-opacity, 1))}.content{--tw-shadow: 0 4px 8px 0 rgba(0,0,0,.2),0 6px 20px 0 rgba(0,0,0,.19);--tw-shadow-colored: 0 4px 8px 0 var(--tw-shadow-color), 0 6px 20px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.content .login{position:relative;z-index:10;margin-top:5rem;border-top-left-radius:.5rem;border-top-right-radius:.5rem;background-color:#fff9;padding:5rem 1.5rem 1.5rem;--tw-backdrop-blur: blur(12px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.content .login .logo-container{margin-top:-10rem;margin-left:auto;margin-right:auto;margin-bottom:1rem;width:fit-content;border-radius:1.5rem;padding:.5rem;--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1));--tw-shadow: 0 4px 8px 0 rgba(0,0,0,.2),0 6px 20px 0 rgba(0,0,0,.19);--tw-shadow-colored: 0 4px 8px 0 var(--tw-shadow-color), 0 6px 20px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.content .login .logo-container .logo{width:fit-content;border-radius:1.5rem;padding:.5rem;border-width:2px;--tw-border-opacity: 1;border-color:rgb(252 211 77 / var(--tw-border-opacity, 1))}.content .login .logo-container .logo img{height:10rem;min-width:fit-content}.content .login .title{padding-bottom:1.25rem;text-align:center;font-size:1.875rem;line-height:2.25rem;font-weight:700}.content .login form>.validations{text-align:center;font-weight:700;--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity, 1))}.content .login form>.validations:after{margin-left:auto;margin-right:auto;margin-bottom:.5rem;display:block;border-bottom-width:1px;--tw-border-opacity: 1;border-color:rgb(239 68 68 / var(--tw-border-opacity, 1));--tw-content: \"\";content:var(--tw-content);animation:separator 1s ease-in-out forwards}.content .login form .options{margin-bottom:.75rem;display:flex;flex-direction:row;flex-wrap:wrap-reverse;justify-content:space-between}.content .login form .options shared-language-selector{margin-bottom:.75rem;width:100%}@media (min-width: 640px){.content .login form .options shared-language-selector{margin-bottom:0;width:auto}}.content .login form .buttons{display:flex;justify-content:center;gap:1rem}.content .footer{border-bottom-right-radius:.5rem;border-bottom-left-radius:.5rem;padding:.125rem .5rem;background-color:#e2e8f099;--tw-backdrop-blur: blur(12px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);font-size:.875rem;line-height:1.25rem;font-weight:600;letter-spacing:-.025em;display:flex;flex-wrap:wrap;justify-content:space-between}@keyframes separator{0%{width:0}to{width:100%}}\n"], dependencies: [{ kind: "component", type: LanguageSelectorComponent, selector: "shared-language-selector", inputs: ["showFlag", "title"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: RouterModule }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
|
|
345
619
|
}
|
|
346
620
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: LoginComponent, decorators: [{
|
|
347
621
|
type: Component,
|
|
@@ -860,6 +1134,7 @@ const ZAMBON_SHARED_I18N_RESOURCES = [
|
|
|
860
1134
|
{ prefix: `/${ZAMBON_SHARED_I18N_ASSET_PATH}/language-selector/`, suffix: '.json' },
|
|
861
1135
|
{ prefix: `/${ZAMBON_SHARED_I18N_ASSET_PATH}/login/`, suffix: '.json' },
|
|
862
1136
|
{ prefix: `/${ZAMBON_SHARED_I18N_ASSET_PATH}/operations-history/`, suffix: '.json' },
|
|
1137
|
+
{ prefix: `/${ZAMBON_SHARED_I18N_ASSET_PATH}/top-bar/`, suffix: '.json' },
|
|
863
1138
|
];
|
|
864
1139
|
function createZambonSharedTranslateLoader(httpBackend, appResources = []) {
|
|
865
1140
|
return new MultiTranslateHttpLoader(httpBackend, [
|
|
@@ -885,5 +1160,5 @@ function provideZambonSharedTranslateService(config = {}) {
|
|
|
885
1160
|
* Generated bundle index. Do not edit.
|
|
886
1161
|
*/
|
|
887
1162
|
|
|
888
|
-
export { AuthGuard, AuthInterceptor, AuthenticationService, BypassHtmlSanitizerPipe, EnumLabelPipe, FiltersBase, LoginLayoutComponent, MainLayoutComponent, OperationsHistoryChildListComponent, OperationsHistoryModalComponent, OperationsHistoryService, ServicesHistoryChildListComponent, ServicesHistoryService, ServicesHistoryViewComponent, SharedAuthModule, UtcDatePipe, ZAMBON_SHARED_I18N_ASSET, ZAMBON_SHARED_I18N_ASSET_PATH, ZAMBON_SHARED_I18N_RESOURCES, createZambonSharedTranslateLoader, provideZambonSharedTranslateLoader, provideZambonSharedTranslateService };
|
|
1163
|
+
export { AuthGuard, AuthInterceptor, AuthenticationService, BrandComponent, BypassHtmlSanitizerPipe, EnumLabelPipe, EnvironmentBadgeComponent, FiltersBase, LoginLayoutComponent, MainLayoutComponent, NotificationsComponent, NotificationsService, OperationsHistoryChildListComponent, OperationsHistoryModalComponent, OperationsHistoryService, ServicesHistoryChildListComponent, ServicesHistoryService, ServicesHistoryViewComponent, SharedAuthModule, TopBarComponent, UserProfileComponent, UtcDatePipe, ZAMBON_SHARED_I18N_ASSET, ZAMBON_SHARED_I18N_ASSET_PATH, ZAMBON_SHARED_I18N_RESOURCES, createZambonSharedTranslateLoader, provideZambonSharedTranslateLoader, provideZambonSharedTranslateService };
|
|
889
1164
|
//# sourceMappingURL=zambon-dev-shared.mjs.map
|