diff --git a/src/app/appmarket/admin/webhook/webhook-list/webhook-list.component.html b/src/app/appmarket/admin/webhook/webhook-list/webhook-list.component.html index 8bb2e3293da2157b960e25861a19eb2d388c5e8b..39080066002c1b24b9069ac5d1f72a99fdebc0c0 100644 --- a/src/app/appmarket/admin/webhook/webhook-list/webhook-list.component.html +++ b/src/app/appmarket/admin/webhook/webhook-list/webhook-list.component.html @@ -1,19 +1,24 @@ +<h4 class="header">{{ 'WEBHOOKS.TITLE' | translate }}</h4> <div style="display: flex; align-items: center; margin-top:20px"> - <div style="margin-right:20px"> - <div > + <div style=" display:flex; align-items: center;"> + <div style="margin-right:20px"> <button class="btn btn-primary" (click)="openModal()">{{'WEBHOOKS.NEW' | translate}}</button> </div> + <span class="p-input-icon-right"> + <i class="pi pi-search" style="font-size: 13px; top: 16px; margin-right: 5px;"></i> + <input pInputText class="form-control" name="search" id="search" + placeholder="Search" type="text" [(ngModel)]="searchValue" (ngModelChange)="filterWebhooks()"> + </span> </div> <div class="flex" style="margin-right:20px"> </div> </div> - <h4 class="header">{{ 'WEBHOOKS.TITLE' | translate }}</h4> <div class="background-section"> <p-table - [value]="webkooks" + [value]="filteredWebhooks" [paginator]="true" [rows]="maxItemsOnPage" [rowsPerPageOptions]="[15, 20, 25, 30, 50]" diff --git a/src/app/appmarket/admin/webhook/webhook-list/webhook-list.component.ts b/src/app/appmarket/admin/webhook/webhook-list/webhook-list.component.ts index 63500136dd4095e6f6474f4bb86338b56d05322c..4710b7e5fd5b3fea959499bf4d22fbe59fb99b0d 100644 --- a/src/app/appmarket/admin/webhook/webhook-list/webhook-list.component.ts +++ b/src/app/appmarket/admin/webhook/webhook-list/webhook-list.component.ts @@ -14,11 +14,13 @@ export class WebhookListComponent implements OnInit { public addedWebhook: Webhook = new Webhook(); public maxItemsOnPage = 15; + public searchValue = ''; + filteredWebhooks: Webhook[] = []; public authRequired: boolean = false; - public type =[ - { name: "DOMAIN_CREATION", value: "DOMAIN_CREATION" }, + public type = [ + { name: "DOMAIN_CREATION", value: "DOMAIN_CREATION" }, { name: "APPLICATION_DEPLOYMENT", value: "APPLICATION_DEPLOYMENT" }, { name: "USER_ASSIGNMENT", value: "USER_ASSIGNMENT" }, { name: "DOMAIN_GROUP_CHANGE", value: "DOMAIN_GROUP_CHANGE" } @@ -29,7 +31,7 @@ export class WebhookListComponent implements OnInit { constructor(private service: WebhookService) { - } + } ngOnInit() { this.refreshList(); @@ -38,6 +40,7 @@ export class WebhookListComponent implements OnInit { public refreshList() { this.service.getAll().subscribe(result => { this.webkooks = result; + this.filterWebhooks() }) } @@ -56,7 +59,13 @@ export class WebhookListComponent implements OnInit { this.modal.hide(); this.refreshList(); }); - } + filterWebhooks() { + const value = this.searchValue?.toLowerCase() || ''; + this.filteredWebhooks = this.webkooks.filter(webhook => + webhook.name?.toLowerCase().includes(value) || + webhook.id?.toString().includes(value) + ); + } } diff --git a/src/app/appmarket/appinstance/appinstancelist/appinstancelist.component.html b/src/app/appmarket/appinstance/appinstancelist/appinstancelist.component.html index ad6b14bde6f734117414ebba9a418c2082d80a08..e2e0bfa73612cd9b31dac8e6494b820a7ecd7a41 100644 --- a/src/app/appmarket/appinstance/appinstancelist/appinstancelist.component.html +++ b/src/app/appmarket/appinstance/appinstancelist/appinstancelist.component.html @@ -6,8 +6,8 @@ <input pInputText class="form-control" name="search" id="search" placeholder="Search" type="text" [(ngModel)]="searchValue"> </span> <div style="display: flex; margin: 0 20px"> - <p-checkbox id="show_my" inputId="show_my" binary="true" [(ngModel)]="showAll" (onChange)="onSelectionChange()" [ngModelOptions]="{standalone: true}" ngDefaultControl></p-checkbox> - <label for="show_my" style="text-wrap: nowrap">Show all instances</label> + <p-checkbox id="show_my" inputId="show_my" binary="true" [(ngModel)]="showMy" (onChange)="onSelectionChange()" [ngModelOptions]="{standalone: true}" ngDefaultControl></p-checkbox> + <label for="show_my" style="text-wrap: nowrap">Show only my instances</label> </div> <div style="display: flex"> <p-checkbox *domainRoles="['ROLE_DOMAIN_ADMIN', 'ROLE_SYSTEM_ADMIN', 'ROLE_OPERATOR'];domainId:domainId" diff --git a/src/app/appmarket/appinstance/appinstancelist/appinstancelist.component.ts b/src/app/appmarket/appinstance/appinstancelist/appinstancelist.component.ts index 6c09b32f86857764f1631aa41a5580bd094e2449..c81c76f21997933419b999402eaf4d2c86caa155 100644 --- a/src/app/appmarket/appinstance/appinstancelist/appinstancelist.component.ts +++ b/src/app/appmarket/appinstance/appinstancelist/appinstancelist.component.ts @@ -23,7 +23,7 @@ export enum AppInstanceListSelection { export class AppInstanceListComponent implements OnInit { public undeployedVisible = false; - public showAll = false; + public showMy = false; private readonly item_number_key: string = 'item_number_per_page'; private readonly list_selection_key: string = 'list_selection'; @@ -85,7 +85,7 @@ export class AppInstanceListComponent implements OnInit { const ls = AppInstanceListSelection[sessionStorage.getItem(this.list_selection_key)]; if (ls !== undefined) { this.listSelection = ls; - this.showAll = ls === AppInstanceListSelection.ALL; + this.showMy = ls === AppInstanceListSelection.MY; } console.log(this.listSelection); this.userDataService.selectedDomainId.subscribe(domainId => { @@ -124,9 +124,9 @@ export class AppInstanceListComponent implements OnInit { } public onSelectionChange() { - this.listSelection = this.showAll - ? AppInstanceListSelection.ALL - : AppInstanceListSelection.MY; + this.listSelection = this.showMy + ? AppInstanceListSelection.MY + : AppInstanceListSelection.ALL; sessionStorage.setItem(this.list_selection_key, AppInstanceListSelection[this.listSelection]); this.update(this.domainId); 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,