diff --git a/src/app/service/dashboard.service.ts b/src/app/service/dashboard.service.ts index eadf24747dfe0e98f2aca7dc637916e917fa4f03..3ed223f1a952aad604f94ff82b796ae5cf58da54 100644 --- a/src/app/service/dashboard.service.ts +++ b/src/app/service/dashboard.service.ts @@ -1,7 +1,8 @@ import { Injectable } from '@angular/core'; -import {HttpClient} from '@angular/common/http'; +import {HttpClient, HttpParams} from '@angular/common/http'; import {AppConfigService} from './appconfig.service'; import {GenericDataService} from './genericdata.service'; +import {Observable} from 'rxjs'; @Injectable({ providedIn: 'root' @@ -12,8 +13,12 @@ export class DashboardService extends GenericDataService { super(http, appConfig); } - public getAdmin() { - return this.get(this.appConfig.getApiUrl() + '/dashboard/admin') + public getAdmin(startDate: string, endDate: string) { + const params = new HttpParams() + .set('startDate', startDate) + .set('end', endDate); + + return this.http.get(this.appConfig.getApiUrl() + '/dashboard/admin', { params }); } public getDomainAdmin(domainId?: number) { diff --git a/src/app/shared/admin-dashboard/admin-dashboard.component.html b/src/app/shared/admin-dashboard/admin-dashboard.component.html index b5e72786990f7cd04b73149b71c0554f90c99594..7e01f82968e8ac8e082a90d767a6e52c5fb10274 100644 --- a/src/app/shared/admin-dashboard/admin-dashboard.component.html +++ b/src/app/shared/admin-dashboard/admin-dashboard.component.html @@ -3,20 +3,35 @@ <div style="display: flex; flex-wrap: wrap;"> <div class="background-section" style="flex: 1 1 30%; margin-right: 20px"> <h5 style="font-weight: bold">Current number of user </h5> - <h1 style="font-weight: bold">{{ adminData.userCount}}</h1> + <h1 style="font-weight: bold">{{ adminData?.userCount}}</h1> </div> <div class="background-section" style="flex: 1 1 30%; margin-right: 20px"> <h5 style="font-weight: bold">Current number of domains </h5> - <h1 style="font-weight: bold">{{ adminData.domainsCount}}</h1> + <h1 style="font-weight: bold">{{ adminData?.domainsCount}}</h1> </div> <div class="background-section" style="flex: 1 1 30% ;"> <h5 style="font-weight: bold">Current number of deployed applications </h5> - <h1 style="font-weight: bold">{{ adminData.instanceCount}}</h1> + <h1 style="font-weight: bold">{{ adminData?.instanceCount}}</h1> </div> </div> <div class="grid" style="display: flex; justify-content: space-between;"> <div class="background-section width-50 " style="margin-right: 20px"> - <h5 style="font-weight: bold">Application deployments in the last week</h5> + <div style=" display: flex; justify-content: space-between;"> + <h5 style="font-weight: bold">Application deployments <ng-container *ngIf="rangeDates && rangeDates[0] && rangeDates[1]; else lastWeek"> + from {{ rangeDates[0] | date:'dd.MM.yyyy' }} to {{ rangeDates[1] | date:'dd.MM.yyyy' }} + </ng-container> + <ng-template #lastWeek>in the last week</ng-template></h5> + <p-calendar + [(ngModel)]="rangeDates" + selectionMode="range" + [readonlyInput]="true" + (ngModelChange)="onDateChange($event)" + placeholder="Select date range" + showClear="true" + dateFormat="dd.mm.yy" + /> + </div> + <p-table [value]="instanceCountInPeriodDetails" [scrollable]="true" [style]="{'width': '100%', 'max-height': '50vh'}"> <ng-template pTemplate="header"> <tr> @@ -28,7 +43,7 @@ </ng-template> <ng-template pTemplate="body" let-instance> <tr> - <td><img style="height: 40px" src="../../../assets/images/app-logo-example.png"/></td> + <td><img style="height: 40px" [src]="(appImagesService.getAppLogoUrl(instance.appId) | secure) || '../../../assets/images/app-logo-example.png'"/></td> <td>{{instance.applicationName}}</td> <td>{{instance.applicationVersion}}</td> <td>{{instance.domainName}}</td> @@ -98,7 +113,7 @@ </ng-template> <ng-template pTemplate="body" let-app> <tr> - <td><img style="height: 40px" src="../../../assets/images/app-logo-example.png"/></td> + <td><img style="height: 40px" [src]="(appImagesService.getAppLogoUrl(app.appId) | secure) || '../../../assets/images/app-logo-example.png'"/></td> <td>{{app.appName}}</td> <td>{{app.appId}}</td> <td>{{app.instanceName}}</td> diff --git a/src/app/shared/admin-dashboard/admin-dashboard.component.spec.ts b/src/app/shared/admin-dashboard/admin-dashboard.component.spec.ts index 1877b765c09ce11d4eb5b6db1900c7627f0837b2..860578e237ff398405ccb76b4281b4d303439d19 100644 --- a/src/app/shared/admin-dashboard/admin-dashboard.component.spec.ts +++ b/src/app/shared/admin-dashboard/admin-dashboard.component.spec.ts @@ -4,6 +4,8 @@ import { AdminDashboardComponent } from './admin-dashboard.component'; import { DashboardService } from '../../service/dashboard.service'; import { UserDataService } from '../../service/userdata.service'; import { CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA } from '@angular/core'; +import {AppImagesService, AppsService} from '../../service'; +import {ActivatedRoute} from '@angular/router'; describe('AdminDashboardComponent', () => { let component: AdminDashboardComponent; @@ -11,9 +13,17 @@ describe('AdminDashboardComponent', () => { let mockDashboardService: jasmine.SpyObj<DashboardService>; let mockUserDataService: jasmine.SpyObj<UserDataService>; + const applications = [ + { id: 1, name: 'App1' }, + { id: 2, name: 'App2' } + ]; + beforeEach(async () => { mockDashboardService = jasmine.createSpyObj('DashboardService', ['getAdmin', 'getDomainAdmin']); mockUserDataService = jasmine.createSpyObj('UserDataService', ['selectedDomainId']); + const appImagesServiceSpy = jasmine.createSpyObj('AppImagesService', ['getAppLogoUrl']); + const appsServiceSpy = jasmine.createSpyObj('AppsService', ['getAllApplicationBase']); + appsServiceSpy.getAllApplicationBase.and.returnValue(of(applications)); mockUserDataService.selectedDomainId = of(123); // Replace 'test-domain-id' with a numeric value mockDashboardService.getAdmin.and.returnValue(of({ popularApps: { App1: 10, App2: 20 }, @@ -27,7 +37,10 @@ describe('AdminDashboardComponent', () => { declarations: [AdminDashboardComponent], providers: [ { provide: DashboardService, useValue: mockDashboardService }, - { provide: UserDataService, useValue: mockUserDataService } + {provide: AppImagesService, useValue: appImagesServiceSpy}, + { provide: UserDataService, useValue: mockUserDataService }, + {provide: ActivatedRoute, useValue: {params: of({id: 1})}}, + { provide: AppsService, useValue: appsServiceSpy } ], schemas: [NO_ERRORS_SCHEMA, CUSTOM_ELEMENTS_SCHEMA] // Add this to allow unknown properties }).compileComponents(); @@ -61,7 +74,7 @@ describe('AdminDashboardComponent', () => { it('should call chartData method and populate chart data', () => { component.chartData(); - expect(component.popularAppsChartData.labels).toEqual(['App1', 'App2']); - expect(component.popularAppsChartData.datasets[0].data).toEqual([10, 20]); + expect(component.popularAppsChartData.labels).toEqual(['App2', 'App1']); + expect(component.popularAppsChartData.datasets[0].data).toEqual([20, 10]); }); }); diff --git a/src/app/shared/admin-dashboard/admin-dashboard.component.ts b/src/app/shared/admin-dashboard/admin-dashboard.component.ts index 3ae9e16672ced2b5176ef5633728141465828473..66918dda6626f8d58a51b5cb7a07c5c47a65ec0d 100644 --- a/src/app/shared/admin-dashboard/admin-dashboard.component.ts +++ b/src/app/shared/admin-dashboard/admin-dashboard.component.ts @@ -1,6 +1,8 @@ import { Component } from '@angular/core'; import {DashboardService} from '../../service/dashboard.service'; import {UserDataService} from '../../service/userdata.service'; +import {AppImagesService, AppsService} from '../../service'; +import {ActivatedRoute} from '@angular/router'; @Component({ selector: 'app-admin-dashboard', @@ -16,24 +18,29 @@ export class AdminDashboardComponent { instanceCountInPeriodDetails: any[] = []; applicationUpgradeStatus: any[] = []; domainId; + public appId: number; + appNameToIdMap: { [key: string]: number } = {}; + rangeDates: Date[] = []; + + startDate; + endDate; constructor(protected dashboardService: DashboardService, - private userDataService: UserDataService) { + private userDataService: UserDataService, + public appImagesService: AppImagesService, + private route: ActivatedRoute, + private appsService: AppsService) { } ngOnInit() { + this.setDefaultDate() + this.getAdmin() this.userDataService.selectedDomainId.subscribe((domainId) => { this.domainId = domainId this.getDomainAdmin() }); - this.dashboardService.getAdmin().subscribe( - (response) => { - this.adminData = response; - this.chartData(); - this.instanceCountInPeriodDetails = this.adminData.instanceCountInPeriodDetails; - } - ) + const documentStyle = getComputedStyle(document.documentElement); const textColor = documentStyle.getPropertyValue('--text-color'); const textColorSecondary = documentStyle.getPropertyValue('--text-color-secondary'); @@ -76,8 +83,11 @@ export class AdminDashboardComponent { } chartData() { - const appNames = Object.keys(this.adminData.popularApps); - const appValues = Object.values(this.adminData.popularApps); + const entries = Object.entries(this.adminData.popularApps); + const sortedEntries = entries.sort((a, b) => Number(b[1]) - Number(a[1])); + const topEntries = sortedEntries.slice(0, 10); + const appNames = topEntries.map(e => e[0]); + const appValues = topEntries.map(e => e[1]); this.popularAppsChartData = { labels: appNames, @@ -95,6 +105,23 @@ export class AdminDashboardComponent { formatDate(date: any): string { return new Date(date).toLocaleString(); } + getAdmin() { + this.appsService.getAllApplicationBase().subscribe(apps => { + apps.forEach(app => { + this.appNameToIdMap[app.name] = app.id; + }); + this.dashboardService.getAdmin(this.startDate, this.endDate).subscribe( + (response) => { + this.adminData = response; + this.instanceCountInPeriodDetails = this.adminData.instanceCountInPeriodDetails.map(instance => ({ + ...instance, + appId: this.appNameToIdMap[instance.applicationName] || null + })); + this.chartData(); + } + ); + }); + } getDomainAdmin() { this.dashboardService.getDomainAdmin(this.domainId).subscribe( (response) => { @@ -103,4 +130,20 @@ export class AdminDashboardComponent { } ) } + onDateChange(dates: Date[] | null) { + if (!dates || dates.length < 2 || !dates[0] || !dates[1]) { + this.setDefaultDate(); + } else { + this.startDate = dates[0].toISOString(); + this.endDate = dates[1].toISOString(); + } + this.getAdmin() + } + setDefaultDate() { + const end = new Date(); + const start = new Date(); + start.setDate(start.getDate() - 7); + this.startDate = start.toISOString(); + this.endDate = end.toISOString(); + } } diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 167728209e92bacbd66f831a2f55b99259d32bf6..ffc9575310adbbc2fa85aa76412a9275eeb6d41c 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -73,6 +73,7 @@ import {ChartModule} from 'primeng/chart'; import { RolesExcludedDirective } from '../directive/roles-exluded.directive'; import { FileUploadModule } from 'primeng/fileupload'; import { RecaptchaVisibilityService } from '../service/recaptcha-visibility.service'; +import {CalendarModule} from 'primeng/calendar'; @@ -102,7 +103,8 @@ import { RecaptchaVisibilityService } from '../service/recaptcha-visibility.serv ButtonModule, ChartModule, FileUploadModule, - TableModule + TableModule, + CalendarModule ], declarations: [ RateComponent,