{"version":3,"file":"main.3357e23939d7c128.js","mappings":"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","names":["HttpParamForcedUriCodec","encodeKey","key","encodeURIComponent","encodeValue","value","decodeKey","decodeURIComponent","decodeValue","BaseApiService","constructor","httpClient","apiBaseUrl","controllerUrl","this","BehaviorSubject","_loading","asObservable","url","getBaseUrl","getList","urlExtension","dto","setLoadingFlag","wrappedRequest","get","getFullUrl","params","toHttpParams","getAll","getItem","getArray","patch","id","replacements","body","Object","keys","map","path","op","post","headers","put","delete","lowCaseUrl","toLowerCase","startsWith","isEmpty","trim","input","HttpParams","encoder","fromObject","request","of","pipe","tap","next","switchMap","_","take","catchError","e","handleErrorObservable","finalize","response","observableThrowError","error","title","detail","message","trimEnd","UsersService","appCfgService","super","getAppServerUrl","getUserList","getUser","getCurrentUser","cachedCurrentUser","u","createUser","updateUser","undefined","deleteUser","i0","factory","ɵfac","AppConfigService","cachedApiBaseUrl","getCookie","cookieNameApiBaseUrl","getConfiguredUsersAndTenants","cachedApiUserKeys","cookieNameApiUserKeys","apiKey","userName","tenantName","getPageSizeOptions","pageSizes","getPageSize","name","defaultValue","document","cookie","split","pop","shift","decodedVal","parseResult","attempt","JSON","parse","bind","isError","AlertDialogComponent","data","MAT_DIALOG_DATA","selectors","ConfirmationDialogComponent","NotificationComponent","messages","MAT_SNACK_BAR_DATA","NotificationService","dialog","notifySnack","zone","showNotification","text","showNotificationWithLink","duration","showNotificationWithLinks","config","MatSnackBarConfig","run","openFromComponent","showError","htmlBody","errDetails","addedMsg","replace","open","html","afterClosed","showConfirmation","confirmationText","cancellationText","showDeleteConfirmation","deleteValidationText","deleteValidationValue","deleteValidationPlaceHolder","PageTitleService","titleService","activatedRoute","fullPageTitle","_title","breadcrumbs","_breadcrumbs","initializePageTitles","getNestedRoutes","root","last","label","setTitle","addLastBreadcrumbs","item","push","setLastBreadcrumbs","length","route","menuItems","children","forEach","child","routeURL","snapshot","segment","join","BaseStorageService","storage","setItem","removeItem","clear","LocalStorageService","localStorage","SessionStorageService","sessionStorage","AuthService","storageService","usersAndTenants","currentUserApiKey","newUserFromSession","find","Error","AuthInterceptor","notifyService","authService","intercept","req","updatedRequest","clone","setHeaders","withCredentials","handle","err","HttpErrorResponse","indexOf","status","authFailedRequestShown","setTimeout","subscribe","EMPTY","noInternetMessageShown","window","navigator","onLine","msg","isRefresh","location","reload","throwError","CoreModule","core","iconRegistry","sanitizer","addSvgIcon","bypassSecurityTrustResourceUrl","static","ngModule","providers","multi","provide","HTTP_INTERCEPTORS","useClass","imports","CommonModule","FormsModule","HttpClientModule","MatButtonModule","MatDialogModule","MatIconModule","MatListModule","MatMenuModule","MatRadioModule","MatSidenavModule","MatSnackBarModule","MatToolbarModule","MatTooltipModule","RouterModule","NoContentComponent","dataWithTitle","pageName","routes","redirectTo","pathMatch","loadChildren","Promise","then","m","BacklogModule","UserModule","SystemModule","component","AppRoutingModule","WelcomeDialogComponent","WelcomeDialogCheckService","dialogService","displayWelcomeDialogIfNecessary","showWelcome","storageTimestampStr","storageTimestampKey","storageTimestamp","Date","diffFromNowInDays","minDaysForShowingWelcomeAgain","width","beforeClosed","filter","Boolean","toISOString","dateTime","diffTime","Math","abs","getTime","ceil","FooterComponent","MainMenuMapItem","uriStartsWith","highlightedLinks","icon","mainLink","first","ChangeUserAndTenantDialogComponent","users","currentUser","ChangeUserAndTenantService","sessionStorageService","displayDialog","s","href","BreadcrumbsComponent","pageTitle","ngOnInit","MainMenuComponent","breakpoint","router","cdr","userService","changeUserDialogService","Subscription","_currentUser","currentUserLink","isMobile","_isMobile","isNarrowMenu","_isNarrowMenu","mobileTitle","siteMap","_subscriptions","add","r","ngAfterViewInit","observe","Breakpoints","result","matches","sidenav","mode","opened","close","detectChanges","ngOnDestroy","unsubscribe","expandCollapseSideMenu","isMenuButtonHighlighted","some","expandMenu","collapseMenu","changeUserAndTenantDialog","openedRecentItemsMenu","MatSidenav","ctx","AppComponent","welcomeDialogCheck","_this","subscriptions","events","event","NavigationEnd","scrollTop","DOCUMENT","AppModule","bootstrap","BrowserModule","BrowserAnimationsModule","MatNativeDateModule","environment","enableProdMode","__NgCli_bootstrap_1","bootstrapModule","catch","console"],"sourceRoot":"webpack:///","sources":["./src/app/core/api-services/http-param-forced-url-codec.ts","./src/app/core/api-services/base-api.service.ts","./src/app/core/api-services/users.service.ts","./src/app/core/app-config.service.ts","./src/app/core/notification/alert-dialog.component.ts","./src/app/core/notification/confirmation-dialog.component.ts","./src/app/core/notification/notification.component.ts","./src/app/core/notification/notification.service.ts","./src/app/core/page-title.service.ts","./src/app/core/storage.service.ts","./src/app/core/auth.service.ts","./src/app/core/auth.interceptor.ts","./src/app/core/core.module.ts","./src/app/no-content/no-content.component.ts","./src/app/app-routing.module.ts","./src/app/core/welcome-dialog/welcome-dialog.component.ts","./src/app/core/welcome-dialog/welcome-dialog.component.html","./src/app/core/welcome-dialog/welcome-dialog-check.service.ts","./src/app/core/footer/footer.component.ts","./src/app/core/footer/footer.component.html","./src/app/core/main-menu/main-menu-map-item.ts","./src/app/core/change-user-and-tenant-dialog/change-user-and-tenant-dialog.component.html","./src/app/core/change-user-and-tenant-dialog/change-user-and-tenant-dialog.component.ts","./src/app/core/change-user-and-tenant-dialog/change-user-and-tenant.service.ts","./src/app/core/main-menu/breadcrumbs/breadcrumbs.component.html","./src/app/core/main-menu/breadcrumbs/breadcrumbs.component.ts","./src/app/core/main-menu/main-menu.component.html","./src/app/core/main-menu/main-menu.component.ts","./src/app/app.component.ts","./src/app/app.module.ts","./src/main.ts"],"sourcesContent":["import { HttpParameterCodec } from '@angular/common/http';\n\n/*\n *\tEnforce the URI on query string parameters (encoding '+', '-', etc.)\n *\tIt's a workaround for official Angular bug https://github.com/angular/angular/issues/11058\n */\nexport class HttpParamForcedUriCodec implements HttpParameterCodec {\n\tencodeKey(key: string): string {\n\t\treturn encodeURIComponent(key);\n\t}\n\n\tencodeValue(value: string): string {\n\t\treturn encodeURIComponent(value);\n\t}\n\n\tdecodeKey(key: string): string {\n\t\treturn decodeURIComponent(key);\n\t}\n\n\tdecodeValue(value: string): string {\n\t\treturn decodeURIComponent(value);\n\t}\n}\n","import { HttpClient, HttpErrorResponse, HttpParams } from '@angular/common/http';\nimport { ListResponse } from '@core/api-models/common/ListResponse';\nimport { get, isEmpty, trim, trimEnd } from 'lodash-es';\nimport { BehaviorSubject, Observable, of, throwError as observableThrowError } from 'rxjs';\nimport { catchError, finalize, switchMap, take, tap } from 'rxjs/operators';\nimport { HttpErrorDetails } from './http-error-details';\nimport { HttpParamForcedUriCodec } from './http-param-forced-url-codec';\n\nexport abstract class BaseApiService {\n\tprivate url: string;\n\n\tprivate _loading = new BehaviorSubject(false);\n\tloading$ = this._loading.asObservable();\n\n\tconstructor(protected httpClient: HttpClient, private apiBaseUrl: string, controllerUrl: string) {\n\t\tthis.url = this.getBaseUrl(controllerUrl);\n\t}\n\n\t// Note: This method is public as it is used by 'paginated-datasource' but it should not be used anywhere else\n\tgetList(\n\t\turlExtension?: string,\n\t\tdto?: Partial,\n\t\tsetLoadingFlag: boolean = true\n\t): Observable> {\n\t\treturn this.wrappedRequest(\n\t\t\t() =>\n\t\t\t\tthis.httpClient.get>(this.getFullUrl(urlExtension), {\n\t\t\t\t\tparams: this.toHttpParams(dto),\n\t\t\t\t}),\n\t\t\tsetLoadingFlag\n\t\t);\n\t}\n\n\t// This method gets all the records without paging and filtering\n\tprotected getAll(urlExtension?: string, setLoadingFlag: boolean = true): Observable {\n\t\treturn this.wrappedRequest(() => this.httpClient.get(this.getFullUrl(urlExtension)), setLoadingFlag);\n\t}\n\n\tprotected getItem(\n\t\turlExtension?: string,\n\t\tdto?: Partial,\n\t\tsetLoadingFlag: boolean = true\n\t): Observable {\n\t\treturn this.wrappedRequest(\n\t\t\t() =>\n\t\t\t\tthis.httpClient.get(this.getFullUrl(urlExtension), {\n\t\t\t\t\tparams: this.toHttpParams(dto),\n\t\t\t\t}),\n\t\t\tsetLoadingFlag\n\t\t);\n\t}\n\n\tprotected getArray(\n\t\turlExtension?: string,\n\t\tdto?: Partial | { [param: string]: string | string[] },\n\t\tsetLoadingFlag: boolean = true\n\t): Observable {\n\t\treturn this.wrappedRequest(\n\t\t\t() =>\n\t\t\t\tthis.httpClient.get(this.getFullUrl(urlExtension), {\n\t\t\t\t\tparams: this.toHttpParams(dto),\n\t\t\t\t}),\n\t\t\tsetLoadingFlag\n\t\t);\n\t}\n\n\tprotected patch(id: string, replacements: Partial): Observable {\n\t\tconst body = Object.keys(replacements).map(path => ({ op: 'replace', path, value: get(replacements, path) }));\n\t\treturn this.wrappedRequest(() => this.httpClient.patch(this.getFullUrl(id), body));\n\t}\n\n\tprotected post(urlExtension?: string, body?: TItemDto): Observable {\n\t\treturn this.wrappedRequest(() =>\n\t\t\tthis.httpClient\n\t\t\t\t// always post with a content type of application/json to prevent Http Status 415 errors\n\t\t\t\t.post(this.getFullUrl(urlExtension), body, {\n\t\t\t\t\theaders: { 'Content-Type': 'application/json' },\n\t\t\t\t})\n\t\t);\n\t}\n\n\tprotected put(urlExtension?: string, body?: TItemDto): Observable {\n\t\treturn this.wrappedRequest(() => this.httpClient.put(this.getFullUrl(urlExtension), body));\n\t}\n\n\tprotected delete(urlExtension?: string): Observable {\n\t\treturn this.wrappedRequest(() => this.httpClient.delete(this.getFullUrl(urlExtension)));\n\t}\n\n\tprotected getFullUrl(urlExtension?: string): string {\n\t\tconst lowCaseUrl = urlExtension?.toLowerCase() ?? '';\n\t\t// End-point on a different controller\n\t\tif (lowCaseUrl.startsWith('api') || lowCaseUrl.startsWith('/api')) return this.getBaseUrl(urlExtension!);\n\n\t\treturn `${this.url}${isEmpty(urlExtension) ? '' : '/'}${trim(urlExtension, '/')}`;\n\t}\n\n\tprotected toHttpParams(input: { [key: string]: any } | undefined): HttpParams {\n\t\t// Convert object to HttpParams (see https://stackoverflow.com/a/47928379/968003).\n\t\t// Use custom encoding to work around problems with encoding of '+'.\n\t\treturn new HttpParams({\n\t\t\tencoder: new HttpParamForcedUriCodec(),\n\t\t\tfromObject: input,\n\t\t});\n\t}\n\n\tprivate wrappedRequest(request: () => Observable, setLoadingFlag: boolean = true): Observable {\n\t\treturn of(null).pipe(\n\t\t\ttap(() => {\n\t\t\t\tif (setLoadingFlag) this._loading.next(true);\n\t\t\t}),\n\t\t\tswitchMap(_ => request()),\n\t\t\ttake(1),\n\t\t\tcatchError(e => this.handleErrorObservable(e)),\n\t\t\tfinalize(() => {\n\t\t\t\tif (setLoadingFlag) this._loading.next(false);\n\t\t\t})\n\t\t);\n\t}\n\n\tprivate handleErrorObservable(response: HttpErrorResponse): Observable {\n\t\tconst thrownError: HttpErrorDetails | any =\n\t\t\tresponse?.error || ({ title: (response as any)?.title, detail: response?.message } as HttpErrorDetails);\n\t\treturn observableThrowError(thrownError);\n\t}\n\n\tprivate getBaseUrl(urlExtension: string): string {\n\t\tconst lowCaseUrl = urlExtension.toLowerCase();\n\t\treturn lowCaseUrl.startsWith('http') ? lowCaseUrl : `${trimEnd(this.apiBaseUrl, '/ ')}/${trim(lowCaseUrl, '/ ')}`;\n\t}\n}\n","import { HttpClient } from '@angular/common/http';\nimport { Injectable } from '@angular/core';\nimport { ListResponse } from '@core/api-models/common/ListResponse';\nimport { UserReference } from '@core/api-models/common/references';\nimport { UserAddUpdRequest, UserGetByIdResponse } from '@core/api-models/user/item';\nimport { CurrentUserResponse } from '@core/api-models/user/item/CurrentUserResponse';\nimport { UserListGetRequest, UserListGetResponse } from '@core/api-models/user/list';\nimport { AppConfigService } from '@core/app-config.service';\nimport { Observable, of } from 'rxjs';\nimport { tap } from 'rxjs/operators';\nimport { BaseApiService } from './base-api.service';\n\n@Injectable({\n\tprovidedIn: 'root',\n})\nexport class UsersService extends BaseApiService {\n\tprivate cachedCurrentUser: CurrentUserResponse | undefined;\n\n\tconstructor(httpClient: HttpClient, appCfgService: AppConfigService) {\n\t\tsuper(httpClient, appCfgService.getAppServerUrl(), 'api/Users');\n\t}\n\n\tgetUserList(request?: Partial): Observable> {\n\t\treturn this.getList('', request);\n\t}\n\tgetUser(id: string): Observable {\n\t\treturn this.getItem(id);\n\t}\n\tgetCurrentUser(): Observable {\n\t\tif (!!this.cachedCurrentUser) return of(this.cachedCurrentUser!);\n\t\treturn this.getItem('current').pipe(tap(u => (this.cachedCurrentUser = u)));\n\t}\n\n\tcreateUser(request?: UserAddUpdRequest): Observable {\n\t\treturn this.post('', request);\n\t}\n\tupdateUser(id: string, request?: UserAddUpdRequest): Observable {\n\t\tif (!this.cachedCurrentUser && id == this.cachedCurrentUser!.id) this.cachedCurrentUser = undefined;\n\n\t\treturn this.put(id, request);\n\t}\n\tdeleteUser(id: string): Observable {\n\t\treturn this.delete(id);\n\t}\n}\n","import { Injectable } from '@angular/core';\nimport { attempt, isError } from 'lodash-es';\nimport { UsersConfigModel } from './api-models/users-config.model';\n\n@Injectable({\n\tprovidedIn: 'root',\n})\nexport class AppConfigService {\n\tpublic static readonly sessionUserChangedParamName = 'newUser';\n\n\tprivate readonly cookieNameApiBaseUrl = 'apiBaseUrl';\n\tprivate readonly cookieNameApiUserKeys = 'apiUsers';\n\n\tprivate readonly pageSizes = [10, 15, 20, 30, 50, 100];\n\n\tprivate cachedApiBaseUrl: string | null = null;\n\tprivate cachedApiUserKeys: UsersConfigModel[] | null = null;\n\n\tgetAppServerUrl(): string {\n\t\tif (!this.cachedApiBaseUrl) this.cachedApiBaseUrl = this.getCookie(this.cookieNameApiBaseUrl, 'https://localhost:5001');\n\t\treturn this.cachedApiBaseUrl;\n\t}\n\n\tgetConfiguredUsersAndTenants(): UsersConfigModel[] {\n\t\tif (!this.cachedApiUserKeys) {\n\t\t\t// The hard-coded value is used exclusively for dev purposes\n\t\t\tconst defaultValue = [\n\t\t\t\t{\n\t\t\t\t\tapiKey: '4C81D6EC-AA5A-4799-8538-84E31CF5493B',\n\t\t\t\t\tuserName: 'Test',\n\t\t\t\t\ttenantName: 'dotnet',\n\t\t\t\t},\n\t\t\t];\n\n\t\t\tthis.cachedApiUserKeys = this.getCookie(this.cookieNameApiUserKeys, defaultValue);\n\t\t}\n\t\treturn this.cachedApiUserKeys;\n\t}\n\n\tgetPageSizeOptions(): number[] {\n\t\treturn this.pageSizes;\n\t}\n\n\tgetPageSize(): number {\n\t\treturn this.pageSizes[1];\n\t}\n\n\t/*\n\t\tGet value from a cookie.\n\t\t\t`T` - type of the expected object (attempts to parse a serialised JSON object)\n\t\t\t`name` - name of the cookie\n\t\t\t`defaultValue` - returned default values if the specified cookie not found\n\t*/\n\tprivate getCookie(name: string, defaultValue: T): T {\n\t\tconst value = ('; ' + document.cookie).split(`; ${name}=`).pop()?.split(';').shift();\n\t\tif (!!value) {\n\t\t\tconst decodedVal = decodeURIComponent(value);\n\t\t\t// The string value might be an object that can be deserialized\n\t\t\tconst parseResult = attempt(JSON.parse.bind(null, decodedVal));\n\t\t\treturn !isError(parseResult) ? parseResult : decodedVal;\n\t\t}\n\t\treturn defaultValue;\n\t}\n}\n","import { Component, Inject } from '@angular/core';\nimport { MAT_DIALOG_DATA } from '@angular/material/dialog';\n\n@Component({\n\ttemplate: `\n\t\t

{{ data.title }}

\n\t\t\n\t\t
\n\t\t\n\t\t\t\n\t\t\n\t`,\n})\nexport class AlertDialogComponent {\n\tconstructor(@Inject(MAT_DIALOG_DATA) public data: any) {}\n}\n","import { Component, Inject } from '@angular/core';\nimport { MAT_DIALOG_DATA } from '@angular/material/dialog';\n\n@Component({\n\ttemplate: `\n\t\t

{{ data.title }}

\n\t\t
\n\t\t
\n\t\t\t\n\t\t\t\n\t\t
\n\t`,\n})\nexport class ConfirmationDialogComponent {\n\tconstructor(@Inject(MAT_DIALOG_DATA) public data: IConfirmationDialogParams) {}\n}\n\nexport interface IConfirmationDialogParams {\n\ttitle: string;\n\thtml: string;\n\tconfirmationText: string;\n\tcancellationText: string;\n}\n","import { Component, Inject } from '@angular/core';\nimport { MAT_SNACK_BAR_DATA } from '@angular/material/snack-bar';\n\nexport interface INotificationMessage {\n\ttext: string;\n\tlinkText?: string;\n\tlinkRoute?: string[];\n\tlinkParams?: { [key: string]: string | string[] };\n}\n\n/*\n Snack notifications with HTML tags inside\n*/\n@Component({\n\tstyles: [\n\t\t`\n\t\t\ta {\n\t\t\t\tcolor: lightblue;\n\t\t\t\tmargin-left: 0.25em;\n\t\t\t}\n\t\t`,\n\t],\n\ttemplate: `\n\t\t
\n\t\t\t\n\t\t\t{{ data.linkText }}\n\t\t
\n\t`,\n})\nexport class NotificationComponent {\n\tconstructor(@Inject(MAT_SNACK_BAR_DATA) public messages: INotificationMessage[]) {}\n}\n","import { Injectable, NgZone } from '@angular/core';\nimport { MatDialog } from '@angular/material/dialog';\nimport { MatSnackBar, MatSnackBarConfig } from '@angular/material/snack-bar';\nimport { HttpErrorDetails } from '@core/api-services/http-error-details';\nimport { Observable } from 'rxjs';\nimport { take } from 'rxjs/operators';\nimport { AlertDialogComponent } from './alert-dialog.component';\nimport { ConfirmationDialogComponent } from './confirmation-dialog.component';\nimport { INotificationMessage, NotificationComponent } from './notification.component';\n\n@Injectable({\n\tprovidedIn: 'root',\n})\nexport class NotificationService {\n\tconstructor(private dialog: MatDialog, private notifySnack: MatSnackBar, private zone: NgZone) {}\n\n\tshowNotification(text: string) {\n\t\tthis.showNotificationWithLink({ text });\n\t}\n\n\tshowNotificationWithLink(data: INotificationMessage, duration: number = 3000) {\n\t\tthis.showNotificationWithLinks([data], duration);\n\t}\n\tprivate showNotificationWithLinks(data: INotificationMessage[], duration: number = 3000) {\n\t\tconst config = new MatSnackBarConfig();\n\t\tconfig.duration = duration;\n\t\tconfig.data = data;\n\t\tthis.zone.run(() => {\n\t\t\tthis.notifySnack.openFromComponent(NotificationComponent, config);\n\t\t});\n\t}\n\n\tshowError(title: string, htmlBody: string, errDetails: HttpErrorDetails | undefined = undefined): Observable {\n\t\tif (!!errDetails) {\n\t\t\tlet addedMsg =\n\t\t\t\t(!!errDetails.title ? '
' + errDetails.title : '') +\n\t\t\t\t(!!errDetails.detail ? '
' + errDetails.detail.replace('\\n', '
\\n') : '');\n\t\t\tif (!addedMsg) addedMsg = '' + errDetails;\n\t\t\thtmlBody += addedMsg;\n\t\t}\n\t\tconst dialogRef = this.dialog.open(AlertDialogComponent, {\n\t\t\tdata: {\n\t\t\t\thtml: htmlBody,\n\t\t\t\ttitle,\n\t\t\t},\n\t\t});\n\t\treturn dialogRef.afterClosed().pipe(take(1));\n\t}\n\n\t/* Show a Yes/Cancel dialog */\n\tshowConfirmation(title: string, html: string, confirmationText: string = '', cancellationText: string = ''): Observable {\n\t\tconst dialogRef = this.dialog.open(ConfirmationDialogComponent, {\n\t\t\tdata: {\n\t\t\t\tcancellationText,\n\t\t\t\tconfirmationText,\n\t\t\t\thtml,\n\t\t\t\ttitle,\n\t\t\t},\n\t\t});\n\t\treturn dialogRef.afterClosed().pipe(take(1));\n\t}\n\n\t/* Show a Yes/Cancel dialog with a delete verification feature */\n\tshowDeleteConfirmation(\n\t\ttitle: string,\n\t\thtml: string,\n\t\tconfirmationText: string = '',\n\t\tcancellationText: string = '',\n\t\tdeleteValidationText: string = '',\n\t\tdeleteValidationValue: string = '',\n\t\tdeleteValidationPlaceHolder: string = ''\n\t): Observable {\n\t\tconst dialogRef = this.dialog.open(ConfirmationDialogComponent, {\n\t\t\tdata: {\n\t\t\t\tcancellationText,\n\t\t\t\tconfirmationText,\n\t\t\t\tdeleteValidationPlaceHolder,\n\t\t\t\tdeleteValidationText,\n\t\t\t\tdeleteValidationValue,\n\t\t\t\thtml,\n\t\t\t\ttitle,\n\t\t\t},\n\t\t});\n\t\treturn dialogRef.afterClosed().pipe(take(1));\n\t}\n}\n","import { Injectable } from '@angular/core';\nimport { Title } from '@angular/platform-browser';\nimport { ActivatedRoute } from '@angular/router';\nimport { isEmpty, last } from 'lodash-es';\n\nexport interface IBreadcrumbItem {\n\tlabel: string;\n\turl: string;\n}\n@Injectable({\n\tprovidedIn: 'root',\n})\nexport class PageTitleService {\n\tget fullPageTitle(): string {\n\t\treturn (!isEmpty(this._title) ? this._title + ' | ' : '') + 'YABT';\n\t}\n\tprivate _title = '';\n\n\tget breadcrumbs(): IBreadcrumbItem[] {\n\t\treturn this._breadcrumbs;\n\t}\n\tprivate _breadcrumbs: IBreadcrumbItem[] = [];\n\n\tconstructor(private titleService: Title, private activatedRoute: ActivatedRoute) {}\n\n\tinitializePageTitles(): void {\n\t\tthis._breadcrumbs = this.getNestedRoutes(this.activatedRoute.root);\n\t\tthis._title = last(this._breadcrumbs)?.label || '';\n\n\t\tthis.titleService.setTitle(this.fullPageTitle);\n\t}\n\taddLastBreadcrumbs(item: IBreadcrumbItem): void {\n\t\tthis._breadcrumbs.push(item);\n\t}\n\tsetLastBreadcrumbs(item: IBreadcrumbItem): void {\n\t\tif (!this._breadcrumbs || this._breadcrumbs.length < 1) return;\n\t\tthis._breadcrumbs[this._breadcrumbs.length - 1] = item;\n\t}\n\n\tprivate getNestedRoutes(route: ActivatedRoute, url: string = '', menuItems: IBreadcrumbItem[] = []): IBreadcrumbItem[] {\n\t\tif (!isEmpty(route.children))\n\t\t\troute.children.forEach(child => {\n\t\t\t\tconst routeURL: string = child.snapshot.url.map(segment => segment.path).join('/');\n\t\t\t\tif (isEmpty(routeURL)) return menuItems;\n\t\t\t\telse {\n\t\t\t\t\turl += `/${routeURL}`;\n\t\t\t\t\tconst label = child.snapshot?.data?.title;\n\t\t\t\t\tif (!!label) {\n\t\t\t\t\t\tmenuItems.push({ label, url });\n\t\t\t\t\t}\n\n\t\t\t\t\treturn this.getNestedRoutes(child, url, menuItems);\n\t\t\t\t}\n\t\t\t});\n\t\treturn menuItems;\n\t}\n}\n","import { Injectable } from '@angular/core';\n\nabstract class BaseStorageService {\n\tprotected constructor(protected storage: Storage) {}\n\n\tpublic setItem(key: string, value: string): void {\n\t\tthis.storage.setItem(key, value);\n\t}\n\n\tpublic getItem(key: string): string | null {\n\t\treturn this.storage.getItem(key);\n\t}\n\n\tpublic removeItem(key: string): void {\n\t\tthis.storage.removeItem(key);\n\t}\n\n\tpublic clear(): void {\n\t\tthis.storage.clear();\n\t}\n}\n\n@Injectable({\n\tprovidedIn: 'root',\n})\nexport class LocalStorageService extends BaseStorageService {\n\tconstructor() {\n\t\tsuper(localStorage);\n\t}\n}\n\n@Injectable({\n\tprovidedIn: 'root',\n})\nexport class SessionStorageService extends BaseStorageService {\n\tconstructor() {\n\t\tsuper(sessionStorage);\n\t}\n}\n","import { Injectable } from '@angular/core';\nimport { UsersConfigModel } from './api-models/users-config.model';\nimport { AppConfigService } from './app-config.service';\nimport { SessionStorageService } from './storage.service';\n\n@Injectable({\n\tprovidedIn: 'root',\n})\nexport class AuthService {\n\tprivate currentUserApiKey: string | undefined;\n\n\tconstructor(private appCfgService: AppConfigService, private storageService: SessionStorageService) {}\n\n\tgetCurrentUser(): UsersConfigModel {\n\t\tconst usersAndTenants = this.appCfgService.getConfiguredUsersAndTenants();\n\t\tif (!this.currentUserApiKey) {\n\t\t\tvar newUserFromSession = this.storageService.getItem(AppConfigService.sessionUserChangedParamName);\n\t\t\tthis.storageService.removeItem(AppConfigService.sessionUserChangedParamName);\n\t\t\tif (!!newUserFromSession) this.currentUserApiKey = newUserFromSession;\n\t\t}\n\t\tif (!!this.currentUserApiKey) {\n\t\t\tconst u = usersAndTenants.find(u => u.apiKey == this.currentUserApiKey);\n\t\t\tif (!u) throw new Error('Failed to resolve allowed user for ' + this.currentUserApiKey);\n\t\t\treturn u;\n\t\t} else return usersAndTenants[0];\n\t}\n}\n","import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';\nimport { Injectable } from '@angular/core';\nimport { EMPTY, Observable, throwError } from 'rxjs';\nimport { catchError } from 'rxjs/operators';\nimport { AuthService } from './auth.service';\nimport { NotificationService } from './notification/notification.service';\n\n/**\n * Adds the authorisation headers and handles 401,403 codes\n */\n@Injectable()\nexport class AuthInterceptor implements HttpInterceptor {\n\tprivate noInternetMessageShown = false;\n\tprivate authFailedRequestShown = false;\n\n\tconstructor(private notifyService: NotificationService, private authService: AuthService) {}\n\n\tintercept(req: HttpRequest, next: HttpHandler): Observable> {\n\t\tconst apiKey = this.authService.getCurrentUser().apiKey;\n\t\tif (!apiKey) throw new Error('Failed to resolve the API key');\n\n\t\tconst updatedRequest = req.clone({\n\t\t\tsetHeaders: { 'X-API-Key': `${apiKey}` },\n\t\t\twithCredentials: true,\n\t\t});\n\t\treturn next.handle(updatedRequest).pipe(\n\t\t\tcatchError(err => {\n\t\t\t\t// Catch the 401/403 status code to redirect to the login page\n\t\t\t\tif (err instanceof HttpErrorResponse && [401, 403].indexOf(err.status) > -1) {\n\t\t\t\t\tif (!this.authFailedRequestShown) {\n\t\t\t\t\t\tthis.authFailedRequestShown = true;\n\t\t\t\t\t\tconst message =\n\t\t\t\t\t\t\t(!!err?.error?.detail ? err!.error!.detail + '

\\n' : '') +\n\t\t\t\t\t\t\t'Please report it as an issue on GitHub (link)';\n\n\t\t\t\t\t\t// No 'switchMap()' here and start a new flow via 'setTimeout()', as flipping HttpClient pipeline\n\t\t\t\t\t\t// to UI can be troublesome. The HttpClient pipeline need to finish as it's expected\n\t\t\t\t\t\tsetTimeout(() =>\n\t\t\t\t\t\t\tthis.notifyService\n\t\t\t\t\t\t\t\t.showError('Authentication failed', message)\n\t\t\t\t\t\t\t\t.subscribe(() => (this.authFailedRequestShown = false))\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\t// Don't throw an error if the notification is already shown to the user\n\t\t\t\t\treturn EMPTY;\n\t\t\t\t}\n\t\t\t\t// If server can't be accessed\n\t\t\t\t// * status code would be 0\n\t\t\t\telse if (err instanceof HttpErrorResponse && err.status === 0) {\n\t\t\t\t\tif (!this.noInternetMessageShown) {\n\t\t\t\t\t\tthis.noInternetMessageShown = true;\n\t\t\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t\t\t// If the internet connection is lost\n\t\t\t\t\t\t\t// * window.navigator.onLine would return false\n\t\t\t\t\t\t\tconst title = !window.navigator.onLine ? 'Internet Connection Lost' : 'No server connection';\n\t\t\t\t\t\t\tconst msg = !window.navigator.onLine\n\t\t\t\t\t\t\t\t? 'Unable to connect to Internet. Please check your Internet connection.'\n\t\t\t\t\t\t\t\t: 'Unable to connect to the server while Internet connection is present.';\n\t\t\t\t\t\t\treturn this.notifyService.showConfirmation(title, msg, 'Reload', 'Cancel').subscribe((isRefresh: boolean) => {\n\t\t\t\t\t\t\t\tif (isRefresh) {\n\t\t\t\t\t\t\t\t\twindow.location.reload();\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tthis.noInternetMessageShown = false;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\t// Don't throw an error if the notification is already shown to the user\n\t\t\t\t\treturn EMPTY;\n\t\t\t\t}\n\t\t\t\treturn throwError(err);\n\t\t\t})\n\t\t);\n\t}\n}\n","import { CommonModule } from '@angular/common';\nimport { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';\nimport { ModuleWithProviders, NgModule, Optional, SkipSelf } from '@angular/core';\nimport { FormsModule } from '@angular/forms';\nimport { MatButtonModule } from '@angular/material/button';\nimport { MatDialogModule } from '@angular/material/dialog';\nimport { MatIconModule, MatIconRegistry } from '@angular/material/icon';\nimport { MatListModule } from '@angular/material/list';\nimport { MatMenuModule } from '@angular/material/menu';\nimport { MatRadioModule } from '@angular/material/radio';\nimport { MatSidenavModule } from '@angular/material/sidenav';\nimport { MatSnackBarModule } from '@angular/material/snack-bar';\nimport { MatToolbarModule } from '@angular/material/toolbar';\nimport { MatTooltipModule } from '@angular/material/tooltip';\nimport { DomSanitizer } from '@angular/platform-browser';\nimport { RouterModule } from '@angular/router';\nimport { AuthInterceptor } from './auth.interceptor';\nimport { ChangeUserAndTenantDialogComponent } from './change-user-and-tenant-dialog';\nimport { FooterComponent } from './footer';\nimport { MainMenuComponent } from './main-menu';\nimport { BreadcrumbsComponent } from './main-menu/breadcrumbs/breadcrumbs.component';\nimport { ConfirmationDialogComponent } from './notification';\nimport { AlertDialogComponent } from './notification/alert-dialog.component';\nimport { NotificationComponent } from './notification/notification.component';\nimport { NotificationService } from './notification/notification.service';\nimport { WelcomeDialogComponent } from './welcome-dialog';\n\n@NgModule({\n\tdeclarations: [\n\t\tAlertDialogComponent,\n\t\tFooterComponent,\n\t\tMainMenuComponent,\n\t\tNotificationComponent,\n\t\tConfirmationDialogComponent,\n\t\tBreadcrumbsComponent,\n\t\tChangeUserAndTenantDialogComponent,\n\t\tWelcomeDialogComponent,\n\t],\n\texports: [FooterComponent, MainMenuComponent],\n\timports: [\n\t\tCommonModule,\n\t\tFormsModule,\n\t\tHttpClientModule,\n\t\tMatButtonModule,\n\t\tMatDialogModule,\n\t\tMatIconModule,\n\t\tMatListModule,\n\t\tMatMenuModule,\n\t\tMatRadioModule,\n\t\tMatSidenavModule,\n\t\tMatSnackBarModule,\n\t\tMatToolbarModule,\n\t\tMatTooltipModule,\n\t\tRouterModule,\n\t],\n\tproviders: [\n\t\t{\n\t\t\tmulti: true,\n\t\t\tprovide: HTTP_INTERCEPTORS,\n\t\t\tuseClass: AuthInterceptor,\n\t\t},\n\t],\n})\nexport class CoreModule {\n\t// The core module must be imported only in the root module. Other modules must not import the core modules.\n\t// This c-tor is a guard to prevent double import\n\t// See https://angular.io/guide/singleton-services#prevent-reimport-of-the-greetingmodule\n\tconstructor(@Optional() @SkipSelf() core: CoreModule, iconRegistry: MatIconRegistry, sanitizer: DomSanitizer) {\n\t\tif (core) {\n\t\t\tthrow new Error('You should import core module only in the root module');\n\t\t}\n\t\ticonRegistry.addSvgIcon('GitHub', sanitizer.bypassSecurityTrustResourceUrl('/assets/img/github.svg'));\n\t}\n\n\t// Use forRoot() to import a module with services, which need to be SINGLETON for lazy-loading modules.\n\t// Otherwise, the lazy modules will have new instances of services created for them\n\t// See https://angular.io/guide/singleton-services#the-forroot-pattern\n\tstatic forRoot(): ModuleWithProviders {\n\t\treturn {\n\t\t\tngModule: CoreModule,\n\t\t\tproviders: [NotificationService],\n\t\t};\n\t}\n}\n","import { Component } from '@angular/core';\n\n@Component({\n\tselector: 'no-content',\n\ttemplate: `\n\t\t
\n\t\t\t

404: page missing

\n\t\t
\n\t`,\n})\nexport class NoContentComponent {}\n","import { NgModule } from '@angular/core';\nimport { Data, RouterModule, Routes } from '@angular/router';\nimport { NoContentComponent } from './no-content';\n\nconst dataWithTitle = (pageName: string): Data => { title: pageName };\n\nconst routes: Routes = [\n\t{ path: '', redirectTo: 'backlog-items', pathMatch: 'full' },\n\t{ path: 'backlog-items', loadChildren: () => import('./backlog').then(m => m.BacklogModule), data: dataWithTitle('Backlog Items') },\n\t{ path: 'users', loadChildren: () => import('./user').then(m => m.UserModule), data: dataWithTitle('Users') },\n\t{ path: 'system', loadChildren: () => import('./system').then(m => m.SystemModule), data: dataWithTitle('System') },\n\t{ path: '**', component: NoContentComponent },\n];\n\n@NgModule({\n\timports: [RouterModule.forRoot(routes)],\n\texports: [RouterModule],\n})\nexport class AppRoutingModule {}\n","import { Component } from '@angular/core';\n\n@Component({\n\tstyleUrls: ['./welcome-dialog.component.scss'],\n\ttemplateUrl: 'welcome-dialog.component.html',\n})\nexport class WelcomeDialogComponent {}\n","

Welcome to YABT

\n\n\t
\n\t\tIt's a bug-tracker. A demo project leveraging RavenDB database (along with .NET &\n\t\tAngular).\n\t
\n\t
\n\t\t\n\t\tOpen-source & MIT project. Grab the code on GitHub.\n\t
\n\t
\n\t\tvisibility\n\t\tCheck out a series of articles explaining\n\t\t\tvarious aspects of building an app like this with NoSQL.\n\t
\n\t
\n\t\tsentiment_satisfied_alt\n\t\tFeel free to poke around. Search, modify, delete and create entries.
You're logged in as one of the users\n\t\t\talready but you can change the user/tenant in the top right menu.
\n\t
\n
\n\n\t\n\n","import { Injectable } from '@angular/core';\nimport { MatDialog } from '@angular/material/dialog';\nimport { LocalStorageService } from '@core/storage.service';\nimport { filter, take } from 'rxjs/operators';\nimport { WelcomeDialogComponent } from './welcome-dialog.component';\n\n@Injectable({\n\tprovidedIn: 'root',\n})\nexport class WelcomeDialogCheckService {\n\treadonly storageTimestampKey = 'welcomeDismissedAt';\n\treadonly minDaysForShowingWelcomeAgain = 7;\n\n\tconstructor(private dialogService: MatDialog, private storageService: LocalStorageService) {}\n\n\tdisplayWelcomeDialogIfNecessary(): void {\n\t\tlet showWelcome = true;\n\t\tconst storageTimestampStr = this.storageService.getItem(this.storageTimestampKey);\n\t\tif (!!storageTimestampStr) {\n\t\t\tconst storageTimestamp = new Date(storageTimestampStr);\n\t\t\tshowWelcome = this.diffFromNowInDays(storageTimestamp) >= this.minDaysForShowingWelcomeAgain;\n\t\t}\n\n\t\tif (showWelcome) {\n\t\t\tthis.dialogService\n\t\t\t\t.open(WelcomeDialogComponent, { width: '600px' })\n\t\t\t\t.beforeClosed()\n\t\t\t\t.pipe(\n\t\t\t\t\tfilter(Boolean), // Continue only on the \"OK\" button\n\t\t\t\t\ttake(1)\n\t\t\t\t)\n\t\t\t\t.subscribe(_ =>\n\t\t\t\t\t// Save when the user dismissed the prompt\n\t\t\t\t\tthis.storageService.setItem(this.storageTimestampKey, new Date().toISOString())\n\t\t\t\t);\n\t\t}\n\t}\n\n\tprivate diffFromNowInDays(dateTime: Date): number {\n\t\tconst diffTime = Math.abs(new Date().getTime() - dateTime.getTime());\n\t\treturn Math.ceil(diffTime / (1000 * 3600 * 24));\n\t}\n}\n","import { Component } from '@angular/core';\n\n/* Footer displayed on each page */\n@Component({\n\tselector: 'app-footer',\n\tstyleUrls: ['./footer.component.scss'],\n\ttemplateUrl: 'footer.component.html'\n})\nexport class FooterComponent {}\n","\n","import { first } from 'lodash-es';\n\nexport class MainMenuMapItem {\n\tget mainLink(): string {\n\t\treturn !!this.highlightedLinks ? first(this.highlightedLinks)! : this.uriStartsWith;\n\t}\n\n\tconstructor(public title: string, public uriStartsWith: string, public highlightedLinks: string[], public icon: string) {}\n}\n","

Choose user/tenant

\n\n\t
\n\t\tThis demo-project is multi-tenanted. Choose below the user/tenant account to run the system:\n\t
\n\t\n\t\t\n\t\t\tUser \"{{u.userName}}\" from \"{{u.tenantName}}\" project\n\t\t\n\t\n\t
\n
\n\n\t\n\t\n\n","import { Component } from '@angular/core';\nimport { UsersConfigModel } from '@core/api-models/users-config.model';\nimport { AppConfigService } from '@core/app-config.service';\nimport { AuthService } from '@core/auth.service';\n\n@Component({\n\tstyleUrls: ['./change-user-and-tenant-dialog.component.scss'],\n\ttemplateUrl: 'change-user-and-tenant-dialog.component.html',\n})\nexport class ChangeUserAndTenantDialogComponent {\n\tusers: UsersConfigModel[];\n\n\tcurrentUser: string;\n\n\tconstructor(appCfgService: AppConfigService, authService: AuthService) {\n\t\tthis.users = appCfgService.getConfiguredUsersAndTenants();\n\t\tthis.currentUser = authService.getCurrentUser().apiKey;\n\t}\n}\n","import { Injectable } from '@angular/core';\nimport { MatDialog } from '@angular/material/dialog';\nimport { AppConfigService } from '@core/app-config.service';\nimport { SessionStorageService } from '@core/storage.service';\nimport { filter, take } from 'rxjs/operators';\nimport { ChangeUserAndTenantDialogComponent } from '.';\n\n@Injectable({\n\tprovidedIn: 'root',\n})\nexport class ChangeUserAndTenantService {\n\tconstructor(private dialogService: MatDialog, private sessionStorageService: SessionStorageService) {}\n\n\tdisplayDialog(): void {\n\t\tthis.dialogService\n\t\t\t.open(ChangeUserAndTenantDialogComponent, { width: '400px' })\n\t\t\t.beforeClosed()\n\t\t\t.pipe(\n\t\t\t\tfilter(s => !!s), // Continue only on the \"OK\" button\n\t\t\t\ttake(1)\n\t\t\t)\n\t\t\t.subscribe((key: string) => {\n\t\t\t\tthis.sessionStorageService.setItem(AppConfigService.sessionUserChangedParamName, key);\n\t\t\t\tlocation.href = '/';\n\t\t\t});\n\t}\n}\n","
    \n\t
  • 1\">\n\t\t\n\t\t\t{{item.label}} / \n\t\t\n\n\t\t{{item.label}}\n\t
  • \n
\n","import { Component, OnInit } from '@angular/core';\nimport { IBreadcrumbItem, PageTitleService } from '@core/page-title.service';\n\n@Component({\n\tselector: 'app-breadcrumbs',\n\ttemplateUrl: './breadcrumbs.component.html',\n\tstyleUrls: ['./breadcrumbs.component.scss'],\n})\nexport class BreadcrumbsComponent implements OnInit {\n\tget breadcrumbs(): IBreadcrumbItem[] {\n\t\treturn this.pageTitle.breadcrumbs;\n\t}\n\n\tconstructor(private pageTitle: PageTitleService) {}\n\n\tngOnInit(): void {}\n}\n","\n\t\n\t\t\n\t\t\t\"YABT\"\n\t\t\tYABT\n\t\t\n\n\t\t\n\t\t\t\n\t\t\t\t{{ item.icon }}\n\t\t\t\t\n\t\t\t\t{{ item.title }}\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\tsettings\n\t\t\t\tSystem\n\t\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t{{ isNarrowMenu ? 'arrow_forward' : 'arrow_backward' }}\n\t\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\tperson_outline\n\t\t\t\t\tUser Profile\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\tGitHub Repo\n\t\t\t\t\n\t\t\t\n\t\t\n\t\n\n\t\n\t\t\n\n\t\t\n\t\n\n\n\n\t\n\t\t\n\t\t\t\n\t\t\n\t\t\n\t\t\n\t\t\n\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\tGitHub\n\t\t\n\t\n\n\n\n\t\n\t\t\n\n\t\t\n\t\t\t\n\t\t\n\n\t\t\n\n\t\t\n\t\n\n\n\n\t\n\t\tperson\n\t\tUser Profile\n\t\n\t\n\t\n\n","import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';\nimport { AfterViewInit, ChangeDetectorRef, Component, OnDestroy, OnInit, ViewChild } from '@angular/core';\nimport { MatSidenav } from '@angular/material/sidenav';\nimport { Router } from '@angular/router';\nimport { CurrentUserResponse } from '@core/api-models/user/item/CurrentUserResponse';\nimport { UsersService } from '@core/api-services/users.service';\nimport { ChangeUserAndTenantService } from '@core/change-user-and-tenant-dialog/change-user-and-tenant.service';\nimport { get, some } from 'lodash-es';\nimport { Subscription } from 'rxjs';\nimport { MainMenuMapItem } from './main-menu-map-item';\n\n/*\n\tIt's the Main Menu. \n\tMost of the logic here is to workaround for an infamous Material problem - lacking of a mini/narrow version of the Drawer/SideNav\n\t(the Wailing Wall - https://github.com/angular/components/issues/1728).\n */\n@Component({\n\tselector: 'app-main-menu',\n\tstyleUrls: ['./main-menu.component.scss'],\n\ttemplateUrl: 'main-menu.component.html',\n})\nexport class MainMenuComponent implements OnInit, AfterViewInit, OnDestroy {\n\t@ViewChild(MatSidenav)\n\tprivate sidenav!: MatSidenav;\n\n\t// Items of the side menu\n\treadonly siteMap: MainMenuMapItem[] = [\n\t\tnew MainMenuMapItem('Backlog Items', '/backlog-items', ['/backlog-items'], 'bug_report'),\n\t\tnew MainMenuMapItem('Users', '/users', ['/users'], 'supervisor_account'),\n\t];\n\n\tget currentUser(): CurrentUserResponse | undefined {\n\t\treturn this._currentUser;\n\t}\n\tget currentUserLink(): string | undefined {\n\t\treturn !!this.currentUser?.id ? `/users/${this.currentUser.id}` : '';\n\t}\n\tget isMobile() {\n\t\treturn this._isMobile;\n\t}\n\n\tget isNarrowMenu() {\n\t\treturn !this._isMobile && this._isNarrowMenu;\n\t}\n\tget mobileTitle(): string {\n\t\treturn get(\n\t\t\tthis.siteMap.find(item => this.router.url.startsWith(item.uriStartsWith)),\n\t\t\t'title',\n\t\t\t'System'\n\t\t);\n\t}\n\n\tprivate _isMobile: boolean = false;\n\t// Flag: whether the side menu is collapsed\n\tprivate _isNarrowMenu: boolean = true;\n\tprivate _currentUser: CurrentUserResponse | undefined;\n\tprivate _subscriptions: Subscription = new Subscription();\n\n\tconstructor(\n\t\tprivate breakpoint: BreakpointObserver,\n\t\tprivate router: Router,\n\t\tprivate cdr: ChangeDetectorRef,\n\t\tprivate userService: UsersService,\n\t\tprivate changeUserDialogService: ChangeUserAndTenantService\n\t) {}\n\n\tngOnInit(): void {\n\t\tthis._subscriptions.add(this.userService.getCurrentUser().subscribe(r => (this._currentUser = r)));\n\t}\n\n\tngAfterViewInit(): void {\n\t\tthis._subscriptions.add(\n\t\t\t// It's very frustrating that this properties can't be controlled from CSS\n\t\t\tthis.breakpoint.observe([Breakpoints.Handset]).subscribe(result => {\n\t\t\t\tthis._isMobile = result.matches;\n\t\t\t\tthis.sidenav.mode = this.isMobile ? 'over' : 'side';\n\n\t\t\t\tif (this.isMobile && this.sidenav.opened) {\n\t\t\t\t\tthis.sidenav.close();\n\t\t\t\t} else if (!this.isMobile && !this.sidenav.opened) {\n\t\t\t\t\tthis.sidenav.open();\n\t\t\t\t}\n\n\t\t\t\tthis.cdr.detectChanges();\n\t\t\t})\n\t\t);\n\t}\n\n\tngOnDestroy(): void {\n\t\tthis._subscriptions.unsubscribe();\n\t}\n\n\t// Collapse/expand the menu\n\texpandCollapseSideMenu(): void {\n\t\tthis._isNarrowMenu = !this._isNarrowMenu;\n\t}\n\n\tisMenuButtonHighlighted(item: MainMenuMapItem): boolean {\n\t\treturn some(item.highlightedLinks, r => this.router.url.split('?')[0] === r);\n\t}\n\n\texpandMenu(): void {\n\t\tthis._isNarrowMenu = false;\n\t\tthis.sidenav.open();\n\t}\n\n\tcollapseMenu(): void {\n\t\tif (this.isMobile) {\n\t\t\tthis.sidenav.close();\n\t\t}\n\t}\n\n\tchangeUserAndTenantDialog(): void {\n\t\tthis.changeUserDialogService.displayDialog();\n\t}\n\n\topenedRecentItemsMenu(): void {}\n}\n","import { DOCUMENT } from '@angular/common';\nimport { Component, Inject, OnDestroy, OnInit } from '@angular/core';\nimport { NavigationEnd, Router } from '@angular/router';\nimport { PageTitleService } from '@core/page-title.service';\nimport { WelcomeDialogCheckService } from '@core/welcome-dialog/welcome-dialog-check.service';\nimport { Subscription } from 'rxjs';\nimport { filter } from 'rxjs/operators';\n\n@Component({\n\tselector: 'app-root',\n\tstyleUrls: ['./app.component.scss'],\n\ttemplate: `\n\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t
\n\t\t\t\n\t\t
\n\t`,\n})\nexport class AppComponent implements OnInit, OnDestroy {\n\tprivate subscriptions: Subscription = new Subscription();\n\n\tconstructor(\n\t\tprivate router: Router,\n\t\t@Inject(DOCUMENT) private document: Document,\n\t\tprivate pageTitle: PageTitleService,\n\t\tprivate welcomeDialogCheck: WelcomeDialogCheckService\n\t) {}\n\n\tasync ngOnInit() {\n\t\tthis.subscriptions.add(\n\t\t\tthis.router.events.pipe(filter(event => event instanceof NavigationEnd)).subscribe((): void => {\n\t\t\t\t// Scroll to top on Route Change (still doesn't work on iOS though)\n\t\t\t\t// taken from http://stackoverflow.com/a/39601987/968003\n\t\t\t\tthis.document.body.scrollTop = 0;\n\t\t\t\t// update the page title\n\t\t\t\tthis.pageTitle.initializePageTitles();\n\t\t\t})\n\t\t);\n\n\t\tthis.welcomeDialogCheck.displayWelcomeDialogIfNecessary();\n\t}\n\n\tngOnDestroy() {\n\t\tthis.subscriptions.unsubscribe();\n\t}\n}\n","import { NgModule } from '@angular/core';\nimport { MatNativeDateModule } from '@angular/material/core';\nimport { MatIconModule } from '@angular/material/icon';\nimport { MatSidenavModule } from '@angular/material/sidenav';\nimport { BrowserModule } from '@angular/platform-browser';\nimport { BrowserAnimationsModule } from '@angular/platform-browser/animations';\nimport { CoreModule } from '@core';\nimport { AppRoutingModule } from './app-routing.module';\nimport { AppComponent } from './app.component';\n@NgModule({\n\tdeclarations: [AppComponent],\n\timports: [\n\t\tBrowserModule,\n\t\tAppRoutingModule,\n\t\tBrowserAnimationsModule,\n\t\tMatIconModule,\n\t\tMatNativeDateModule,\n\t\tMatSidenavModule,\n\t\tCoreModule.forRoot(),\n\t],\n\tproviders: [],\n\tbootstrap: [AppComponent],\n})\nexport class AppModule {}\n","import { enableProdMode } from '@angular/core';\nimport { platformBrowserDynamic } from '@angular/platform-browser-dynamic';\n\nimport { AppModule } from './app/app.module';\nimport { environment } from './environments/environment';\n\nif (environment.production) {\n enableProdMode();\n}\n\nplatformBrowserDynamic().bootstrapModule(AppModule)\n .catch(err => console.error(err));\n"],"x_google_ignoreList":[]}