diff --git a/package-lock.json b/package-lock.json index 5e89650c86fcdc0c3ef00f3e1b9b1a9ef4f79f84..b377d483f3953dc89516118f9124b1f0b1dd380f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31,6 +31,7 @@ "ajv": "^6.12.6", "angular-password-strength-meter": "^11.0.0", "bootstrap": "^3.4.1", + "chart.js": "^4.4.8", "document-register-element": "^1.14.10", "jquery": "^3.6.0", "lodash": "^4.17.21", @@ -3853,6 +3854,12 @@ "resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.4.0.tgz", "integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==" }, + "node_modules/@kurkle/color": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz", + "integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==", + "license": "MIT" + }, "node_modules/@leichtgewicht/ip-codec": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", @@ -6271,6 +6278,18 @@ "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true }, + "node_modules/chart.js": { + "version": "4.4.8", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.8.tgz", + "integrity": "sha512-IkGZlVpXP+83QpMm4uxEiGqSI7jFizwVtF3+n5Pc3k7sMO+tkd0qxh2OzLhenM0K80xtmAONWGBn082EiBQSDA==", + "license": "MIT", + "dependencies": { + "@kurkle/color": "^0.3.0" + }, + "engines": { + "pnpm": ">=8" + } + }, "node_modules/chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -18401,6 +18420,11 @@ "resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.4.0.tgz", "integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==" }, + "@kurkle/color": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz", + "integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==" + }, "@leichtgewicht/ip-codec": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", @@ -20278,6 +20302,14 @@ "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true }, + "chart.js": { + "version": "4.4.8", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.8.tgz", + "integrity": "sha512-IkGZlVpXP+83QpMm4uxEiGqSI7jFizwVtF3+n5Pc3k7sMO+tkd0qxh2OzLhenM0K80xtmAONWGBn082EiBQSDA==", + "requires": { + "@kurkle/color": "^0.3.0" + } + }, "chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", diff --git a/package.json b/package.json index 645cfdaa6ab9c4828071e69753316a491fdb3a50..b33a36f15661b2377a497bccb465d57b59fd5c23 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "ajv": "^6.12.6", "angular-password-strength-meter": "^11.0.0", "bootstrap": "^3.4.1", + "chart.js": "^4.4.8", "document-register-element": "^1.14.10", "jquery": "^3.6.0", "lodash": "^4.17.21", diff --git a/src/app/app.component.css b/src/app/app.component.css index 2d3060c1f46d28b991427f655ffdb30d15344f47..035dee4e90e9ec4bc20dd27e9817e86ba1045d3d 100644 --- a/src/app/app.component.css +++ b/src/app/app.component.css @@ -8,8 +8,52 @@ body{ flex-direction: column; } +.flex-container-row { + min-height:100vh; + display: flex; + flex-direction: row; +} + +.flex-container-column { + flex: 1; + height:100vh; + display: flex; + flex-direction: column; +} + .flex-spacer { flex: 1 0 auto; - width: 100%; - height: 100%; + height: 100% +} + +.logged-out-layout { + display: flex; + flex-direction: column; + min-height: 100vh; +} +.logged-in-layout { + display: flex; + flex-direction: row; + min-height: 100vh; +} +.content-area { + flex: 1; + padding: 1rem; + display: flex; + flex-direction: column; + min-height: calc(100vh - 150px); +} + +.router-outlet { + flex: 1; /* Wypełnia dostępną przestrzeń */ +} + +.side-menu { + /*background-color: var(--menu-color);*/ + /*color: var(--l-text-color);*/ + /*height: 100vh ;*/ + /*position: fixed;*/ + /*top: 0;*/ + /*left: 0;*/ + /*padding: 1rem;*/ } diff --git a/src/app/app.component.html b/src/app/app.component.html index a8c945d797108b8d4379b825adf5f8a7d7564373..479de55ee239bb09c3c58c025f65c0eef14ed746 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,6 +1,25 @@ -<div class="flex-container"> - <app-navbar></app-navbar> +<div *ngIf="!isLoggedIn" class="flex-container"> <!--- --> + <app-navbar ></app-navbar> <router-outlet></router-outlet> <div class="flex-spacer"></div> <nmaas-footer></nmaas-footer> + </div> + +<div *ngIf="isLoggedIn" class="flex-container-row"> + <div class="side-menu"> + <app-left-menu [style]="{'display': 'flex', 'height': '100%'}"></app-left-menu> + </div> + + <div class="flex flex-column" [style]="{'margin-left': 'var(--left-panel-width)', 'width': 'calc(100vw - var(--left-panel-width))'}"> + <div class="content-area"> + <router-outlet></router-outlet> + </div> + + <nmaas-footer></nmaas-footer> + </div> + + +</div> + +<app-toast-container aria-live="polite" aria-atomic="true"></app-toast-container> diff --git a/src/app/app.component.spec.ts b/src/app/app.component.spec.ts index c9f6f599a90484886cf179ce005644d846eec145..67ac91ebe50e5c5f3f2cfe3b28052b2120ca3e51 100644 --- a/src/app/app.component.spec.ts +++ b/src/app/app.component.spec.ts @@ -12,6 +12,10 @@ import {AuthService} from './auth/auth.service'; import {JwtHelperService, JwtModule} from '@auth0/angular-jwt'; import {ServiceUnavailableService} from './service-unavailable/service-unavailable.service'; import {SharedModule} from './shared'; +import { LeftMenuComponent } from './shared/left-menu/left-menu.component'; +import { ToastContainerComponent } from './shared/toast-container/toast-container.component'; +import { CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA } from '@angular/core'; +import { MessageService } from 'primeng/api'; import {HttpClientTestingModule} from '@angular/common/http/testing'; class MockConfigurationService { @@ -75,7 +79,9 @@ describe('App: NmaasPortal', () => { beforeEach(() => { TestBed.configureTestingModule({ declarations: [ - AppComponent + AppComponent, + LeftMenuComponent, + ToastContainerComponent ], imports: [ HttpClientTestingModule, @@ -102,8 +108,13 @@ describe('App: NmaasPortal', () => { TranslateService, AuthService, JwtHelperService, + MessageService, {provide: ServiceUnavailableService, useClass: MockServiceUnavailableService} - ] + ], + schemas: [ + CUSTOM_ELEMENTS_SCHEMA, + NO_ERRORS_SCHEMA + ] }); }); diff --git a/src/app/app.component.ts b/src/app/app.component.ts index bf87ca2b95b767c7fb09a4f6ee7efc094eb63c72..21d613bc01e9360cf4c5aeb2ef9ede645bc424bd 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -16,6 +16,7 @@ export class AppComponent { config: any; private timer: IdleTimer; + public isLoggedIn = false; constructor(private appConfigService: AppConfigService, private configService: ConfigurationService, private authService: AuthService, private translate: TranslateService, @@ -23,6 +24,7 @@ export class AppComponent { } async ngOnInit() { + this.isLoggedIn = this.authService.isLogged() ; if (this.serviceHealth.isServiceAvailable === false) { this.router.navigate(['/service-unavailable']); } @@ -31,7 +33,9 @@ export class AppComponent { console.debug('Configuration: ' + JSON.stringify(this.config)); await this.delay(2000); console.warn("User logged ? -", this.authService.isLogged()) + this.updateLogin(); if (this.authService.isLogged()) { + this.isLoggedIn = true; this.timer = new IdleTimer({ timeout: 900, // 15 min onTimeout: () => { @@ -42,6 +46,13 @@ export class AppComponent { } } + private updateLogin() { + this.authService.isLoggedIn$.subscribe(isLogged => { + console.log("User state update", isLogged); + this.isLoggedIn = isLogged; + }); + } + public handleDefaultLanguage(): void { if (this.authService.getSelectedLanguage() != null) { this.setLanguage(this.authService.getSelectedLanguage()); diff --git a/src/app/app.module.ts b/src/app/app.module.ts index c3d69f86790494116874bf7bf234045999729b77..be39d339e116fb2a4cf03099c6c8666ea24ace06 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -26,6 +26,14 @@ import {ServiceUnavailableService} from './service-unavailable/service-unavailab import {NgTerminalModule} from 'ng-terminal'; import { provideZxvbnServiceForPSM } from 'angular-password-strength-meter/zxcvbn'; import { FormioModule } from '@formio/angular'; +import { LeftMenuComponent } from './shared/left-menu/left-menu.component'; +import { ToastContainerComponent, ToastMode } from './shared/toast-container/toast-container.component'; +import { MessageService } from 'primeng/api'; +import { ToastModule } from 'primeng/toast'; +import {SplitButtonModule} from 'primeng/splitbutton'; +import {MenuModule} from 'primeng/menu'; +import { AdminLeftMenuComponent } from './shared/admin-left-menu/admin-left-menu.component'; +import {AccordionModule} from 'primeng/accordion'; export function appConfigFactory(config: AppConfigService) { return function create() { @@ -52,6 +60,9 @@ export const jwtOptionsFactory = (appConfig: AppConfigService) => ({ @NgModule({ declarations: [ AppComponent, + LeftMenuComponent, + ToastContainerComponent, + AdminLeftMenuComponent ], imports: [ BrowserModule, @@ -78,7 +89,11 @@ export const jwtOptionsFactory = (appConfig: AppConfigService) => ({ } }), NgTerminalModule, - FormioModule + FormioModule, + ToastModule, + SplitButtonModule, + MenuModule, + AccordionModule, ], providers: [ AuthGuard, @@ -98,7 +113,8 @@ export const jwtOptionsFactory = (appConfig: AppConfigService) => ({ useFactory: serviceAvailableFactory, deps: [AppConfigService, HttpClient, ServiceUnavailableService], multi: true, - } + }, + MessageService ], exports: [ TranslateModule diff --git a/src/app/appmarket/admin/clusters/details/clusterdetails.component.html b/src/app/appmarket/admin/clusters/details/clusterdetails.component.html index bed274174116378cf2e63aabfa68b5bfaec37345..464ced99150a4f51d3e6e2fb7f4aece24ad04137 100644 --- a/src/app/appmarket/admin/clusters/details/clusterdetails.component.html +++ b/src/app/appmarket/admin/clusters/details/clusterdetails.component.html @@ -1 +1 @@ -<nmaas-clusterdetails class="col-sm-12 col-sm-12 col-md-12" [cluster]="cluster" [error]="error" [mode]="getCurrentMode()" [allowedModes]="[ComponentMode.VIEW, ComponentMode.CREATE]"></nmaas-clusterdetails> +<nmaas-clusterdetails class="" [cluster]="cluster" [error]="error" [mode]="getCurrentMode()" [allowedModes]="[ComponentMode.VIEW, ComponentMode.CREATE]"></nmaas-clusterdetails> diff --git a/src/app/appmarket/admin/configuration/configuration.routes.ts b/src/app/appmarket/admin/configuration/configuration.routes.ts index 3d232bc59f5f9a1407e2e5237807268e7d989421..adde5168ce4badc1f068931ff7978c8122ecdbbf 100644 --- a/src/app/appmarket/admin/configuration/configuration.routes.ts +++ b/src/app/appmarket/admin/configuration/configuration.routes.ts @@ -4,6 +4,6 @@ import {RoleGuard} from "../../../auth/role.guard"; import {ConfigurationDetailsComponent} from "./index"; export const ConfigurationRoutes: Route[] = [ - {path: 'admin/configuration', component: ConfigurationDetailsComponent, canActivate: [AuthGuard, RoleGuard], + {path: 'configuration', component: ConfigurationDetailsComponent, canActivate: [AuthGuard, RoleGuard], data:{roles: ['ROLE_SYSTEM_ADMIN']} } ]; diff --git a/src/app/appmarket/admin/configuration/details/configurationdetails.component.html b/src/app/appmarket/admin/configuration/details/configurationdetails.component.html index 30c84ce7fe97321197b2faa778088dd1a7e6df4f..ce27f1b5be109d61b374c1b97bcd5fa5d2cbc1a0 100644 --- a/src/app/appmarket/admin/configuration/details/configurationdetails.component.html +++ b/src/app/appmarket/admin/configuration/details/configurationdetails.component.html @@ -1,6 +1,6 @@ -<div class="col-sm-12 col-sm-12 col-md-12" > - <div class="panel panel-default"> - <div class="panel-heading">{{ 'PORTAL_CONFIGURATION.TITLE' | translate }}</div> +<div class="" > + <div class="background-section"> + <h4 style="font-size:15px; font-weight: bold">{{ 'PORTAL_CONFIGURATION.TITLE' | translate }}</h4> <div class="panel-body"> <form (submit)="save()" class="form-horizontal" #configurationForm="ngForm" *ngIf="this.configuration"> <div class="form-group"> diff --git a/src/app/appmarket/admin/languagemanagement/languagelist/languagelist.component.css b/src/app/appmarket/admin/languagemanagement/languagelist/languagelist.component.css index 76a9184a9116d73518d2bd3b9db0239df54da9cb..4c3941d86d1a667e26892873acca26b27c3a880f 100644 --- a/src/app/appmarket/admin/languagemanagement/languagelist/languagelist.component.css +++ b/src/app/appmarket/admin/languagemanagement/languagelist/languagelist.component.css @@ -13,6 +13,29 @@ tr.clickable { cursor: pointer; } -.dropdown:hover .dropdown-menu { - display: block; +/*.dropdown:hover .dropdown-menu {*/ +/* display: block;*/ +/*}*/ +:host ::ng-deep .p-datatable .p-datatable-thead > tr > th{ + border: 1px solid #E0E2E5; + background:transparent; + border-width: 0 0 1px 0; +} +:host ::ng-deep .p-datatable .p-datatable-tbody > tr > td { + text-align: left; + border: 1px solid #E0E2E5; + border-width: 0 0 1px 0; + padding: 1rem 1rem; +} +:host ::ng-deep .p-datatable .p-paginator-bottom{ + height: 40px; + background: transparent; + border: none; + margin-top:10px; +} +:host ::ng-deep .p-datatable .p-datatable-tbody > tr{ + background: transparent; +} +:host ::ng-deep .p-datatable>.p-datatable-wrapper { + overflow: visible; } diff --git a/src/app/appmarket/admin/languagemanagement/languagelist/languagelist.component.html b/src/app/appmarket/admin/languagemanagement/languagelist/languagelist.component.html index fb9fcc5dce55c757cd218532db961cea45ee2c53..12bab7e9b17986da97dca30fc7dfda12de7c6acc 100644 --- a/src/app/appmarket/admin/languagemanagement/languagelist/languagelist.component.html +++ b/src/app/appmarket/admin/languagemanagement/languagelist/languagelist.component.html @@ -1,40 +1,40 @@ -<div class="col-sm-12 col-sm-offset-1 col-sm-10 col-md-offset-1 col-md-10"> - <h3>{{'LANGUAGE_MANAGEMENT.TITLE' | translate }}</h3> - <hr> - <table class="table table-hover table-condensed" aria-describedby="Language list table"> - <thead> - <tr> - <th scope="col"> </th> - <th scope="col">{{'LANGUAGE_MANAGEMENT.LANGUAGE' | translate }}</th> - <th scope="col"> </th> - </tr> - </thead> +<div class=""> + <h4 class="header">{{'LANGUAGE_MANAGEMENT.TITLE' | translate }}</h4> + <div class="background-section"> + <p-table [value]="languages" class="p-datatable-hover p-datatable-sm" [responsive]="true"> + <ng-template pTemplate="header"> + <tr> + <th></th> + <th>{{'LANGUAGE_MANAGEMENT.LANGUAGE' | translate }}</th> + <th></th> + </tr> + </ng-template> - <tbody> - <ng-template ngFor let-lang [ngForOf]="languages" let-i="index"> - <tr class="clickable" [routerLink]="['/admin/languages/' + lang.language]"> - <td><img alt="language" src="assets/images/country/{{lang.language}}_circle.png" style="height: 22px;"></td> - <td>{{'LANGUAGE.' + lang.language.toUpperCase() + '_LABEL' | translate}}</td> + <ng-template pTemplate="body" let-lang> + <tr class="clickable" > + <td [routerLink]="['/admin/languages/' + lang.language]"> + <img alt="language" src="assets/images/country/{{lang.language}}_circle.png" style="height: 22px;"> + </td> + <td [routerLink]="['/admin/languages/' + lang.language]">{{'LANGUAGE.' + lang.language.toUpperCase() + '_LABEL' | translate}}</td> <td class="text-right"> <span class="dropdown"> - <a style="display: inline-block" class="dropdown-toggle " aria-expanded="false" aria-haspopup="true" - data-toggle="dropdown" href="#" role="button"> - <em class="fas fa-cog icon-black icon-bigger"></em> + <a style="display: inline-block" class="dropdown-toggle " aria-expanded="false" aria-haspopup="true" data-toggle="dropdown" href="#" role="button"> + <em class="pi pi-cog" style="font-size: 1.8rem; color: var(--l-text-color)"></em> </a> <ul class="dropdown-menu pull-right-drop"> <li> <a [routerLink]="['/admin/languages/' + lang.language]"> - {{ 'LANGUAGE_MANAGEMENT.EDIT_BUTTON' | translate }} + {{ 'LANGUAGE_MANAGEMENT.EDIT_BUTTON' | translate }} </a> </li> <li> <a *ngIf="lang.enabled" (click)="changeLanguageState(lang)"> - {{ 'LANGUAGE_MANAGEMENT.LANGUAGE_DISABLED' | translate }} + {{ 'LANGUAGE_MANAGEMENT.LANGUAGE_DISABLED' | translate }} </a> </li> <li> <a *ngIf="!lang.enabled" (click)="changeLanguageState(lang)"> - {{ 'LANGUAGE_MANAGEMENT.LANGUAGE_ENABLED' | translate }} + {{ 'LANGUAGE_MANAGEMENT.LANGUAGE_ENABLED' | translate }} </a> </li> </ul> @@ -42,8 +42,9 @@ </td> </tr> </ng-template> - </tbody> - </table> + </p-table> + </div> + </div> <nmaas-modal (click)="modal.hide()"> diff --git a/src/app/appmarket/admin/languagemanagement/languagelist/languagelist.component.spec.ts b/src/app/appmarket/admin/languagemanagement/languagelist/languagelist.component.spec.ts index d3b5af459a7e540f00cb8a5af733f244aad46240..c444bc324042ff3be92876785d98b41622fad058 100644 --- a/src/app/appmarket/admin/languagemanagement/languagelist/languagelist.component.spec.ts +++ b/src/app/appmarket/admin/languagemanagement/languagelist/languagelist.component.spec.ts @@ -8,6 +8,7 @@ import {InternationalizationService} from '../../../../service/internationalizat import {AppConfigService} from '../../../../service'; import {of} from 'rxjs'; import {ModalComponent} from '../../../../shared/modal'; +import {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core'; describe('LanguagelistComponent', () => { let component: LanguageListComponent; @@ -35,7 +36,8 @@ describe('LanguagelistComponent', () => { } }, {provide: AppConfigService, useValue: {}} - ] + ], + schemas: [CUSTOM_ELEMENTS_SCHEMA] }) .compileComponents(); })); diff --git a/src/app/appmarket/admin/languagemanagement/languagemanagement.module.ts b/src/app/appmarket/admin/languagemanagement/languagemanagement.module.ts index 1fb084a507599dfa164511b46d80f94853a41fee..801e94f4dbc50cfa8eece97b8abea1c35f436ee2 100644 --- a/src/app/appmarket/admin/languagemanagement/languagemanagement.module.ts +++ b/src/app/appmarket/admin/languagemanagement/languagemanagement.module.ts @@ -8,17 +8,19 @@ import {TranslateModule} from '@ngx-translate/core'; import {SharedModule} from '../../../shared'; import {FormsModule} from '@angular/forms'; import {InputSwitchModule} from 'primeng/inputswitch'; +import {TableModule} from 'primeng/table'; @NgModule({ declarations: [LanguageListComponent, LanguageDetailsComponent], - imports: [ - CommonModule, - FormsModule, - InputSwitchModule, - RouterModule, - SharedModule, - TranslateModule.forChild() - ], + imports: [ + CommonModule, + FormsModule, + InputSwitchModule, + RouterModule, + SharedModule, + TranslateModule.forChild(), + TableModule + ], providers: [InternationalizationService] }) export class LanguageManagementModule { } diff --git a/src/app/appmarket/admin/languagemanagement/languagemanagement.routes.ts b/src/app/appmarket/admin/languagemanagement/languagemanagement.routes.ts index dcc2b8c840b7a220ef228fa3c9b5105fde2f714d..6586b88743e1b3164e96e5ba749cd6ca2ef045ce 100644 --- a/src/app/appmarket/admin/languagemanagement/languagemanagement.routes.ts +++ b/src/app/appmarket/admin/languagemanagement/languagemanagement.routes.ts @@ -5,6 +5,6 @@ import {AuthGuard} from "../../../auth/auth.guard"; import {RoleGuard} from "../../../auth/role.guard"; export const LanguageManagementRoutes: Route[] = [ - {path: 'admin/languages', component: LanguageListComponent, canActivate: [AuthGuard, RoleGuard], data:{ roles: 'ROLE_SYSTEM_ADMIN'}}, - {path: 'admin/languages/:id', component: LanguageDetailsComponent, canActivate: [AuthGuard, RoleGuard], data: { roles: 'ROLE_SYSTEM_ADMIN'}} + {path: 'languages', component: LanguageListComponent, canActivate: [AuthGuard, RoleGuard], data:{ roles: 'ROLE_SYSTEM_ADMIN'}}, + {path: 'languages/:id', component: LanguageDetailsComponent, canActivate: [AuthGuard, RoleGuard], data: { roles: 'ROLE_SYSTEM_ADMIN'}} ]; \ No newline at end of file diff --git a/src/app/appmarket/admin/monitor/list/monitor-list.component.html b/src/app/appmarket/admin/monitor/list/monitor-list.component.html index d762b99e4772dd1b0b8e7f24401d5914d3dd2f1f..cdb2ce67972016f5020c03ec64f06851d24309e5 100644 --- a/src/app/appmarket/admin/monitor/list/monitor-list.component.html +++ b/src/app/appmarket/admin/monitor/list/monitor-list.component.html @@ -1,36 +1,36 @@ -<div class="col-sm-12 col-sm-12 col-md-12"> - <h3>{{ 'MONITOR.TITLE' | translate }}</h3> - <br> - <table class="table table-hover table-condensed" aria-describedby="Service status table"> - <thead> - <tr> - <th scope="col">{{ 'MONITOR.SERVICE_NAME' | translate }}</th> - <th scope="col">{{ 'MONITOR.LAST_CHECK' | translate }}</th> - <th scope="col">{{ 'MONITOR.LAST_SUCCESS' | translate }}</th> - <th scope="col">{{ 'MONITOR.CHECK_INTERVAL' | translate }}</th> - <th scope="col"> </th> - </tr> - </thead> +<div class=""> + <h4 class="header">{{ 'MONITOR.TITLE' | translate }}</h4> + <div class="background-section"> + <table class="table table-hover table-condensed" aria-describedby="Service status table"> + <thead> + <tr> + <th scope="col">{{ 'MONITOR.SERVICE_NAME' | translate }}</th> + <th scope="col">{{ 'MONITOR.LAST_CHECK' | translate }}</th> + <th scope="col">{{ 'MONITOR.LAST_SUCCESS' | translate }}</th> + <th scope="col">{{ 'MONITOR.CHECK_INTERVAL' | translate }}</th> + <th scope="col"> </th> + </tr> + </thead> - <tbody> - <ng-template ngFor let-entry [ngForOf]="monitorEntries"> - <tr class="clickable" [routerLink]="['view/', entry.serviceName.toString()]" - [ngClass]="{'entry-unknown': entry.status === null || entry.status === undefined, + <tbody> + <ng-template ngFor let-entry [ngForOf]="monitorEntries"> + <tr class="clickable" [routerLink]="['view/', entry.serviceName.toString()]" + [ngClass]="{'entry-unknown': entry.status === null || entry.status === undefined, 'entry-deactivated': !entry.active, 'entry-success': entry.status.toString() === 'SUCCESS', 'entry-failure': entry.status.toString() === 'FAILURE'}"> - <td>{{services[entry.serviceName]}}</td> - <td> - <span *ngIf="entry.lastCheck">{{entry.lastCheck | date:'medium'}}</span> - <span *ngIf="!entry.lastCheck">---</span> - </td> - <td> - <span *ngIf="entry.lastSuccess">{{entry.lastSuccess | date:'medium'}}</span> - <span *ngIf="!entry.lastSuccess">---</span> - </td> - <td>{{getIntervalCheck(entry.checkInterval, entry.timeFormat)}}</td> - <td> + <td>{{services[entry.serviceName]}}</td> + <td> + <span *ngIf="entry.lastCheck">{{entry.lastCheck | date:'medium'}}</span> + <span *ngIf="!entry.lastCheck">---</span> + </td> + <td> + <span *ngIf="entry.lastSuccess">{{entry.lastSuccess | date:'medium'}}</span> + <span *ngIf="!entry.lastSuccess">---</span> + </td> + <td>{{getIntervalCheck(entry.checkInterval, entry.timeFormat)}}</td> + <td> <span class="dropdown"> <a style="display: inline-block" class="dropdown-toggle " aria-expanded="false" aria-haspopup="true" @@ -51,9 +51,10 @@ </li> </ul> </span> - </td> - </tr> - </ng-template> - </tbody> - </table> + </td> + </tr> + </ng-template> + </tbody> + </table> + </div> </div> diff --git a/src/app/appmarket/admin/monitor/monitor.routes.ts b/src/app/appmarket/admin/monitor/monitor.routes.ts index b91a355cc48de6fb9712241163d8601e10e42283..2ae94cfbf60f50dc7f18bd5232772a60edd764dc 100644 --- a/src/app/appmarket/admin/monitor/monitor.routes.ts +++ b/src/app/appmarket/admin/monitor/monitor.routes.ts @@ -6,11 +6,11 @@ import {MonitorListComponent} from './list/monitor-list.component'; import {ComponentMode} from '../../../shared'; export const MonitorRoutes: Route[] = [ - {path: 'admin/monitor', component: MonitorListComponent, canActivate: [AuthGuard, RoleGuard], + {path: 'monitor', component: MonitorListComponent, canActivate: [AuthGuard, RoleGuard], data: {roles: ['ROLE_SYSTEM_ADMIN', 'ROLE_OPERATOR']}}, - {path: 'admin/monitor/edit/:name', component: MonitorDetailsComponent, canActivate: [AuthGuard, RoleGuard], + {path: 'monitor/edit/:name', component: MonitorDetailsComponent, canActivate: [AuthGuard, RoleGuard], data: {mode: ComponentMode.EDIT, roles: ['ROLE_SYSTEM_ADMIN', 'ROLE_OPERATOR']}}, - {path: 'admin/monitor/view/:name', component: MonitorDetailsComponent, canActivate: [AuthGuard, RoleGuard], + {path: 'monitor/view/:name', component: MonitorDetailsComponent, canActivate: [AuthGuard, RoleGuard], data: {mode: ComponentMode.VIEW, roles: ['ROLE_SYSTEM_ADMIN', 'ROLE_OPERATOR']}} ]; diff --git a/src/app/appmarket/appdetails/appdetails.component.css b/src/app/appmarket/appdetails/appdetails.component.css index 59a19d772ba2deb41fa86137484fce97d7b4f1c8..cc5f76a6103577d1823e5197bd7040f41776d5f9 100644 --- a/src/app/appmarket/appdetails/appdetails.component.css +++ b/src/app/appmarket/appdetails/appdetails.component.css @@ -1,7 +1,6 @@ .tag-button{ - background-color: white; - border: solid lightgray 1px; - color: gray; + background-color: var(--tag-color); + color: var(--text-color); padding: 4px 8px; text-align: center; text-decoration: none; @@ -11,6 +10,9 @@ border-radius: 12px; cursor: default; } +.background-section{ + padding:30px; +} .block-light-gray{ background-image: linear-gradient(rgba(211, 211, 211, 0.1),rgba(211, 211, 211, 0.1)); @@ -22,28 +24,15 @@ border-radius: 8px; } -hr{ - margin-bottom: 8px; - margin-top: 8px; -} .no-padding{ padding: 0; border: 0; } -/*.first-button-substitute{*/ -/* border-top-right-radius: 0;*/ -/* border-bottom-right-radius: 0;*/ -/*}*/ - .disabled-url { pointer-events: none; cursor: default!important; color: gray; } -.thumbnail{ - border: none; - box-shadow: none; -} diff --git a/src/app/appmarket/appdetails/appdetails.component.html b/src/app/appmarket/appdetails/appdetails.component.html index bb2bbfff4d2c1ff653f1c8dc6942bb1ef5bc55eb..9307f36cea14190d00dc5963ea429958897dd30c 100644 --- a/src/app/appmarket/appdetails/appdetails.component.html +++ b/src/app/appmarket/appdetails/appdetails.component.html @@ -1,17 +1,35 @@ - <div class="row"> - <div class="col-xs-4 col-sm-3 col-md-3 col-lg-2"> - <div class="thumbnail" *ngIf="app"> - <img alt="App logo" [src]="(appImagesService.getAppLogoUrl(appId) | secure) || 'assets/images/app-logo-example.png'"/> + <div class="background-section" style=" display: flex; flex-direction: column"> + <div style=" display: flex; flex-direction: row; justify-content: space-between; padding-bottom: 50px"> + <div style="display: flex; align-items: center;"> + <div *ngIf="app"> + <img alt="App logo" [src]="(appImagesService.getAppLogoUrl(appId) | secure) || 'assets/images/app-logo-example.png'" height="90px"/> + </div> + <div *ngIf="!app"> + <img alt="App logo" src="assets/images/app-logo-example.png" height="90px"/> + </div> + <h3 style="margin:0 20px;">{{app?.name}}</h3> </div> - <div class="thumbnail" *ngIf="!app"> - <img alt="App logo" src="assets/images/app-logo-example.png"/> + <div class="" *ngIf="app && domain" style="display:flex; align-items: center"> + <div *ngIf="!subscribed && isSubscriptionAllowed()" class="btn-group pull-right" + pTooltip="{{'APPLICATIONS.TOOLTIP_MESSAGE_SUBSCRIBE' | translate}}" tooltipPosition="bottom" [showDelay]="50" [tooltipDisabled]="defaultTooltipDisabled"> + <button class="btn btn-primary m-1" [disabled]="!active || !isApplicationEnabledInDomain()" (click)="subscribe()">{{'APPLICATIONS.SUBSCRIBE_BUTTON' | translate}}</button> + <button class="btn btn-primary m-1"[routerLink]="['/admin/apps/bulks']">Bulk deployments</button> + </div> + + <div *ngIf="subscribed" class=" pull-right" > + <div class="btn no-padding" pTooltip="{{'APPLICATIONS.TOOLTIP_MESSAGE_DEPLOY' | translate}}" tooltipPosition="bottom" [showDelay]="50" [tooltipDisabled]="defaultTooltipDisabled"> + <button *ngIf="isSubscriptionAllowed()" class="btn btn-danger m-1" (click)="unsubscribe()">{{'APPLICATIONS.UNSUBSCRIBE_BUTTON' | translate}}</button> + </div> + <button class="btn btn-primary m-1"[routerLink]="['/admin/apps/bulks']">Bulk deployments</button> + <button *ngIf="isSubscriptionAllowed()" class="btn btn-primary m-1" [disabled]="!isApplicationEnabledInDomain()" (click)="appInstallModal.show()">{{'APPLICATIONS.DEPLOY_BUTTON' | translate}}</button> + </div> + </div> </div> - <div class="col-xs-8 col-sm-9 col-md-9 col-lg-10"> - <div class="row"> - <div class="col-xs-12 col-sm-6 col-md-6 col-lg-6" *ngIf="app"> - <h2 style="margin: 4px 0;">{{app?.name}}</h2> - <rate [showVotes]="true" [short]="true" [pathUrl]="getPathUrl(appId)"></rate> + <div class="" style="display: flex;flex-direction: column;"> + <div class=""> + <div class="" *ngIf="app"> +<!-- <rate [showVotes]="true" [short]="true" [pathUrl]="getPathUrl(appId)"></rate>--> <div class="text-muted mt-2" style="font-size: small;"> <span pTooltip="{{'APPLICATIONS.TOOLTIP_MESSAGE_NOT_AVAILABLE' | translate}}" tooltipPosition="bottom" [showDelay]="50" [tooltipDisabled]="activeVersions.length !== 0"> <a class="{{activeVersions.length > 0 ? '' : 'disabled-url'}}" style="cursor: pointer;" (click)="showVersions()">{{'APP_INSTANCE.SHOW_VERSIONS_LABEL' | translate }}</a> @@ -40,57 +58,43 @@ </div> </div> </div> - <div class="row" *ngIf="versionVisible"> - <div class="col-xs-12 col-sm-6 col-md-6 col-lg-6" *ngIf="activeVersions"> + <div class="" *ngIf="versionVisible"> + <div class="" *ngIf="activeVersions"> <a *ngFor="let version of activeVersions" class="tag-button"> v{{version}} </a> </div> </div> - <div class="row mt-2"> - <div class="col-xs-12 col-sm-6 col-md-6 col-lg-6" *ngIf="app?.tags"> + <div class=""> + <div class="" *ngIf="app?.tags"> <a *ngFor="let tag of app.tags" class="tag-button"> {{tag.name | lowercase}} </a> </div> </div> <!-- block the button until app and domain are downloaded to load proper tooltip state--> - <div class="row" *ngIf="app && domain"> - <div *ngIf="!subscribed && isSubscriptionAllowed()" class="btn-group pull-right" - pTooltip="{{'APPLICATIONS.TOOLTIP_MESSAGE_SUBSCRIBE' | translate}}" tooltipPosition="bottom" [showDelay]="50" [tooltipDisabled]="defaultTooltipDisabled"> - <button class="btn btn-primary" [disabled]="!active || !isApplicationEnabledInDomain()" (click)="subscribe()">{{'APPLICATIONS.SUBSCRIBE_BUTTON' | translate}}</button> - </div> - <div *ngIf="subscribed" class=" pull-right" > - <div class="btn no-padding" pTooltip="{{'APPLICATIONS.TOOLTIP_MESSAGE_DEPLOY' | translate}}" tooltipPosition="bottom" [showDelay]="50" [tooltipDisabled]="defaultTooltipDisabled"> - <button *ngIf="isSubscriptionAllowed()" class="btn btn-primary m-1" [disabled]="!isApplicationEnabledInDomain()" (click)="appInstallModal.show()">{{'APPLICATIONS.DEPLOY_BUTTON' | translate}}</button> - </div> - <button *ngIf="isSubscriptionAllowed()" class="btn btn-danger m-1" (click)="unsubscribe()">{{'APPLICATIONS.UNSUBSCRIBE_BUTTON' | translate}}</button> - </div> - </div> </div> </div> - - <div class='row'> - <h3 *ngIf="numberOfScreenshots > 0">{{'SCREENSHOTS.HEADER' | translate}}</h3> - <screenshots (numberOfScreenshots)="screenshots($event)" [pathUrl]="'/apps/' + appId + '/screenshots'"></screenshots> - </div> - - <div class="row mt-5 mb-8"> - <h3>{{'APPLICATIONS.DESCRIPTION' | translate}}</h3> - <div [innerHTML]="getDescription()?.fullDescription"> + <div class="background-section"> + <div> + <p style="font-weight: bold">{{'APPLICATIONS.DESCRIPTION' | translate}}</p> + <div [innerHTML]="getDescription()?.fullDescription"> + </div> </div> - </div> - - <hr> - - <div *ngIf="appId" class="row mb-6 mt-6"> - <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 block-border"> - <rating-extended name="appRateUpdate" [editable]="true" (onChange)="onRateChanged()" [pathUrl]="getPathUrl(appId)"></rating-extended> + <div style="padding-top:50px"> + <p style="font-weight: bold" *ngIf="numberOfScreenshots > 0">{{'SCREENSHOTS.HEADER' | translate}}</p> + <screenshots (numberOfScreenshots)="screenshots($event)" [pathUrl]="'/apps/' + appId + '/screenshots'"></screenshots> </div> </div> - <comments [pathUrl]="'/apps/' + appId + '/comments'"></comments> +<!-- <div *ngIf="appId" class="row mb-6 mt-6">--> +<!-- <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 block-border">--> +<!-- <rating-extended name="appRateUpdate" [editable]="true" (onChange)="onRateChanged()" [pathUrl]="getPathUrl(appId)"></rating-extended>--> +<!-- </div>--> +<!-- </div>--> + +<!-- <comments [pathUrl]="'/apps/' + appId + '/comments'"></comments>--> <nmaas-modal-app-install *ngIf="app && domain" [app]="app" [domain]="domain"> diff --git a/src/app/appmarket/appinstance/appinstance.module.ts b/src/app/appmarket/appinstance/appinstance.module.ts index 37ff61222687abee81977fbf0e6f9e1d0d2be3d5..bc7d02664d02586552884f6722dc9727463396a2 100644 --- a/src/app/appmarket/appinstance/appinstance.module.ts +++ b/src/app/appmarket/appinstance/appinstance.module.ts @@ -36,7 +36,9 @@ import {ButtonModule} from 'primeng/button'; import {AppLogAccessComponent} from './app-log-access/app-log-access.component'; import {FormioAppConfig, FormioModule} from '@formio/angular'; import {SelectButtonModule} from 'primeng/selectbutton'; - +import {CheckboxModule} from 'primeng/checkbox'; +import {TableModule} from 'primeng/table'; +import {ProgressBarModule} from 'primeng/progressbar'; @NgModule({ declarations: [ @@ -74,7 +76,10 @@ import {SelectButtonModule} from 'primeng/selectbutton'; TimelineModule, ButtonModule, InputTextModule, + CheckboxModule, SelectButtonModule, + TableModule, + ProgressBarModule, ], exports: [ AppInstanceComponent, diff --git a/src/app/appmarket/appinstance/appinstance/appinstance.component.css b/src/app/appmarket/appinstance/appinstance/appinstance.component.css index 96f3d93cb7b3cb432509395a983316d14c8ba532..82b1538c8590a63a2c7b88b8d2c025bf9e461744 100644 --- a/src/app/appmarket/appinstance/appinstance/appinstance.component.css +++ b/src/app/appmarket/appinstance/appinstance/appinstance.component.css @@ -23,22 +23,26 @@ .other-states{ padding:15px; - margin-bottom:20px; + margin:50px 0; border-bottom: 1px solid lightgray; border-top: 1px solid lightgray; text-align: center; color: #337ab7; + background-image: none; + width:100%; + background: transparent; } .alert-danger{ padding:15px; - margin-bottom:20px; + margin:50px 0; text-align: center; border-bottom: 1px solid #a94442; border-top: 1px solid #a94442; border-radius: 0; - background-color: #FFFFFF; background-image: none; + width:100%; + background: transparent; } .h-100 { diff --git a/src/app/appmarket/appinstance/appinstance/appinstance.component.html b/src/app/appmarket/appinstance/appinstance/appinstance.component.html index 201b7698f8ab91f85596ecee8465ed82aa0aa563..abecb2b58014fe284786ebdbe59d2f99443b6983 100644 --- a/src/app/appmarket/appinstance/appinstance/appinstance.component.html +++ b/src/app/appmarket/appinstance/appinstance/appinstance.component.html @@ -1,73 +1,61 @@ -<div class="container" *ngIf="appInstance"> - <div class="row"> - <!-- App logo --> - <div class="col-xs-4 col-sm-3 col-md-3 col-lg-2"> - <div class="thumbnail" *ngIf="app"> - <img alt="App logo" - [src]="(appImagesService.getAppLogoUrl(app?.applicationBase.id) | secure) || 'assets/images/app-logo-example.png'"/> - </div> - <div class="thumbnail" *ngIf="!app"> - <img alt="App logo" src="assets/images/app-logo-example.png"/> - </div> - </div> - <!-- App information--> - <div class="col-xs-8 col-sm-9 col-md-9 col-lg-10"> - <div class="row flex-stretch"> - <div class="col-xs-12 col-sm-6 col-md-6 col-lg-6"> - <h2 style="margin-bottom: 4px;">{{appInstance?.name}} ({{app?.applicationBase.name}})</h2> - <rate *ngIf="app?.applicationBase.id" [showVotes]="true" [short]="true" - [pathUrl]="getPathUrl(app?.applicationBase.id)"></rate> - <div class="text-muted" style="font-size: small;"> - {{app?.application.version ? 'v.' + app?.application.version : 'None'}} - | - <span pTooltip="{{'APPLICATIONS.TOOLTIP_MESSAGE_NOT_AVAILABLE' | translate}}" - tooltipPosition="bottom" [showDelay]="50" - [tooltipDisabled]="!!app?.applicationBase.licenseUrl"> - <a class="{{app?.applicationBase.licenseUrl ? '' : 'disabled-url'}}" - [href]="app?.applicationBase.licenseUrl" - target="_blank">{{app?.applicationBase.license || 'License'}}</a> - </span> - | - <span pTooltip="{{'APPLICATIONS.TOOLTIP_MESSAGE_NOT_AVAILABLE' | translate}}" - tooltipPosition="bottom" [showDelay]="50" - [tooltipDisabled]="!!app?.applicationBase.wwwUrl"> - <a class="{{app?.applicationBase.wwwUrl ? '' : 'disabled-url'}}" - [href]="app?.applicationBase.wwwUrl">WWW</a> - </span> - | - <span pTooltip="{{'APPLICATIONS.TOOLTIP_MESSAGE_NOT_AVAILABLE' | translate}}" - tooltipPosition="bottom" [showDelay]="50" - [tooltipDisabled]="!!app?.applicationBase.sourceUrl"> - <a class="{{app?.applicationBase.sourceUrl ? '' : 'disabled-url'}}" - [href]="app?.applicationBase.sourceUrl" - target="_blank">{{'APP_INSTANCE.SOURCE' | translate}}</a> - </span> - | - <span pTooltip="{{'APPLICATIONS.TOOLTIP_MESSAGE_NOT_AVAILABLE' | translate}}" - tooltipPosition="bottom" [showDelay]="50" - [tooltipDisabled]="!!app?.applicationBase.issuesUrl"> - <a class="{{app?.applicationBase.issuesUrl ? '' : 'disabled-url'}}" - [href]="app?.applicationBase.issuesUrl" - target="_blank">{{'APP_INSTANCE.ISSUES' | translate}}</a> - </span> - </div> +<div class="" *ngIf="appInstance"> + <div class="background-section"> + <div style=" display: flex; flex-direction: row; justify-content: space-between; padding-bottom: 50px"> + <div style="display: flex; align-items: center;"> + <div class="" *ngIf="app"> + <img alt="App logo" + [src]="(appImagesService.getAppLogoUrl(app?.applicationBase.id) | secure) || 'assets/images/app-logo-example.png'" height="90px"/> </div> - <div class="col-xs-12 col-sm-6 col-md-6 col-lg-6 container-bottom-right"> - <h3><em style="color: #337ab7;">{{ translateState(appInstanceStatus?.state) }}</em></h3> + <div class="" *ngIf="!app"> + <img alt="App logo" src="assets/images/app-logo-example.png" height="90px"/> </div> + <h2 style="margin:0 20px;">{{appInstance?.name}} ({{app?.applicationBase.name}})</h2> </div> - <hr> - <div class="row"> - <!-- Tags --> - <div class="col-xs-12 col-sm-6 col-md-6 col-lg-6" *ngIf="app?.applicationBase.tags"> + <h3><em style="color: #337ab7;">{{ translateState(appInstanceStatus?.state) }}</em></h3> + </div> + <div style=" display: flex; flex-direction: row; justify-content: space-between;"> + <div class="" style="display: flex;flex-direction: column;"> + <div class="text-muted mt-2" style="font-size: small;"> + {{app?.application.version ? 'v.' + app?.application.version : 'None'}} + | + <span pTooltip="{{'APPLICATIONS.TOOLTIP_MESSAGE_NOT_AVAILABLE' | translate}}" + tooltipPosition="bottom" [showDelay]="50" + [tooltipDisabled]="!!app?.applicationBase.licenseUrl"> + <a class="{{app?.applicationBase.licenseUrl ? '' : 'disabled-url'}}" + [href]="app?.applicationBase.licenseUrl" + target="_blank">{{app?.applicationBase.license || 'License'}}</a> + </span> + | + <span pTooltip="{{'APPLICATIONS.TOOLTIP_MESSAGE_NOT_AVAILABLE' | translate}}" + tooltipPosition="bottom" [showDelay]="50" + [tooltipDisabled]="!!app?.applicationBase.wwwUrl"> + <a class="{{app?.applicationBase.wwwUrl ? '' : 'disabled-url'}}" + [href]="app?.applicationBase.wwwUrl">WWW</a> + </span> + | + <span pTooltip="{{'APPLICATIONS.TOOLTIP_MESSAGE_NOT_AVAILABLE' | translate}}" + tooltipPosition="bottom" [showDelay]="50" + [tooltipDisabled]="!!app?.applicationBase.sourceUrl"> + <a class="{{app?.applicationBase.sourceUrl ? '' : 'disabled-url'}}" + [href]="app?.applicationBase.sourceUrl" + target="_blank">{{'APP_INSTANCE.SOURCE' | translate}}</a> + </span> + | + <span pTooltip="{{'APPLICATIONS.TOOLTIP_MESSAGE_NOT_AVAILABLE' | translate}}" + tooltipPosition="bottom" [showDelay]="50" + [tooltipDisabled]="!!app?.applicationBase.issuesUrl"> + <a class="{{app?.applicationBase.issuesUrl ? '' : 'disabled-url'}}" + [href]="app?.applicationBase.issuesUrl" + target="_blank">{{'APP_INSTANCE.ISSUES' | translate}}</a> + </span> + </div> + <div class="" *ngIf="app?.applicationBase.tags"> <a *ngFor="let tag of app.applicationBase.tags" class="tag-button"> {{tag.name | titlecase}} </a> </div> - - <!-- Deployment buttons --> - - <!-- if application is still being deployed --> + </div> + <div> <div class=" pull-right" *ngIf="getStateAsEnum(appInstanceStatus?.state) != AppInstanceState.FAILURE && getStateAsEnum(appInstanceStatus?.state) != AppInstanceState.RUNNING @@ -86,7 +74,7 @@ <div *ngIf="appInstance && ( getStateAsEnum(appInstanceStatus?.state) == AppInstanceState.RUNNING || getStateAsEnum(appInstanceStatus?.state) == AppInstanceState.FAILURE)" - class="col-xs-12 col-sm-6 col-md-6 col-lg-6"> + class="col-xs-12 col-sm-12 col-md-12 col-lg-12"> <div class="btn-group pull-right"> <button type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> @@ -199,6 +187,83 @@ </div> </div> </div> + <div class="background-section"> + <!-- Installation step description text --> + <div id="app-progress-info" style="font-size: 16px;" class=""> + <div id="app-progress-info-sub alert" + [ngClass]="{ + 'alert-danger': getStateAsEnum(appInstanceStatus?.state) === AppInstanceState.FAILURE || getStateAsEnum(appInstanceStatus?.state) === AppInstanceState.UNKNOWN, + 'other-states': getStateAsEnum(appInstanceStatus?.state) !== AppInstanceState.FAILURE && getStateAsEnum(appInstanceStatus?.state) !== AppInstanceState.UNKNOWN }" + class=""> + <div *ngIf="getStateAsEnum(appInstanceStatus?.state) === AppInstanceState.FAILURE" class="info-container"> + <span class="glyphicon glyphicon-exclamation-sign info-icon" aria-hidden="true"></span> + <span [innerHTML]="'APP_INSTANCE.INSTALLATION_PROGRESS.DEFAULT_ERROR_MESSAGE' | translate"></span> + </div> + <div *ngIf="getStateAsEnum(appInstanceStatus?.state) !== AppInstanceState.FAILURE && getStateAsEnum(appInstanceStatus?.state) !== AppInstanceState.UNKNOWN" + class="info-container"> + <span class="glyphicon glyphicon-info-sign info-icon" aria-hidden="true"></span> + <span [innerHTML]="'APP_INSTANCE.USER_FRIENDLY.' + getStateAsString(appInstanceStatus?.state) | translate"></span> + </div> + </div> + </div> + + <!-- Progress bar --> + <div class=''> + <h3 style="margin-bottom: 30px">{{'APP_INSTANCE.INSTALLATION_PROGRESS.HEADER' | translate}}</h3> + + + <div id="app-prop" class="col-xs-12 col-sm-12 col-md-12 col-lg-12" style="overflow-x: auto;"> + <nmaas-appinstanceprogress [stages]='getStages()' + [activeState]="getStateAsEnum(appInstanceStatus?.state)"></nmaas-appinstanceprogress> + </div> + </div> + + + <!-- Show additional information checkbox --> + <div class="" > + <label style="margin-top: 40px"> + {{ 'APP_INSTANCE.ADDITIONAL_INFO' | translate}} + <input type="checkbox" [(ngModel)]="showAppInstanceHistory" (change)="showHistory()"> + </label> + </div> + + <!-- App Instance State History table --> + <div class="" *ngIf="this.showAppInstanceHistory && this.appInstanceStateHistory"> + <h3>{{'APP_INSTANCE.DEPLOYMENT_HISTORY.HEADER' | translate}}</h3> + <hr> + <table class="table table-hover table-condensed" aria-describedby="App instance deployment history table"> + <thead> + <tr> + <th scope="col">{{'APP_INSTANCE.DEPLOYMENT_HISTORY.TIMESTAMP' | translate}}</th> + <th scope="col">{{'APP_INSTANCE.DEPLOYMENT_HISTORY.STATE_TRANSITIONS' | translate}}</th> + <th scope="col"> </th> + </tr> + </thead> + <tbody> + <ng-template ngFor let-history + [ngForOf]="this.appInstanceStateHistory | paginate: { itemsPerPage: maxItemsOnPage, currentPage: pageNumber, id: p_first }" + let-isLast="last"> + <tr> + <td>{{history.timestamp | localDate:'medium' }}</td> + <td *ngIf="history.previousState !== null"> + {{history.previousState | translate }} + <span class="glyphicon glyphicon-arrow-right" + style="padding-left: 5px;padding-right: 5px"></span> + {{history.currentState | translate }} + </td> + <td *ngIf="history.previousState === null">{{history.currentState | translate }}</td> + </tr> + </ng-template> + </tbody> + </table> + <pagination-controls class="text-right" (pageChange)="pageNumber = $event" id="{{ p_first }}" + previousLabel="{{ 'PAGINATION.PREVIOUS' | translate }}" + nextLabel="{{ 'PAGINATION.NEXT' | translate }}" + screenReaderPaginationLabel="{{ 'PAGINATION.SCREEN_READER.PAGINATION' | translate }}" + screenReaderPageLabel="{{ 'PAGINATION.SCREEN_READER.PAGE' | translate }}" + screenReaderCurrentLabel="{{ 'PAGINATION.SCREEN_READER.CURRENT' | translate }}"></pagination-controls> + </div> + </div> <!-- Undeploy modal--> <nmaas-modal styleModal="warning" #undeployModal> @@ -301,82 +366,6 @@ </div> </nmaas-modal> - <!-- Installation step description text --> - <div id="app-progress-info" style="font-size: 16px;" class="col-xs-12"> - <div id="app-progress-info-sub alert" - [ngClass]="{ - 'alert-danger': getStateAsEnum(appInstanceStatus?.state) === AppInstanceState.FAILURE || getStateAsEnum(appInstanceStatus?.state) === AppInstanceState.UNKNOWN, - 'other-states': getStateAsEnum(appInstanceStatus?.state) !== AppInstanceState.FAILURE && getStateAsEnum(appInstanceStatus?.state) !== AppInstanceState.UNKNOWN }" - class="col-xs-offset-1 col-xs-10"> - <div *ngIf="getStateAsEnum(appInstanceStatus?.state) === AppInstanceState.FAILURE" class="info-container"> - <span class="glyphicon glyphicon-exclamation-sign info-icon" aria-hidden="true"></span> - <span [innerHTML]="'APP_INSTANCE.INSTALLATION_PROGRESS.DEFAULT_ERROR_MESSAGE' | translate"></span> - </div> - <div *ngIf="getStateAsEnum(appInstanceStatus?.state) !== AppInstanceState.FAILURE && getStateAsEnum(appInstanceStatus?.state) !== AppInstanceState.UNKNOWN" - class="info-container"> - <span class="glyphicon glyphicon-info-sign info-icon" aria-hidden="true"></span> - <span [innerHTML]="'APP_INSTANCE.USER_FRIENDLY.' + getStateAsString(appInstanceStatus?.state) | translate"></span> - </div> - </div> - </div> - - <!-- Progress bar --> - <div class='row'> - <h3>{{'APP_INSTANCE.INSTALLATION_PROGRESS.HEADER' | translate}}</h3> - <hr> - - <div id="app-prop" class="col-xs-12 col-sm-12 col-md-12 col-lg-12" style="overflow-x: auto;"> - <nmaas-appinstanceprogress [stages]='getStages()' - [activeState]="getStateAsEnum(appInstanceStatus?.state)"></nmaas-appinstanceprogress> - </div> - </div> - <hr> - - <!-- Show additional information checkbox --> - <div class="row"> - <label> - {{ 'APP_INSTANCE.ADDITIONAL_INFO' | translate}} - <input type="checkbox" [(ngModel)]="showAppInstanceHistory" (change)="showHistory()"> - </label> - </div> - - <!-- App Instance State History table --> - <div class="row" *ngIf="this.showAppInstanceHistory && this.appInstanceStateHistory"> - <h3>{{'APP_INSTANCE.DEPLOYMENT_HISTORY.HEADER' | translate}}</h3> - <hr> - <table class="table table-hover table-condensed" aria-describedby="App instance deployment history table"> - <thead> - <tr> - <th scope="col">{{'APP_INSTANCE.DEPLOYMENT_HISTORY.TIMESTAMP' | translate}}</th> - <th scope="col">{{'APP_INSTANCE.DEPLOYMENT_HISTORY.STATE_TRANSITIONS' | translate}}</th> - <th scope="col"> </th> - </tr> - </thead> - <tbody> - <ng-template ngFor let-history - [ngForOf]="this.appInstanceStateHistory | paginate: { itemsPerPage: maxItemsOnPage, currentPage: pageNumber, id: p_first }" - let-isLast="last"> - <tr> - <td>{{history.timestamp | localDate:'medium' }}</td> - <td *ngIf="history.previousState !== null"> - {{history.previousState | translate }} - <span class="glyphicon glyphicon-arrow-right" - style="padding-left: 5px;padding-right: 5px"></span> - {{history.currentState | translate }} - </td> - <td *ngIf="history.previousState === null">{{history.currentState | translate }}</td> - </tr> - </ng-template> - </tbody> - </table> - <pagination-controls class="text-right" (pageChange)="pageNumber = $event" id="{{ p_first }}" - previousLabel="{{ 'PAGINATION.PREVIOUS' | translate }}" - nextLabel="{{ 'PAGINATION.NEXT' | translate }}" - screenReaderPaginationLabel="{{ 'PAGINATION.SCREEN_READER.PAGINATION' | translate }}" - screenReaderPageLabel="{{ 'PAGINATION.SCREEN_READER.PAGE' | translate }}" - screenReaderCurrentLabel="{{ 'PAGINATION.SCREEN_READER.CURRENT' | translate }}"></pagination-controls> - </div> - <!-- apply config modal --> <nmaas-modal styleModal="info" #applyConfig> <div class="nmaas-modal-header"> diff --git a/src/app/appmarket/appinstance/appinstancelist/appinstancelist.component.css b/src/app/appmarket/appinstance/appinstancelist/appinstancelist.component.css index 0458c598e35d54aa2de5e57f56ed9f359cf6225f..4e7c658771b40fe54ddeb0e02fe92d69261037a6 100644 --- a/src/app/appmarket/appinstance/appinstancelist/appinstancelist.component.css +++ b/src/app/appmarket/appinstance/appinstancelist/appinstancelist.component.css @@ -25,7 +25,7 @@ height: 20px; } -tr.clickable { +.clickable { cursor: pointer; } @@ -33,7 +33,103 @@ tr.clickable { display: block; } +label{ + padding-left:5px; + display: unset; + margin-bottom: 0; + font-weight: unset; +} +:host ::ng-deep .p-datatable .p-datatable-thead > tr > th{ + border: 1px solid #E0E2E5; + border-width: 0 0 1px 0; +} +:host ::ng-deep .p-datatable .p-datatable-tbody > tr > td { + text-align: left; + border: 1px solid #E0E2E5; + border-width: 0 0 1px 0; + padding: 1rem 1rem; +} +:host ::ng-deep .p-datatable .p-paginator-bottom{ + height: 40px; + background: transparent; + border: none; + margin-top:10px; +} +:host ::ng-deep .p-datatable .p-datatable-tbody > tr{ + background: transparent; +} + +:host ::ng-deep .p-paginator .p-paginator-pages .p-paginator-page{ + transition: unset; + border-radius: 50%; + min-width:3.5rem; + height:3.5rem; + margin:0 5px; + font-size: 14px; +} + +:host ::ng-deep .p-paginator-element{ + border-radius:50%; + margin:0 5px; + min-width:3.5rem; + height:3.5rem; + font-size: 14px; +} +:host ::ng-deep .p-paginator .p-dropdown{ + height:3rem; +} +:host ::ng-deep .p-paginator-icon{ + height: 1.5rem; + width: 1.5rem; +} +:host ::ng-deep .p-paginator .p-dropdown .p-dropdown-label{ + padding-right: 10px; +} ::ng-deep.p-selectbutton .p-button.p-highlight{ - background: #233354 !important; - border-color: #233354; + background: var(--primary-button-color) !important; + border-color: var(--primary-button-color); +} +:host ::ng-deep .p-selectbutton .p-button{ + background: #fff; +} +:host ::ng-deep .p-button .p-button-label{ + font-weight: normal; +} +:host ::ng-deep .p-selectbutton .p-button:not(.p-disabled):not(.p-highlight):hover{ + background: var(--primary-button-color); + border-color: var(--primary-button-color); + color: var(--background); +} +:host ::ng-deep .p-paginator .p-paginator-pages .p-paginator-page.p-highlight{ + background: var(--user-button-background-hover); +} +:host ::ng-deep .p-button.p-button-icon-only{ + width: unset; +} +::ng-deep .running .p-progressbar .p-progressbar-value { + background: #EBBD59; +} +.running{ + color: #EBBD59; + font-weight: bold; +} +::ng-deep .failure .p-progressbar .p-progressbar-value{ + background: #AA0404; +} +.failure{ + color: #AA0404; + font-weight: bold; +} +::ng-deep .done .p-progressbar .p-progressbar-value{ + background: #136214; +} +.done{ + color: #136214; + font-weight: bold; +} +::ng-deep .p-progressbar .p-progressbar-label{ + display: none; +} +p-selectbutton{ + width: max-content; } diff --git a/src/app/appmarket/appinstance/appinstancelist/appinstancelist.component.html b/src/app/appmarket/appinstance/appinstancelist/appinstancelist.component.html index d97bd6f0962466ec5d1382e6f37d344ceb78cd61..c49146f7a9b849236499157af455e499196403d9 100644 --- a/src/app/appmarket/appinstance/appinstancelist/appinstancelist.component.html +++ b/src/app/appmarket/appinstance/appinstancelist/appinstancelist.component.html @@ -1,145 +1,160 @@ -<div class="col-sm-12 col-sm-12 col-md-12"> - - <h3>{{ 'APP_INSTANCES.TITLE' | translate }}</h3> - - <div class="col-sm-12 col-sm-12 col-md-12"> - <div style="margin-left: -10px;" class="col-xs-12 col-sm-6 col-md-6 col-lg-6"> - <form class="form-inline" role="form"> - <div class="form-group " style="display: inline-flex; align-items: flex-end"> - <label class="mr-3" for="selectionType">{{ 'APP_INSTANCES.SHOW' | translate }}: </label> - <p-selectButton - id="selectionType" - [options]="selectionOptions" - [(ngModel)]="listSelection" - [ngModelOptions]="{standalone: true}" - (ngModelChange)="onSelectionChange($event)" - optionLabel="label" - optionValue="value" - ngDefaultControl/> - </div> - <strong class="checkbox-label" *domainRoles="['ROLE_DOMAIN_ADMIN', 'ROLE_SYSTEM_ADMIN', 'ROLE_OPERATOR'];domainId:domainId"> - {{'APP_INSTANCES.UNDEPLOYED_VISIBLE' | translate}}: - </strong> - <div class="form-group" *domainRoles="['ROLE_DOMAIN_ADMIN', 'ROLE_SYSTEM_ADMIN', 'ROLE_OPERATOR'];domainId:domainId"> - <label for="show_visible"></label> - <input id="show_visible" type="checkbox" class="big-checkbox" [(ngModel)]="undeployedVisible" [ngModelOptions]="{standalone: true}"> - </div> - </form> +<div style="display: flex; align-items: flex-start; justify-content: space-between; margin-top:20px"> + <div style="display:flex; align-items: center;"> + <input pInputText name="search" id="search" placeholder="Search" type="text" [(ngModel)]="searchValue"> + <div class="" style="display: inline-flex; align-items: center; margin-right:20px"> + <label class="mr-3" for="selectionType">{{ 'APP_INSTANCES.SHOW' | translate }}: </label> + <p-selectButton + id="selectionType" + [options]="selectionOptions" + [(ngModel)]="listSelection" + [ngModelOptions]="{standalone: true}" + (ngModelChange)="onSelectionChange($event)" + optionLabel="label" + optionValue="value" + ngDefaultControl/> </div> - <div style="display: inline;" class="col-xs-12 col-sm-6 col-md-6 col-lg-6 pull-right text-right"> - {{ 'APP_INSTANCES.ITEMS_PER_PAGE' | translate }}: - <span id="selectionItems" class="dropdown" style="vertical-align: middle; display: inline-block; margin-right: 10px"> - <button class="dropdown-toggle btn" data-toggle="dropdown" data-close-others="true"> - {{maxItemsOnPage}} - </button> - <ul class="dropdown-menu"> - <li *ngFor="let item of itemsPerPage" [ngClass]="{'active': maxItemsOnPage == item}"> - <a (click)="setItems(item)"> - <span>{{item.toString()}}</span> - </a> - </li> - </ul> - </span> - <input pInputText name="search" id="search" placeholder="Search" type="text" [(ngModel)]="searchValue"> - + <div style="display: flex"> + <p-checkbox *domainRoles="['ROLE_DOMAIN_ADMIN', 'ROLE_SYSTEM_ADMIN', 'ROLE_OPERATOR'];domainId:domainId" + id="show_visible" inputId="show_visible" binary="true" [(ngModel)]="undeployedVisible" [ngModelOptions]="{standalone: true}"></p-checkbox> + <label for="show_visible" style="text-wrap: nowrap" *domainRoles="['ROLE_DOMAIN_ADMIN', 'ROLE_SYSTEM_ADMIN', 'ROLE_OPERATOR'];domainId:domainId">{{'APP_INSTANCES.UNDEPLOYED_VISIBLE' | translate}}</label> </div> </div> + <div class="" style="display: inline-flex; align-items: center"> + <p-selectButton + id="selectionType" + [options]="viewOptions" + [(ngModel)]="selectedOption" + [ngModelOptions]="{standalone: true}" + optionLabel="label" + optionValue="value" + ngDefaultControl> + <ng-template let-item pTemplate> + <i style="font-size: 14px; padding: 0 10px" [class]="item.icon"></i> + </ng-template> + </p-selectButton> + </div> </div> -<div class="col-sm-12 col-sm-12 col-md-12"> - <h4 *roles="['ROLE_DOMAIN_ADMIN', 'ROLE_SYSTEM_ADMIN', 'ROLE_OPERATOR']">{{ 'APP_INSTANCES.DEPLOYED' | translate }}</h4> - <table class="table table-hover table-condensed" sortable-table (sorted)="onSorted($event)" - aria-describedby="Deployed instances"> - <thead> - <tr> - <th scope="col" class="col-lg-1 col-md-1 column-sortable" - sortable-column="name">{{ 'APP_INSTANCES.NAME' | translate }}</th> - <th scope="col" class="col-lg-2 col-md-2 column-sortable" - sortable-column="applicationName">{{ 'APP_INSTANCES.APPLICATION' | translate }}</th> - <th scope="col" class="col-lg-1 col-md-1">{{ 'APP_INSTANCES.VERSION' | translate }}</th> - <th scope="col" class="col-lg-2 col-md-2 column-sortable" sortable-column="domainId" - *ngIf="domainId === undefined || domainId === domainService.getGlobalDomainId()"> - {{ 'APP_INSTANCES.DOMAIN' | translate }} - </th> - <th scope="col" class="col-lg-1 col-md-1 column-sortable" - sortable-column="owner">{{ 'APP_INSTANCES.OWNER' | translate }}</th> - <th scope="col" class="col-lg-2 col-md-2 column-sortable" sortable-column="createdAt" - sort-direction="asc">{{ 'APP_INSTANCES.DEPLOYED_AT' | translate }}</th> - <th scope="col" class="col-lg-3 col-md-3 column-sortable" - sortable-column="userFriendlyState">{{ 'APP_INSTANCES.STATE' | translate }}</th> - <th scope="col" class="col-lg-1 col-md-1"></th> - </tr> - </thead> - <tbody> - <ng-template ngFor let-appInstance - [ngForOf]="appDeployedInstances | async | searchAppInstance: searchValue | paginate: { itemsPerPage: maxItemsOnPage, currentPage: pageNumber, id: p_first }"> - <tr [ngClass]="userHasGuestRoleInCurrentDomain() ? '' : 'clickable'" [routerLink]="userHasGuestRoleInCurrentDomain() ? [] : [appInstance.id]"> - <td class="col-lg-1 col-md-1">{{appInstance?.name}}</td> - <td class="col-lg-2 col-md-2">{{appInstance?.applicationName}}</td> - <td class="col-lg-1 col-md-1">{{appInstance?.applicationVersion}}</td> - <td class="col-lg-2 col-md-2" - *ngIf="domainId === undefined || domainId === domainService.getGlobalDomainId()"> - {{appInstance?.domainName}} + +<h4 style="margin-top:40px; font-weight: bold" *roles="['ROLE_DOMAIN_ADMIN', 'ROLE_SYSTEM_ADMIN', 'ROLE_OPERATOR']">{{ 'APP_INSTANCES.DEPLOYED' | translate }}</h4> +<div *ngIf="selectedOption === 'list'" class="background-section" style="margin-top:30px"> + <p-table [value]="appDeployedInstances | async | searchAppInstance: searchValue" + [paginator]="true" + [rows]="maxItemsOnPage" + [rowsPerPageOptions]="[5, 10, 20]" + [responsiveLayout]="'scroll'"> + + <ng-template pTemplate="header"> + <tr> + <th>{{ 'APP_INSTANCES.NAME' | translate }}</th> + <th>{{ 'APP_INSTANCES.APPLICATION' | translate }}</th> + <th>{{ 'APP_INSTANCES.VERSION' | translate }}</th> + <th *ngIf="domainId === undefined || domainId === domainService.getGlobalDomainId()"> + {{ 'APP_INSTANCES.DOMAIN' | translate }} + </th> + <th>{{ 'APP_INSTANCES.OWNER' | translate }}</th> + <th>{{ 'APP_INSTANCES.DEPLOYED_AT' | translate }}</th> + <th>{{ 'APP_INSTANCES.STATE' | translate }}</th> + <th></th> + </tr> + </ng-template> + + <ng-template pTemplate="body" let-appInstance> + <tr [ngClass]="{'clickable': !userHasGuestRoleInCurrentDomain()}" + [routerLink]="userHasGuestRoleInCurrentDomain() ? [] : [appInstance.id]"> + <td>{{ appInstance?.name }}</td> + <td>{{ appInstance?.applicationName }}</td> + <td>{{ appInstance?.applicationVersion }}</td> + <td *ngIf="domainId === undefined || domainId === domainService.getGlobalDomainId()"> + {{ appInstance?.domainName }} </td> - <td class="col-lg-1 col-md-1">{{appInstance?.owner?.username}}</td> - <td class="col-lg-2 col-md-2">{{appInstance?.createdAt | localDate:'dd-MM-yyyy HH:mm'}}</td> - <td class="col-lg-3 col-md-3">{{ translateState(appInstance?.state) }}</td> - <td class="col-lg-1 col-md-1"> - <div *ngIf="appInstance?.upgradePossible"> - <span class="glyphicon glyphicon-circle-arrow-up"></span> - </div> + <td>{{ appInstance?.owner?.username }}</td> + <td>{{ appInstance?.createdAt | localDate:'dd-MM-yyyy HH:mm' }}</td> + <td>{{ translateState(appInstance?.state) }}</td> + <td> + <ng-container *ngIf="appInstance?.upgradePossible"> + <span class="pi pi-arrow-circle-up"></span> + </ng-container> </td> </tr> </ng-template> - </tbody> - </table> - <pagination-controls class="text-right" (pageChange)="pageNumber = $event" id="{{ p_first }}" - previousLabel="{{ 'PAGINATION.PREVIOUS' | translate }}" - nextLabel="{{ 'PAGINATION.NEXT' | translate }}" - screenReaderPaginationLabel="{{ 'PAGINATION.SCREEN_READER.PAGINATION' | translate }}" - screenReaderPageLabel="{{ 'PAGINATION.SCREEN_READER.PAGE' | translate }}" - screenReaderCurrentLabel="{{ 'PAGINATION.SCREEN_READER.CURRENT' | translate }}"></pagination-controls> + </p-table> </div> -<div class="col-sm-12 col-sm-12 col-md-12" *roles="['ROLE_DOMAIN_ADMIN', 'ROLE_SYSTEM_ADMIN', 'ROLE_OPERATOR']"> - <h4 *ngIf="undeployedVisible">{{ 'APP_INSTANCES.UNDEPLOYED' | translate }}</h4> - <table *ngIf="undeployedVisible" class="table table-hover table-condensed" sortable-table (sorted)="onSorted($event)" - aria-describedby="Undeployed instances"> - <thead> - <tr> - <th scope="col" class="col-lg-1 col-md-1 column-sortable" - sortable-column="name">{{ 'APP_INSTANCES.NAME' | translate }}</th> - <th scope="col" class="col-lg-2 col-md-2 column-sortable" - sortable-column="applicationName">{{ 'APP_INSTANCES.APPLICATION' | translate }}</th> - <th scope="col" class="col-lg-1 col-md-1 column-sortable" sortable-column="domainId" - *ngIf="domainId === undefined || domainId === domainService.getGlobalDomainId()"> - {{ 'APP_INSTANCES.DOMAIN' | translate }}</th> - <th scope="col" class="col-lg-1 col-md-1 column-sortable" - sortable-column="owner">{{ 'APP_INSTANCES.OWNER' | translate }}</th> - <th scope="col" class="col-lg-2 col-md-2 column-sortable" sortable-column="createdAt" - sort-direction="asc">{{ 'APP_INSTANCES.DEPLOYED_AT' | translate }}</th> - <th scope="col" class="col-lg-3 col-md-3 column-sortable" - sortable-column="userFriendlyState">{{ 'APP_INSTANCES.STATE' | translate }}</th> - </tr> - </thead> - <tbody> - <ng-template ngFor let-appInstance - [ngForOf]="appUndeployedInstances | async | paginate: { itemsPerPage: maxItemsOnPageSec, currentPage: secondPageNumber, id: p_second }"> - <tr class="clickable" [routerLink]="[appInstance.id]"> - <td class="col-lg-1 col-md-1">{{appInstance?.name}}</td> - <td class="col-lg-2 col-md-2">{{appInstance?.applicationName}}</td> - <td class="col-lg-1 col-md-1" - *ngIf="domainId === undefined || domainId === domainService.getGlobalDomainId()"> - {{appInstance?.domainName}}</td> - <td class="col-lg-1 col-md-1">{{appInstance?.owner?.username}}</td> - <td class="col-lg-2 col-md-2">{{appInstance?.createdAt | localDate:'dd-MM-yyyy HH:mm'}}</td> - <td class="col-lg-3 col-md-3">{{ translateState(appInstance?.state) }}</td> - </tr> - </ng-template> - </tbody> - </table> - <pagination-controls *ngIf="undeployedVisible" class="text-right" (pageChange)="secondPageNumber = $event" id="{{ p_second }}" - previousLabel="{{ 'PAGINATION.PREVIOUS' | translate }}" - nextLabel="{{ 'PAGINATION.NEXT' | translate }}" - screenReaderPaginationLabel="{{ 'PAGINATION.SCREEN_READER.PAGINATION' | translate }}" - screenReaderPageLabel="{{ 'PAGINATION.SCREEN_READER.PAGE' | translate }}" - screenReaderCurrentLabel="{{ 'PAGINATION.SCREEN_READER.CURRENT' | translate }}"></pagination-controls> + +<div *ngIf="selectedOption === 'cards'" class="grid col-12"> + <div class="col-lg-4 col-md-6 col-sm-6 col-xs-12" *ngFor="let appInstance of appDeployedInstances | async | searchAppInstance: searchValue"> + <div class="background-section" [ngClass]="{'clickable': !userHasGuestRoleInCurrentDomain()}" + [routerLink]="userHasGuestRoleInCurrentDomain() ? [] : [appInstance.id]" > + <div style="display:flex; justify-content: space-between; margin-bottom: 30px"> + <div> + <p>{{appInstance?.name}}</p> + <p>{{ appInstance?.applicationName }}</p> + </div> + <div> + <div class="" *ngIf="appInstance?.application"> + <img alt="App logo" + [src]="(appImagesService.getAppLogoUrl(appInstance?.application.applicationBase.id) | secure) || 'assets/images/app-logo-example.png'" height="50px"/> + </div> + <div class="" *ngIf="!appInstance?.application"> + <img alt="App logo" src="assets/images/app-logo-example.png" height="50px"/> + </div> + </div> + </div> + <div *ngIf="getStateAsEnum(appInstance?.state) == AppInstanceState.FAILURE || + getStateAsEnum(appInstance?.state) == AppInstanceState.REMOVED"> + <p class="failure">{{ translateState(appInstance?.state) }}</p> + <p-progressBar class="failure" [value]="100" [style]="{'height': '6px'}"></p-progressBar> + </div> + <div *ngIf="getStateAsEnum(appInstance?.state) == AppInstanceState.RUNNING || + getStateAsEnum(appInstance?.state) == AppInstanceState.DEPLOYING || + getStateAsEnum(appInstance?.state) == AppInstanceState.CONFIGURATION_AWAITING || + getStateAsEnum(appInstance?.state) == AppInstanceState.CONNECTING"> + <p class="running">{{ translateState(appInstance?.state) }}</p> + <p-progressBar class="running" mode="indeterminate" [style]="{'height': '6px'}"></p-progressBar> + </div> + <div *ngIf="getStateAsEnum(appInstance?.state) == AppInstanceState.DONE || + getStateAsEnum(appInstance?.state) == AppInstanceState.RUNNING "> + <p class="done">{{ translateState(appInstance?.state) }}</p> + <p-progressBar class="done" [value]="100" [style]="{'height': '6px'}"></p-progressBar> + </div> + + + </div> + </div> + +</div> + +<div style="margin-top:40px" *roles="['ROLE_DOMAIN_ADMIN', 'ROLE_SYSTEM_ADMIN', 'ROLE_OPERATOR']"> + <h4 class="header" *ngIf="undeployedVisible">{{ 'APP_INSTANCES.UNDEPLOYED' | translate }}</h4> + <div style="margin-top:30px" *ngIf="undeployedVisible && selectedOption === 'list'" class="background-section"> + <p-table [value]="appUndeployedInstances | async | paginate: { itemsPerPage: maxItemsOnPageSec, currentPage: secondPageNumber, id: p_second }" + [paginator]="true" + [rows]="maxItemsOnPageSec" + [rowsPerPageOptions]="[5, 10, 20]" + [responsiveLayout]="'scroll'" aria-describedby="Undeployed instances"> + <ng-template pTemplate="header"> + <tr> + <th>{{ 'APP_INSTANCES.NAME' | translate }}</th> + <th>{{ 'APP_INSTANCES.APPLICATION' | translate }}</th> + <th *ngIf="domainId === undefined || domainId === domainService.getGlobalDomainId()"> + {{ 'APP_INSTANCES.DOMAIN' | translate }} + </th> + <th>{{ 'APP_INSTANCES.OWNER' | translate }}</th> + <th>{{ 'APP_INSTANCES.DEPLOYED_AT' | translate }}</th> + <th>{{ 'APP_INSTANCES.STATE' | translate }}</th> + </tr> + </ng-template> + <ng-template pTemplate="body" let-appInstance> + <tr [ngClass]="{'clickable': true}" [routerLink]="[appInstance.id]"> + <td>{{ appInstance?.name }}</td> + <td>{{ appInstance?.applicationName }}</td> + <td *ngIf="domainId === undefined || domainId === domainService.getGlobalDomainId()"> + {{ appInstance?.domainName }} + </td> + <td>{{ appInstance?.owner?.username }}</td> + <td>{{ appInstance?.createdAt | localDate:'dd-MM-yyyy HH:mm' }}</td> + <td>{{ translateState(appInstance?.state) }}</td> + </tr> + </ng-template> + </p-table> + </div> </div> diff --git a/src/app/appmarket/appinstance/appinstancelist/appinstancelist.component.ts b/src/app/appmarket/appinstance/appinstancelist/appinstancelist.component.ts index b02be4cc73d46546d2600be1885040d0bbc19528..34c19ec8c7a6fbd77cccceb90684018e19fe31c6 100644 --- a/src/app/appmarket/appinstance/appinstancelist/appinstancelist.component.ts +++ b/src/app/appmarket/appinstance/appinstancelist/appinstancelist.component.ts @@ -1,7 +1,7 @@ import {Component, OnInit} from '@angular/core'; import {AppInstance, AppInstanceState, parseAppInstanceState} from '../../../model'; -import {AppConfigService, AppInstanceService, CustomerSearchCriteria, DomainService} from '../../../service'; +import {AppConfigService, AppImagesService, AppInstanceService, CustomerSearchCriteria, DomainService} from '../../../service'; import {AuthService} from '../../../auth/auth.service'; import {UserDataService} from '../../../service/userdata.service'; import {forkJoin, Observable, of} from 'rxjs'; @@ -52,6 +52,13 @@ export class AppInstanceListComponent implements OnInit { public selectedUsername: string; public domainId = 0; + public domains: Domain[] = []; + public viewOptions = [ + {icon: 'pi pi-list', value: 'list'}, + {icon: 'pi pi-th-large', value: 'cards'} + ]; + public selectedOption = 'list'; + public searchValue = ''; public selectionOptions = [ { label: this.translateEnum(AppInstanceListSelection.ALL), value: AppInstanceListSelection.ALL }, @@ -65,7 +72,8 @@ export class AppInstanceListComponent implements OnInit { public authService: AuthService, private appConfig: AppConfigService, private translateService: TranslateService, - private sessionService: SessionService) { + private sessionService: SessionService, + public appImagesService: AppImagesService) { } @@ -92,7 +100,6 @@ export class AppInstanceListComponent implements OnInit { this.update(domainId) }); - forkJoin({ all: this.translateService.get('ENUM.ALL'), my: this.translateService.get('ENUM.MY') @@ -102,7 +109,6 @@ export class AppInstanceListComponent implements OnInit { { label: translations.my, value: AppInstanceListSelection.MY }, ]; }); - } @@ -219,4 +225,8 @@ export class AppInstanceListComponent implements OnInit { public userHasGuestRoleInCurrentDomain(): boolean { return this.authService.hasDomainRole(this.domainId, 'ROLE_GUEST'); } + + public getStateAsEnum(state: string | AppInstanceState): AppInstanceState { + return typeof state === 'string' ? AppInstanceState[state] : state; + } } diff --git a/src/app/appmarket/applist/applist.component.html b/src/app/appmarket/applist/applist.component.html index e39a6a560095b604441ce2ea47ba6c4ed49cbe32..4e46de83f11494f1d5ac48708eb7782aac7f1af4 100644 --- a/src/app/appmarket/applist/applist.component.html +++ b/src/app/appmarket/applist/applist.component.html @@ -1 +1 @@ -<nmaas-applications-view class="col-sm-12 col-sm-12 col-md-12" [domainId]="domainId" [appView]="appsView"></nmaas-applications-view> +<nmaas-applications-view class="col-sm-12 col-sm-12 col-md-12" style="padding:0;" [domainId]="domainId" [appView]="appsView"></nmaas-applications-view> diff --git a/src/app/appmarket/appmanagement/app-add-json-app/app-add-json-app.component.css b/src/app/appmarket/appmanagement/app-add-json-app/app-add-json-app.component.css new file mode 100644 index 0000000000000000000000000000000000000000..f2c387a9db7ec1e2c9aeefc41a92b4d6336c9fdb --- /dev/null +++ b/src/app/appmarket/appmanagement/app-add-json-app/app-add-json-app.component.css @@ -0,0 +1,38 @@ +:host ::ng-deep input[type=file]{ + display:none; +} +:host ::ng-deep .p-button{ + width: unset; + margin-right: 5px; + background: var(--primary-button-color); + color: var(--button-text-color); +} +:host ::ng-deep .p-button:hover{ + background: var(--primary-button-hover); + border:none; +} +:host ::ng-deep .p-button-label{ + font-weight: normal; +} +:host ::ng-deep .p-fileupload .p-fileupload-buttonbar{ + border: none; + background: transparent; + margin-bottom: 10px; + padding: 0; +} +:host ::ng-deep .p-fileupload .p-fileupload-content{ + border: none; + padding: 0; + border-radius: 3px; + +} +:host ::ng-deep .p-fileupload-content .p-progressbar{ + display: none; +} +textarea{ + border-color: #ccc; +} +:host ::ng-deep .p-inputtext:enabled:focus{ + box-shadow: none; + border-color: var(--l-text-color); +} diff --git a/src/app/appmarket/appmanagement/app-add-json-app/app-add-json-app.component.html b/src/app/appmarket/appmanagement/app-add-json-app/app-add-json-app.component.html index 4fbade572a717bc1ceaa366511bc588551d6f8b7..ebd41c48dae2e11520b48a6727d77f2ad92d16f0 100644 --- a/src/app/appmarket/appmanagement/app-add-json-app/app-add-json-app.component.html +++ b/src/app/appmarket/appmanagement/app-add-json-app/app-add-json-app.component.html @@ -14,7 +14,7 @@ <div style="margin-bottom: 10px"> {{ 'APPS_MANAGEMENT.ADD_JSON_TEXTAREA'| translate}} </div> - <textarea rows="10" cols="100" pInputTextarea [(ngModel)]="jsonText" (keyup)="this.JsonError = false; this.error = ''"></textarea> + <textarea pInputTextarea rows="10" cols="100" style="min-height: 200px; width: 100%" [autoResize]="true" [(ngModel)]="jsonText" (keyup)="this.JsonError = false; this.error = ''"></textarea> </div> <div class="flex flex-row justify-content-center justify-content-center mt-2"> <button *ngIf="jsonText.length >0" pButton class="btn btn-secondary" diff --git a/src/app/appmarket/appmanagement/app-add-json-app/app-add-json-app.component.ts b/src/app/appmarket/appmanagement/app-add-json-app/app-add-json-app.component.ts index e0d4bf65b773d08f3427a7c5fcec100a4a99d6d7..10e18dcfc0598fd779fe86dbd52a66e796f8bf94 100644 --- a/src/app/appmarket/appmanagement/app-add-json-app/app-add-json-app.component.ts +++ b/src/app/appmarket/appmanagement/app-add-json-app/app-add-json-app.component.ts @@ -6,7 +6,7 @@ import {Router} from '@angular/router'; @Component({ selector: 'app-app-add-json-app', templateUrl: './app-add-json-app.component.html', - styleUrls: [] + styleUrls: ['./app-add-json-app.component.css'] }) export class AppAddJsonAppComponent { diff --git a/src/app/appmarket/appmanagement/app-add-json-version-app/app-add-json-version-app.component.css b/src/app/appmarket/appmanagement/app-add-json-version-app/app-add-json-version-app.component.css new file mode 100644 index 0000000000000000000000000000000000000000..f2c387a9db7ec1e2c9aeefc41a92b4d6336c9fdb --- /dev/null +++ b/src/app/appmarket/appmanagement/app-add-json-version-app/app-add-json-version-app.component.css @@ -0,0 +1,38 @@ +:host ::ng-deep input[type=file]{ + display:none; +} +:host ::ng-deep .p-button{ + width: unset; + margin-right: 5px; + background: var(--primary-button-color); + color: var(--button-text-color); +} +:host ::ng-deep .p-button:hover{ + background: var(--primary-button-hover); + border:none; +} +:host ::ng-deep .p-button-label{ + font-weight: normal; +} +:host ::ng-deep .p-fileupload .p-fileupload-buttonbar{ + border: none; + background: transparent; + margin-bottom: 10px; + padding: 0; +} +:host ::ng-deep .p-fileupload .p-fileupload-content{ + border: none; + padding: 0; + border-radius: 3px; + +} +:host ::ng-deep .p-fileupload-content .p-progressbar{ + display: none; +} +textarea{ + border-color: #ccc; +} +:host ::ng-deep .p-inputtext:enabled:focus{ + box-shadow: none; + border-color: var(--l-text-color); +} diff --git a/src/app/appmarket/appmanagement/app-add-json-version-app/app-add-json-version-app.component.html b/src/app/appmarket/appmanagement/app-add-json-version-app/app-add-json-version-app.component.html index b1aebf85fa2ec769679f9d620222cb298756fc8c..658ebdfd794e2511480d2b3a613ca1ad9caf018d 100644 --- a/src/app/appmarket/appmanagement/app-add-json-version-app/app-add-json-version-app.component.html +++ b/src/app/appmarket/appmanagement/app-add-json-version-app/app-add-json-version-app.component.html @@ -14,7 +14,7 @@ <div style="margin-bottom: 10px"> {{ 'APPS_MANAGEMENT.ADD_JSON_TEXTAREA'| translate}} </div> - <textarea rows="10" cols="100" pInputTextarea [(ngModel)]="jsonText" (keyup)="this.JsonError = false; this.error = ''"></textarea> + <textarea rows="10" cols="100" pInputTextarea style="min-height: 200px; width: 100%" [autoResize]="true" [(ngModel)]="jsonText" (keyup)="this.JsonError = false; this.error = ''"></textarea> </div> <div class="flex flex-row justify-content-center justify-content-center mt-2"> <button *ngIf="jsonText.length >0" pButton class="btn btn-primary" diff --git a/src/app/appmarket/appmanagement/app-add-json-version-app/app-add-json-version-app.component.ts b/src/app/appmarket/appmanagement/app-add-json-version-app/app-add-json-version-app.component.ts index b72cade07a20a92d867071265e9642d8ce4aa6ee..063751809bd85bdb65003f0d41f3043773569408 100644 --- a/src/app/appmarket/appmanagement/app-add-json-version-app/app-add-json-version-app.component.ts +++ b/src/app/appmarket/appmanagement/app-add-json-version-app/app-add-json-version-app.component.ts @@ -5,7 +5,7 @@ import {AppsService} from '../../../service'; @Component({ selector: 'app-app-add-json-version-app', templateUrl: './app-add-json-version-app.component.html', - styleUrls: [] + styleUrls: ['app-add-json-version-app.component.css'] }) export class AppAddJsonVersionAppComponent { diff --git a/src/app/appmarket/appmanagement/app-change-owner-modal/app-change-owner-modal.component.html b/src/app/appmarket/appmanagement/app-change-owner-modal/app-change-owner-modal.component.html index b9bdad7579139c2b35a093187883df888e18cd3f..8968f1e38a3745a3b76255014caab217d1dd8f06 100644 --- a/src/app/appmarket/appmanagement/app-change-owner-modal/app-change-owner-modal.component.html +++ b/src/app/appmarket/appmanagement/app-change-owner-modal/app-change-owner-modal.component.html @@ -18,12 +18,13 @@ </div> </div> <div class="nmaas-modal-footer"> - <button type="button" class="btn btn-primary" (click)="submit()" [disabled]="selectOwner.invalid"> - {{'APPS_MANAGEMENT.CHANGE_OWNER_BUTTON' | translate}} - </button> <button type="button" class="btn btn-secondary" (click)="modal.hide()"> {{'APP_CHANGE_STATE_MODAL.CANCEL_BUTTON' | translate}} </button> + <button type="button" class="btn btn-primary" (click)="submit()" [disabled]="selectOwner.invalid"> + {{'APPS_MANAGEMENT.CHANGE_OWNER_BUTTON' | translate}} + </button> + </div> </nmaas-modal> diff --git a/src/app/appmarket/appmanagement/app-management-list/appmanagementlist.component.css b/src/app/appmarket/appmanagement/app-management-list/appmanagementlist.component.css index 08d4bc6e817ee8bbdd9dcedfe621a9a439dc6709..cd6a22945f00765bcea111aac4bf1502a039359d 100644 --- a/src/app/appmarket/appmanagement/app-management-list/appmanagementlist.component.css +++ b/src/app/appmarket/appmanagement/app-management-list/appmanagementlist.component.css @@ -13,6 +13,29 @@ margin-right: 5px; } -.dropdown:hover .dropdown-menu { - display: block; +/*.dropdown:hover .dropdown-menu {*/ +/* display: block;*/ +/*}*/ +:host ::ng-deep .p-datatable .p-datatable-thead > tr > th{ + border: 1px solid #E0E2E5; + background:transparent; + border-width: 0 0 1px 0; +} +:host ::ng-deep .p-datatable .p-datatable-tbody > tr > td { + text-align: left; + border: 1px solid #E0E2E5; + border-width: 0 0 1px 0; + padding: 1rem 1rem; +} +:host ::ng-deep .p-datatable .p-paginator-bottom{ + height: 40px; + background: transparent; + border: none; + margin-top:10px; +} +:host ::ng-deep .p-datatable .p-datatable-tbody > tr{ + background: transparent; +} +:host ::ng-deep .p-datatable>.p-datatable-wrapper { + overflow: visible; } diff --git a/src/app/appmarket/appmanagement/app-management-list/appmanagementlist.component.html b/src/app/appmarket/appmanagement/app-management-list/appmanagementlist.component.html index 19fca710ffd4222b0e182db1df8224394cd6eb49..1659e65fd75554b06494ad9e87d2cdf9404edac7 100644 --- a/src/app/appmarket/appmanagement/app-management-list/appmanagementlist.component.html +++ b/src/app/appmarket/appmanagement/app-management-list/appmanagementlist.component.html @@ -1,80 +1,88 @@ -<div class="col-sm-12 col-sm-offset-1 col-sm 10 col-md-offset-1 col-md-10"> - <h3>{{'APPS_MANAGEMENT.TITLE'| translate}}</h3> - <div style="display:flex; justify-content: space-between"> +<div class=""> + + <div style="display:flex; "> + <div style="margin-right:20px"> + <span class="p-input-icon-right" style="width: 100%"> + <i class="pi pi-search" style="font-size: 13px; top: 16px; margin-right: 5px;"></i> + <input pInputText id="searchText" type="text" class="form-control" placeholder="Search" (keyup)="searchApp($event.target.value)"> + </span> + </div> <div style="display:flex"> <button [routerLink]="['/admin/apps/create']" class="btn btn-primary">{{ 'APPS_MANAGEMENT.ADD_BUTTON' | translate }}</button> <button (click)="appAddJson.show()" class="btn btn-primary" style="margin-left: 10px">{{ 'APPS_MANAGEMENT.ADD_BUTTON' | translate }} (JSON)</button> </div> - <div style="display: flex"> - <input pInputText id="searchText" type="text" placeholder="Search" (keyup)="searchApp($event.target.value)"> - </div> </div> + <h4 class="header">{{'APPS_MANAGEMENT.TITLE'| translate}}</h4> + <div class="background-section"> + <p-table [value]="apps" class="p-datatable-hover p-datatable-sm" [responsive]="true"> + <ng-template pTemplate="header"> + <tr> + <th></th> + <th>{{'APPS_MANAGEMENT.NAME' | translate}}</th> + <th>{{'APPS_MANAGEMENT.OWNER' | translate}}</th> + <th *ngIf="isAnySubtableVisible()">{{'APPS_MANAGEMENT.VERSION' | translate}}</th> + <th *ngIf="isAnySubtableVisible()">{{'APPS_MANAGEMENT.STATE' | translate}}</th> + <th></th> + <th></th> + <th></th> + </tr> + </ng-template> - - <table class="table table-hover table-condensed" aria-describedby="Apps management table" style="margin-top:15px"> - <thead> - <tr> - <th scope="col"></th> - <th scope="col">{{'APPS_MANAGEMENT.NAME' | translate}}</th> - <th scope="col">{{'APPS_MANAGEMENT.OWNER' | translate}}</th> - <th scope="col" *ngIf="isAnySubtableVisible()">{{'APPS_MANAGEMENT.VERSION' | translate}}</th> - <th scope="col" *ngIf="isAnySubtableVisible()">{{'APPS_MANAGEMENT.STATE' | translate}}</th> - <th scope="col"></th> - </tr> - </thead> - - <tbody> - <ng-template ngFor let-app [ngForOf]="apps" let-i="index"> - <tr class="table-row" (click)="clickTableRow(i)"> - <td style="width: 5%" *ngIf="!versionRowVisible[i]"><span class="glyphicon glyphicon-chevron-right"></span></td> - <td style="width: 5%" *ngIf="versionRowVisible[i]"><span class="glyphicon glyphicon-chevron-down"></span></td> - <td style="width: 25%">{{app?.name}}</td> - <td style="width: 20%">{{app?.owner}}</td> - <td style="width: 15%"></td> - <td style="width: 15%"></td> - <td style="width: 20%" class="text-right"> - <span class="dropdown"> - <a style="display: inline-block" class="dropdown-toggle " aria-expanded="false" aria-haspopup="true" data-toggle="dropdown" href="#" role="button"> - <em class="fas fa-cog icon-black icon-bigger"></em> - </a> - <ul class="dropdown-menu pull-right-drop"> - <li *roles="['ROLE_SYSTEM_ADMIN']"> - <a (click)="appChangeOwnerModal.show(app)" >{{ 'APPS_MANAGEMENT.CHANGE_OWNER_BUTTON' | translate }}</a> - </li> - <li> - <a [routerLink]="['/admin/apps/create/version', app?.name]">{{ 'APPS_MANAGEMENT.ADD_NEW_VERSION_BUTTON' | translate }}</a> - </li> - <li> - <a (click)="appAddJsonVersion.show()" >{{ 'APPS_MANAGEMENT.ADD_NEW_VERSION_BUTTON' | translate }} (JSON)</a> - </li> - <li> - <a (click)="getApplicationInfoJSONWithBase(app?.id)"> {{'APPS_MANAGEMENT.EXPORT_JSON' | translate}}</a> - </li> - <li> - <a [routerLink]="['/admin/apps/edit', app?.id]">{{ 'APPS_MANAGEMENT.EDIT_BUTTON' | translate }}</a> - </li> - <li> - <a (click)="openRemovalModal(app)"> {{ 'APPS_MANAGEMENT.DELETE_BUTTON' | translate}}</a> - </li> - </ul> - </span> - </td> - </tr> - <ng-template ngFor let-version [ngForOf]="app.versions.sort(appVersionCompare)"> - <tr *ngIf="versionRowVisible[i]" class="table-row" > - <td [routerLink]="['/admin/apps/edit/version', version?.appVersionId]"></td> - <td [routerLink]="['/admin/apps/edit/version', version?.appVersionId]"></td> - <td [routerLink]="['/admin/apps/edit/version', version?.appVersionId]"></td> - <td [routerLink]="['/admin/apps/edit/version', version?.appVersionId]">{{version.version}}</td> - <td [routerLink]="['/admin/apps/edit/version', version?.appVersionId]">{{"ENUM.STATE." + getStateAsString(version.state).toUpperCase() | translate }}</td> + <ng-template pTemplate="body" let-app let-i="rowIndex"> + <tr> + <td *ngIf="!versionRowVisible[i]" (click)="clickTableRow(i)"> + <span class="pi pi-chevron-right"></span> + </td> + <td *ngIf="versionRowVisible[i]" (click)="clickTableRow(i)"> + <span class="pi pi-chevron-down"></span> + </td> + <td (click)="clickTableRow(i)">{{app?.name}}</td> + <td (click)="clickTableRow(i)">{{app?.owner}}</td> + <td></td> + <td></td> <td class="text-right"> - <a [routerLink]="['/admin/apps/view', version?.appVersionId]"> - <em class="far fa-eye icon-black icon-bigger"></em> - </a> <span class="dropdown"> + <a style="display: inline-block" class="dropdown-toggle " aria-expanded="false" aria-haspopup="true" data-toggle="dropdown" href="#" role="button"> + <em class="pi pi-cog" style="font-size: 1.8rem; color: var(--l-text-color)"></em> + </a> + <ul class="dropdown-menu pull-right-drop"> + <li *roles="['ROLE_SYSTEM_ADMIN']"> + <a (click)="appChangeOwnerModal.show(app)" >{{ 'APPS_MANAGEMENT.CHANGE_OWNER_BUTTON' | translate }}</a> + </li> + <li> + <a [routerLink]="['/admin/apps/create/version', app?.name]">{{ 'APPS_MANAGEMENT.ADD_NEW_VERSION_BUTTON' | translate }}</a> + </li> + <li> + <a (click)="appAddJsonVersion.show()" >{{ 'APPS_MANAGEMENT.ADD_NEW_VERSION_BUTTON' | translate }} (JSON)</a> + </li> + <li> + <a (click)="getApplicationInfoJSONWithBase(app?.id)"> {{'APPS_MANAGEMENT.EXPORT_JSON' | translate}}</a> + </li> + <li> + <a [routerLink]="['/admin/apps/edit', app?.id]">{{ 'APPS_MANAGEMENT.EDIT_BUTTON' | translate }}</a> + </li> + <li> + <a (click)="openRemovalModal(app)"> {{ 'APPS_MANAGEMENT.DELETE_BUTTON' | translate}}</a> + </li> + </ul> + </span> + </td> + </tr> + <ng-container *ngIf="versionRowVisible[i]"> + <tr *ngFor="let version of app.versions.sort(appVersionCompare)"> + <td [routerLink]="['/admin/apps/edit/version', version?.appVersionId]"></td> + <td [routerLink]="['/admin/apps/edit/version', version?.appVersionId]"></td> + <td [routerLink]="['/admin/apps/edit/version', version?.appVersionId]"></td> + <td [routerLink]="['/admin/apps/edit/version', version?.appVersionId]">{{version.version}}</td> + <td [routerLink]="['/admin/apps/edit/version', version?.appVersionId]">{{"ENUM.STATE." + getStateAsString(version.state).toUpperCase() | translate }}</td> + <td class="text-right"> + <a [routerLink]="['/admin/apps/view', version?.appVersionId]"> + <em class="pi pi-eye" style="font-size: 1.8rem; color: var(--l-text-color); margin-right:10px"></em> + </a> + <span class="dropdown"> <a style="display: inline-block" class="dropdown-toggle " aria-expanded="false" aria-haspopup="true" data-toggle="dropdown" href="#" role="button"> - <em class="fas fa-cog icon-black icon-bigger"></em> + <em class="pi pi-cog" style="font-size: 1.8rem; color: var(--l-text-color)"></em> </a> <ul class="dropdown-menu pull-right-drop"> <li *roles="['ROLE_SYSTEM_ADMIN']"> @@ -88,13 +96,12 @@ </li> </ul> </span> - </td> - </tr> + </td> + </tr> + </ng-container> </ng-template> - </ng-template> - </tbody> - </table> - + </p-table> + </div> </div> <app-appchangestatemodal [appName]="selectedAppName" [app]="selectedVersion"></app-appchangestatemodal> diff --git a/src/app/appmarket/appmanagement/app-management.module.ts b/src/app/appmarket/appmanagement/app-management.module.ts index ebaa7d15cc0cb276cc55ef0203201a683c594bdc..fa4470584c3485a0e65ed7be4eba85c84ecc76db 100644 --- a/src/app/appmarket/appmanagement/app-management.module.ts +++ b/src/app/appmarket/appmanagement/app-management.module.ts @@ -34,6 +34,7 @@ import {AppAddJsonAppComponent} from './app-add-json-app/app-add-json-app.compon import {InputTextareaModule} from 'primeng/inputtextarea'; import {AppAddJsonVersionAppComponent} from './app-add-json-version-app/app-add-json-version-app.component'; import {DomainsModule} from '../domains/domains.module'; +import {TableModule} from 'primeng/table'; export function getJsonTemplates(config: ConfigTemplateService) { @@ -83,7 +84,8 @@ export function formioAppConfigFactory(appConfig: AppConfigService) { TooltipModule, DropdownModule, InputTextareaModule, - DomainsModule + DomainsModule, + TableModule ], exports: [], providers: [ diff --git a/src/app/appmarket/appmanagement/app-management.routes.ts b/src/app/appmarket/appmanagement/app-management.routes.ts index 5b7f58eb2b73e13b934ae3e02a10e43d5cc20201..8ef778a898b373941e2f6909a1dd2be0100dd534 100644 --- a/src/app/appmarket/appmanagement/app-management.routes.ts +++ b/src/app/appmarket/appmanagement/app-management.routes.ts @@ -15,48 +15,48 @@ import {AppsummaryComponent} from '../bulkDeployment/appDeployment/appsummary/ap export const AppManagementRoutes: Route[] = [ { - path: 'admin/apps', + path: 'apps', component: AppManagementListComponent, canActivate: [AuthGuard, RoleGuard], data: {roles: ['ROLE_SYSTEM_ADMIN', 'ROLE_TOOL_MANAGER']} }, { - path: 'admin/apps/create', + path: 'apps/create', component: AppCreateWizardComponent, canActivate: [AuthGuard, RoleGuard], data: {roles: ['ROLE_SYSTEM_ADMIN', 'ROLE_TOOL_MANAGER'], mode: ComponentMode.CREATE} }, { - path: 'admin/apps/create/version/:name', + path: 'apps/create/version/:name', component: AppVersionCreateWizardComponent, canActivate: [AuthGuard, RoleGuard], data: {roles: ['ROLE_SYSTEM_ADMIN', 'ROLE_TOOL_MANAGER'], mode: ComponentMode.CREATE} }, { - path: 'admin/apps/edit/:id', + path: 'apps/edit/:id', component: AppCreateWizardComponent, canActivate: [AuthGuard, RoleGuard], data: {roles: ['ROLE_SYSTEM_ADMIN', 'ROLE_TOOL_MANAGER'], mode: ComponentMode.EDIT} }, { - path: 'admin/apps/edit/version/:id', + path: 'apps/edit/version/:id', component: AppVersionCreateWizardComponent, canActivate: [AuthGuard, RoleGuard], data: {roles: ['ROLE_SYSTEM_ADMIN', 'ROLE_TOOL_MANAGER'], mode: ComponentMode.EDIT} }, { - path: 'admin/apps/view/:id', + path: 'apps/view/:id', component: AppPreviewComponent, canActivate: [AuthGuard, RoleGuard], data: {roles: ['ROLE_SYSTEM_ADMIN', 'ROLE_TOOL_MANAGER']} }, { - path: 'admin/apps/bulks', + path: 'apps/bulks', component: BulkAppListComponent, canActivate: [AuthGuard, RoleGuard], data: {roles: ['ROLE_SYSTEM_ADMIN', 'ROLE_GROUP_MANAGER']} }, - { path: 'admin/apps/bulks/new', + { path: 'apps/bulks/new', component: AppnavigatorComponent, children: [ {path: '', redirectTo: 'select', pathMatch: 'full'}, @@ -65,7 +65,7 @@ export const AppManagementRoutes: Route[] = [ {path: 'summary', component: AppsummaryComponent} ]}, { - path: 'admin/apps/bulks/:id', + path: 'apps/bulks/:id', component: BulkViewComponent, canActivate: [AuthGuard, RoleGuard], data: {roles: ['ROLE_SYSTEM_ADMIN', 'ROLE_GROUP_MANAGER' ]} diff --git a/src/app/appmarket/appmarket.component.html b/src/app/appmarket/appmarket.component.html index b3848168ed1d35d831c17ba1c0cfa2297244b960..13e6a41fc2ccea1b205d6fc7587baacfa442d8a5 100644 --- a/src/app/appmarket/appmarket.component.html +++ b/src/app/appmarket/appmarket.component.html @@ -1,4 +1,4 @@ -<div class="container" id="appmarket-container"> +<div class="" style="margin:40px" id="appmarket-container"> <router-outlet></router-outlet> </div> <modal-test-instance></modal-test-instance> diff --git a/src/app/appmarket/appmarket.routes.ts b/src/app/appmarket/appmarket.routes.ts index f1c40acf24a4f60353bf6c957a51de17a704ecd1..2d0196a195449dad55a32f1f6ed4a791b6a0b657 100644 --- a/src/app/appmarket/appmarket.routes.ts +++ b/src/app/appmarket/appmarket.routes.ts @@ -13,6 +13,8 @@ import {ConfigurationRoutes} from './admin/configuration/configuration.routes'; import {MonitorRoutes} from './admin/monitor/monitor.routes'; import {AppManagementRoutes} from './appmanagement/app-management.routes'; import {LanguageManagementRoutes} from './admin/languagemanagement/languagemanagement.routes'; +import { AdminLeftMenuComponent } from '../shared/admin-left-menu/admin-left-menu.component'; +import { AdminDashboardComponent } from '../shared/admin-dashboard/admin-dashboard.component'; export const AppMarketRoutes: Route[] = [ { @@ -23,15 +25,32 @@ export const AppMarketRoutes: Route[] = [ children: [ ...AppListRoutes, ...AppInstanceRoutes, + { path: 'apps/:id', component: AppDetailsComponent }, + + ] + }, + { + path: 'admin', + component: AdminLeftMenuComponent, + canActivate: [AuthGuard], + canActivateChild: [AuthGuard], + children: [ + { + path: '', + redirectTo: '/admin/dashboard', + pathMatch: 'full' + }, + { + path: 'dashboard', + component: AdminDashboardComponent + }, ...DomainsRoutes, ...UsersRoutes, ...ClustersRoutes, - ...ConfigurationRoutes, - ...MonitorRoutes, - ...AppManagementRoutes, - ...LanguageManagementRoutes, - { path: 'apps/:id', component: AppDetailsComponent }, - + ...ConfigurationRoutes, + ...MonitorRoutes, + ...AppManagementRoutes, + ...LanguageManagementRoutes, ] } ]; diff --git a/src/app/appmarket/bulkDeployment/appDeployment/appupload/appupload.component.css b/src/app/appmarket/bulkDeployment/appDeployment/appupload/appupload.component.css new file mode 100644 index 0000000000000000000000000000000000000000..f2c387a9db7ec1e2c9aeefc41a92b4d6336c9fdb --- /dev/null +++ b/src/app/appmarket/bulkDeployment/appDeployment/appupload/appupload.component.css @@ -0,0 +1,38 @@ +:host ::ng-deep input[type=file]{ + display:none; +} +:host ::ng-deep .p-button{ + width: unset; + margin-right: 5px; + background: var(--primary-button-color); + color: var(--button-text-color); +} +:host ::ng-deep .p-button:hover{ + background: var(--primary-button-hover); + border:none; +} +:host ::ng-deep .p-button-label{ + font-weight: normal; +} +:host ::ng-deep .p-fileupload .p-fileupload-buttonbar{ + border: none; + background: transparent; + margin-bottom: 10px; + padding: 0; +} +:host ::ng-deep .p-fileupload .p-fileupload-content{ + border: none; + padding: 0; + border-radius: 3px; + +} +:host ::ng-deep .p-fileupload-content .p-progressbar{ + display: none; +} +textarea{ + border-color: #ccc; +} +:host ::ng-deep .p-inputtext:enabled:focus{ + box-shadow: none; + border-color: var(--l-text-color); +} diff --git a/src/app/appmarket/bulkDeployment/appDeployment/appupload/appupload.component.html b/src/app/appmarket/bulkDeployment/appDeployment/appupload/appupload.component.html index 328d1af22da2d36c22a9624a6d9c57e56fa4e5d3..1385e9861fe9bd8022d8459450797eee0ea952f1 100644 --- a/src/app/appmarket/bulkDeployment/appDeployment/appupload/appupload.component.html +++ b/src/app/appmarket/bulkDeployment/appDeployment/appupload/appupload.component.html @@ -17,7 +17,7 @@ <div style="margin-top: 1.5rem; margin-bottom: 1rem"> <p>{{'BULK.APP.UPLOAD_TEXT' | translate}}</p> </div> - <textarea pInputTextarea [(ngModel)]="csvText" rows="10" cols="127" (keyup)="changeDetector = true"></textarea> + <textarea pInputTextarea [(ngModel)]="csvText" rows="10" cols="127" [autoResize]="true" (keyup)="changeDetector = true"></textarea> </div> <div *ngIf="errorMessage !== ''" style="margin-top: 1rem; display: flex; justify-content: start; color: indianred"> diff --git a/src/app/appmarket/bulkDeployment/appDeployment/appupload/appupload.component.ts b/src/app/appmarket/bulkDeployment/appDeployment/appupload/appupload.component.ts index d718907e2389cbb9192bacddda36ad28e8733c32..015af6e6d18fc46553d4898a6e79e531d0f2c34e 100644 --- a/src/app/appmarket/bulkDeployment/appDeployment/appupload/appupload.component.ts +++ b/src/app/appmarket/bulkDeployment/appDeployment/appupload/appupload.component.ts @@ -6,7 +6,7 @@ import {AppImagesService} from '../../../../service'; @Component({ selector: 'app-appupload', templateUrl: './appupload.component.html', - styleUrls: [] + styleUrls: ['./appupload.component.css'] }) export class AppuploadComponent implements OnInit { diff --git a/src/app/appmarket/bulkDeployment/bulk-list/bulk-list.component.css b/src/app/appmarket/bulkDeployment/bulk-list/bulk-list.component.css index 90bb2e521b576a5d1a687e77ce002fc4b7cded50..4cf5f82b19e0be3b3cffaf9b59b5303e36e7c46b 100644 --- a/src/app/appmarket/bulkDeployment/bulk-list/bulk-list.component.css +++ b/src/app/appmarket/bulkDeployment/bulk-list/bulk-list.component.css @@ -8,3 +8,55 @@ margin-left: 5px; margin-right: 5px; } +:host ::ng-deep .p-datatable .p-datatable-thead > tr > th{ + border: 1px solid #E0E2E5; + background:transparent; + border-width: 0 0 1px 0; +} +:host ::ng-deep .p-datatable .p-datatable-tbody > tr > td { + text-align: left; + border: 1px solid #E0E2E5; + border-width: 0 0 1px 0; + padding: 1rem 1rem; +} +:host ::ng-deep .p-datatable .p-paginator-bottom{ + height: 40px; + background: transparent; + border: none; + margin-top:10px; +} +:host ::ng-deep .p-datatable .p-datatable-tbody > tr{ + background: transparent; +} +:host ::ng-deep .p-datatable>.p-datatable-wrapper { + overflow: visible; +} +:host ::ng-deep .p-paginator .p-paginator-pages .p-paginator-page{ + transition: unset; + border-radius: 50%; + min-width:3.5rem; + height:3.5rem; + margin:0 5px; + font-size: 14px; +} + +:host ::ng-deep .p-paginator-element{ + border-radius:50%; + margin:0 5px; + min-width:3.5rem; + height:3.5rem; + font-size: 14px; +} +:host ::ng-deep .p-paginator .p-dropdown{ + height:3rem; +} +:host ::ng-deep .p-paginator-icon{ + height: 1.5rem; + width: 1.5rem; +} +:host ::ng-deep .p-paginator .p-dropdown .p-dropdown-label{ + padding-right: 10px; +} +:host ::ng-deep .p-paginator .p-paginator-pages .p-paginator-page.p-highlight{ + background: var(--user-button-background-hover); +} diff --git a/src/app/appmarket/bulkDeployment/bulk-list/bulk-list.component.html b/src/app/appmarket/bulkDeployment/bulk-list/bulk-list.component.html index d974dc0a1a2154e5679a4c39d6efd633d8839c0a..a5a8ef712c04e2e0127d1a1aa305b201c93abee4 100644 --- a/src/app/appmarket/bulkDeployment/bulk-list/bulk-list.component.html +++ b/src/app/appmarket/bulkDeployment/bulk-list/bulk-list.component.html @@ -1,156 +1,252 @@ -<div class="col-sm-12 col-sm-offset-1 col-sm 10 col-md-offset-1 col-md-10"> - <h3>{{header | translate}}</h3> - <div class="" style="display: flex; justify-content: space-between; margin-top: 10px;"> - <div *ngIf="mode=== bulkTypeDomain"> +<div class=""> + <div style="display:flex; "> + <div style="margin-right:20px"> + <span class="p-input-icon-right" style="width: 100%"> + <i class="pi pi-search" style="font-size: 13px; top: 16px; margin-right: 5px;"></i> + <input pInputText class="flex" name="search" id="search" placeholder="Search" type="text" + style="height: 34px" [(ngModel)]="searchValue"> + </span> + </div> + <div *ngIf="mode=== bulkTypeDomain" style="margin-right: 10px"> <button class="btn btn-primary" [routerLink]="['/admin/domains/bulks/new']">New deployment</button> </div> - <div *ngIf="mode=== bulkTypeApp"> + <div *ngIf="mode=== bulkTypeApp" style="margin-right: 10px"> <button class="btn btn-primary" [routerLink]="['/admin/apps/bulks/new']">New deployment</button> </div> - - <div class="flex"> - <div *roles="['ROLE_SYSTEM_ADMIN', 'ROLE_GROUP_MANAGER']" class="flex align-items-center mr-6"> - - <p-button - type="button" - class="mr-2" - (onClick)="sidebarVisible4 = true" + <div *roles="['ROLE_SYSTEM_ADMIN', 'ROLE_GROUP_MANAGER']" class="flex align-items-center mr-6"> + <p-button + type="button" + class="" + (onClick)="sidebarVisible4 = true" label="Deployments Queue" severity="secondary" - ></p-button> - <p-sidebar [(visible)]="sidebarVisible4" position="left" styleClass="w-30rem"> - <h3>Deployments Queue</h3> - <div class="flex flex-column"> - <div class="flex grid"> - <label class="col-10" for="jobInQueue">Jobs in queue</label> - <span class="col-2 " id="jobInQueue">{{queueDetails?.jobInQueue}}</span> - </div> - <div class="flex grid"> - <label class="col-10" for="jobInProcess">In progress</label> - <span class="col-2 " id="jobInProcess">{{queueDetails?.jobInProcess}}</span> - </div> - <div class="flex grid"> - <label class="col-10" for="jobInProcessId">Current proccessing bulk id</label> - <span class="col-2" id="jobInProcessId">{{queueDetails?.jobInProcessId}}</span> - </div> + ></p-button> + <p-sidebar [(visible)]="sidebarVisible4" position="left" styleClass="w-30rem"> + <h3>Deployments Queue</h3> + <div class="flex flex-column"> + <div class="flex grid"> + <label class="col-10" for="jobInQueue">Jobs in queue</label> + <span class="col-2 " id="jobInQueue">{{queueDetails?.jobInQueue}}</span> </div> - </p-sidebar> - - </div> - <div *roles="['ROLE_SYSTEM_ADMIN']" class="flex align-items-center mr-6 pt-2"> - <label *ngIf="mode=== bulkTypeApp" class="mr-2" for="showDeleted">Show all</label> - <p-inputSwitch *ngIf="mode=== bulkTypeApp" id="showDeleted" (onChange)="refreshBulks()" [(ngModel)]="showDeleted" ngDefaultControl/> - </div> - <div class="flex align-items-center mr-1">{{ 'BULK.LIST.PER_PAGE' | translate }}:</div> - <span id="selectionItems" class="dropdown" - style="vertical-align: middle; display: inline-block; margin-right: 1rem;"> - <button class="dropdown-toggle btn" data-toggle="dropdown" data-close-others="true"> - {{maxItemsOnPage}} - </button> - <ul class="dropdown-menu"> - <li *ngFor="let item of itemsPerPage" [ngClass]="{'active': maxItemsOnPage == item}"> - <a (click)="setItems(item)"> - <span>{{item.toString()}}</span> - </a> - </li> - </ul> - </span> - <input pInputText class="flex" name="search" id="search" placeholder="Search" type="text" - style="height: 34px" [(ngModel)]="searchValue"> + <div class="flex grid"> + <label class="col-10" for="jobInProcess">In progress</label> + <span class="col-2 " id="jobInProcess">{{queueDetails?.jobInProcess}}</span> + </div> + <div class="flex grid"> + <label class="col-10" for="jobInProcessId">Current proccessing bulk id</label> + <span class="col-2" id="jobInProcessId">{{queueDetails?.jobInProcessId}}</span> + </div> + </div> + </p-sidebar> + + </div> + <div *roles="['ROLE_SYSTEM_ADMIN']" class="flex align-items-center mr-6 pt-2"> + <label *ngIf="mode=== bulkTypeApp" class="mr-2" for="showDeleted">Show all</label> + <p-inputSwitch *ngIf="mode=== bulkTypeApp" id="showDeleted" (onChange)="refreshBulks()" [(ngModel)]="showDeleted" ngDefaultControl/> </div> </div> - <table *ngIf="mode === 'DOMAIN'" class="table table-hover table-condensed" style="margin-top: 3rem" - aria-describedby="Bulk deployment table" sortable-table (sorted)="onSort($event)"> - <thead> - <tr> - <th scope="col" class="column-sortable" sortable-column="id">{{'BULK.LIST.ID' | translate}}</th> - <th scope="col" class="column-sortable" sortable-column="creator">{{'BULK.LIST.CREATOR' | translate}}</th> - <th scope="col" class="column-sortable" sortable-column="date">{{'BULK.LIST.CREATION_DATE' | translate}}</th> - <th scope="col" class="column-sortable" sortable-column="state">{{'BULK.LIST.STATE' | translate}}</th> - <th scope="col" ></th> - </tr> - </thead> + <h4 class="header">{{header | translate}}</h4> +<!-- <div class="" style="display: flex; justify-content: space-between; margin-top: 10px;">--> +<!-- <div class="flex">--> +<!-- <div class="flex align-items-center mr-1">{{ 'BULK.LIST.PER_PAGE' | translate }}:</div>--> +<!-- <span id="selectionItems" class="dropdown"--> +<!-- style="vertical-align: middle; display: inline-block; margin-right: 1rem;">--> +<!-- <button class="dropdown-toggle btn" data-toggle="dropdown" data-close-others="true">--> +<!-- {{maxItemsOnPage}}--> +<!-- </button>--> +<!-- <ul class="dropdown-menu">--> +<!-- <li *ngFor="let item of itemsPerPage" [ngClass]="{'active': maxItemsOnPage == item}">--> +<!-- <a (click)="setItems(item)">--> +<!-- <span>{{item.toString()}}</span>--> +<!-- </a>--> +<!-- </li>--> +<!-- </ul>--> +<!-- </span>--> - <tbody> - <ng-template ngFor let-bulk [ngForOf]="bulks" let-i="index"> - <tr class="table-row"> - <td style="width: 10%">{{bulk?.id}}</td> - <td style="width: 25%">{{bulk?.creator.username}}</td> - <td style="width: 25%">{{bulk?.creationDate | date: 'dd-MM-yyyy HH:mm'}}</td> - <td style="width: 15%">{{'BULK.STATE.' + bulk?.state | translate}}</td> - <td style="width: 20%" class="text-right"> - <span class="dropdown"> - <a style="display: inline-block" class="dropdown-toggle " aria-expanded="false" aria-haspopup="true" - data-toggle="dropdown" href="#" role="button"> - <em class="fas fa-cog icon-black icon-bigger"></em> - </a> - <ul class="dropdown-menu pull-right-drop"> - <li *ngIf="mode === bulkTypeDomain"> - <a [routerLink]="['/admin/domains/bulks/', bulk?.id]">{{ 'BULK.LIST.DETAILS' | translate }}</a> - </li> - </ul> - </span> - </td> - </tr> - </ng-template> - </tbody> - </table> - <table *ngIf="mode === 'APPLICATION'" class="table table-hover table-condensed" style="margin-top: 3rem" - aria-describedby="Bulk deployment table" sortable-table (sorted)="onSort($event)"> - <thead> - <tr> - <th scope="col" class="column-sortable" sortable-column="id">{{'BULK.LIST.ID' | translate}}</th> - <th scope="col" class="column-sortable" sortable-column="creator">{{'BULK.LIST.CREATOR' | translate}}</th> - <th scope="col" class="column-sortable" sortable-column="app_name">{{'BULK.LIST.APP_NAME' | translate}}</th> - <th scope="col" class="column-sortable" - sortable-column="instance_no">{{'BULK.LIST.INSTANCE_NO' | translate}}</th> - <th scope="col" class="column-sortable" sortable-column="date" - sort-direction="desc">{{'BULK.LIST.CREATION_DATE' | translate}}</th> - <th scope="col" class="column-sortable" sortable-column="state">{{'BULK.LIST.STATE' | translate}}</th> - <th scope="col"></th> - </tr> - </thead> +<!-- </div>--> +<!-- </div>--> + <div *ngIf="mode === 'DOMAIN'" class="background-section"> + <p-table *ngIf="mode === 'DOMAIN'" [value]="bulks" class="p-datatable-hover p-datatable-sm" [responsive]="true" (onSort)="onSort($event)"> + <ng-template pTemplate="header"> + <tr> + <th pSortableColumn="id">{{'BULK.LIST.ID' | translate}}</th> + <th pSortableColumn="creator">{{'BULK.LIST.CREATOR' | translate}}</th> + <th pSortableColumn="date">{{'BULK.LIST.CREATION_DATE' | translate}}</th> + <th pSortableColumn="state">{{'BULK.LIST.STATE' | translate}}</th> + <th></th> + </tr> + </ng-template> - <tbody> - <ng-template ngFor let-bulk - [ngForOf]="bulks | searchBulk: searchValue: true | paginate: {itemsPerPage: maxItemsOnPage, currentPage: p}" - let-i="index"> - <tr class="table-row"> - <td style="width: 5%">{{bulk?.id}}</td> - <td style="width: 15%">{{bulk?.creator.username}}</td> - <td style="width: 20%">{{getApplicationName(bulk?.details)}}</td> - <td style="width: 20%">{{getInstancesNumber(bulk?.details)}}</td> - <td style="width: 15%">{{bulk?.creationDate | date: 'dd-MM-yyyy HH:mm'}}</td> - <td style="width: 20%">{{'BULK.STATE.' + bulk?.state | translate}}</td> - <td style="width: 5%" class="text-right"> - <span class="dropdown"> - <a style="display: inline-block" class="dropdown-toggle " aria-expanded="false" aria-haspopup="true" - data-toggle="dropdown" href="#" role="button"> - <em class="fas fa-cog icon-black icon-bigger"></em> - </a> - <ul class="dropdown-menu pull-right-drop"> - <li *ngIf="mode === bulkTypeApp"> - <a [routerLink]="['/admin/apps/bulks/', bulk?.id]">{{ 'BULK.LIST.DETAILS' | translate }}</a> - </li> - <li *ngIf="mode === bulkTypeApp && bulk?.state !== 'REMOVED'"> - <a (click)="getAppBulkDetails(bulk?.id)"> {{"BULK.APP.DOWNLOAD_CSV" | translate}}</a> - </li> - <li *ngIf="mode === bulkTypeApp && !(bulk?.state === 'REMOVED' || bulk?.deleted )"> - <a (click)="modal.show(); removeBulkId=bulk?.id">{{ 'BULK.LIST.REMOVE' | translate }}</a> - </li> - </ul> - </span> - </td> - </tr> - </ng-template> - </tbody> - </table> - <pagination-controls class="text-right" (pageChange)="p = $event" - previousLabel="{{ 'PAGINATION.PREVIOUS' | translate }}" - nextLabel="{{ 'PAGINATION.NEXT' | translate }}" - screenReaderPaginationLabel="{{ 'PAGINATION.SCREEN_READER.PAGINATION' | translate }}" - screenReaderPageLabel="{{ 'PAGINATION.SCREEN_READER.PAGE' | translate }}" - screenReaderCurrentLabel="{{ 'PAGINATION.SCREEN_READER.CURRENT' | translate }}"></pagination-controls> + <ng-template pTemplate="body" let-bulk> + <tr class="table-row"> + <td>{{bulk?.id}}</td> + <td>{{bulk?.creator.username}}</td> + <td>{{bulk?.creationDate | date: 'dd-MM-yyyy HH:mm'}}</td> + <td>{{'BULK.STATE.' + bulk?.state | translate}}</td> + <td class="text-right"> + <span class="dropdown"> + <a style="display: inline-block" class="dropdown-toggle " aria-expanded="false" aria-haspopup="true" data-toggle="dropdown" href="#" role="button"> + <em class="fas fa-cog icon-black icon-bigger"></em> + </a> + <ul class="dropdown-menu pull-right-drop"> + <li *ngIf="mode === bulkTypeDomain"> + <a [routerLink]="['/admin/domains/bulks/', bulk?.id]">{{ 'BULK.LIST.DETAILS' | translate }}</a> + </li> + </ul> + </span> + </td> + </tr> + </ng-template> + </p-table> + + +<!-- <table *ngIf="mode === 'DOMAIN'" class="table table-hover table-condensed" style="margin-top: 3rem"--> +<!-- aria-describedby="Bulk deployment table" sortable-table (sorted)="onSort($event)">--> +<!-- <thead>--> +<!-- <tr>--> +<!-- <th scope="col" class="column-sortable" sortable-column="id">{{'BULK.LIST.ID' | translate}}</th>--> +<!-- <th scope="col" class="column-sortable" sortable-column="creator">{{'BULK.LIST.CREATOR' | translate}}</th>--> +<!-- <th scope="col" class="column-sortable" sortable-column="date">{{'BULK.LIST.CREATION_DATE' | translate}}</th>--> +<!-- <th scope="col" class="column-sortable" sortable-column="state">{{'BULK.LIST.STATE' | translate}}</th>--> +<!-- <th scope="col" ></th>--> +<!-- </tr>--> +<!-- </thead>--> + +<!-- <tbody>--> +<!-- <ng-template ngFor let-bulk [ngForOf]="bulks" let-i="index">--> +<!-- <tr class="table-row">--> +<!-- <td style="width: 10%">{{bulk?.id}}</td>--> +<!-- <td style="width: 25%">{{bulk?.creator.username}}</td>--> +<!-- <td style="width: 25%">{{bulk?.creationDate | date: 'dd-MM-yyyy HH:mm'}}</td>--> +<!-- <td style="width: 15%">{{'BULK.STATE.' + bulk?.state | translate}}</td>--> +<!-- <td style="width: 20%" class="text-right">--> +<!-- <span class="dropdown">--> +<!-- <a style="display: inline-block" class="dropdown-toggle " aria-expanded="false" aria-haspopup="true"--> +<!-- data-toggle="dropdown" href="#" role="button">--> +<!-- <em class="fas fa-cog icon-black icon-bigger"></em>--> +<!-- </a>--> +<!-- <ul class="dropdown-menu pull-right-drop">--> +<!-- <li *ngIf="mode === bulkTypeDomain">--> +<!-- <a [routerLink]="['/admin/domains/bulks/', bulk?.id]">{{ 'BULK.LIST.DETAILS' | translate }}</a>--> +<!-- </li>--> +<!-- </ul>--> +<!-- </span>--> +<!-- </td>--> +<!-- </tr>--> +<!-- </ng-template>--> +<!-- </tbody>--> +<!-- </table>--> + </div> + + <div *ngIf="mode === 'APPLICATION'" class="background-section"> + <p-table *ngIf="mode === 'APPLICATION'" [value]="bulks | searchBulk: searchValue: true " + class="p-datatable-hover p-datatable-sm" + [responsive]="true" + [paginator]="true" + [rows]="maxItemsOnPage" + [rowsPerPageOptions]="[15, 20, 25, 30, 50]" + (onSort)="onSort($event)"> + <ng-template pTemplate="header"> + <tr> + <th pSortableColumn="id">{{'BULK.LIST.ID' | translate}}</th> + <th pSortableColumn="creator">{{'BULK.LIST.CREATOR' | translate}}</th> + <th pSortableColumn="app_name">{{'BULK.LIST.APP_NAME' | translate}}</th> + <th pSortableColumn="instance_no">{{'BULK.LIST.INSTANCE_NO' | translate}}</th> + <th pSortableColumn="date" pSortOrder="-1">{{'BULK.LIST.CREATION_DATE' | translate}}</th> + <th pSortableColumn="state">{{'BULK.LIST.STATE' | translate}}</th> + <th></th> + </tr> + </ng-template> + + <ng-template pTemplate="body" let-bulk> + <tr class="table-row"> + <td>{{bulk?.id}}</td> + <td>{{bulk?.creator.username}}</td> + <td>{{getApplicationName(bulk?.details)}}</td> + <td>{{getInstancesNumber(bulk?.details)}}</td> + <td>{{bulk?.creationDate | date: 'dd-MM-yyyy HH:mm'}}</td> + <td>{{'BULK.STATE.' + bulk?.state | translate}}</td> + <td class="text-right"> + <span class="dropdown"> + <a style="display: inline-block" class="dropdown-toggle " aria-expanded="false" aria-haspopup="true" data-toggle="dropdown" href="#" role="button"> + <em class="fas fa-cog icon-black icon-bigger"></em> + </a> + <ul class="dropdown-menu pull-right-drop"> + <li *ngIf="mode === bulkTypeApp"> + <a [routerLink]="['/admin/apps/bulks/', bulk?.id]">{{ 'BULK.LIST.DETAILS' | translate }}</a> + </li> + <li *ngIf="mode === bulkTypeApp && bulk?.state !== 'REMOVED'"> + <a (click)="getAppBulkDetails(bulk?.id)"> {{"BULK.APP.DOWNLOAD_CSV" | translate}}</a> + </li> + <li *ngIf="mode === bulkTypeApp && !(bulk?.state === 'REMOVED' || bulk?.deleted )"> + <a (click)="modal.show(); removeBulkId=bulk?.id">{{ 'BULK.LIST.REMOVE' | translate }}</a> + </li> + </ul> + </span> + </td> + </tr> + </ng-template> + </p-table> + +<!-- <table *ngIf="mode === 'APPLICATION'" class="table table-hover table-condensed" style="margin-top: 3rem"--> +<!-- aria-describedby="Bulk deployment table" sortable-table (sorted)="onSort($event)">--> +<!-- <thead>--> +<!-- <tr>--> +<!-- <th scope="col" class="column-sortable" sortable-column="id">{{'BULK.LIST.ID' | translate}}</th>--> +<!-- <th scope="col" class="column-sortable" sortable-column="creator">{{'BULK.LIST.CREATOR' | translate}}</th>--> +<!-- <th scope="col" class="column-sortable" sortable-column="app_name">{{'BULK.LIST.APP_NAME' | translate}}</th>--> +<!-- <th scope="col" class="column-sortable"--> +<!-- sortable-column="instance_no">{{'BULK.LIST.INSTANCE_NO' | translate}}</th>--> +<!-- <th scope="col" class="column-sortable" sortable-column="date"--> +<!-- sort-direction="desc">{{'BULK.LIST.CREATION_DATE' | translate}}</th>--> +<!-- <th scope="col" class="column-sortable" sortable-column="state">{{'BULK.LIST.STATE' | translate}}</th>--> +<!-- <th scope="col"></th>--> +<!-- </tr>--> +<!-- </thead>--> + +<!-- <tbody>--> +<!-- <ng-template ngFor let-bulk--> +<!-- [ngForOf]="bulks | searchBulk: searchValue: true | paginate: {itemsPerPage: maxItemsOnPage, currentPage: p}"--> +<!-- let-i="index">--> +<!-- <tr class="table-row">--> +<!-- <td style="width: 5%">{{bulk?.id}}</td>--> +<!-- <td style="width: 15%">{{bulk?.creator.username}}</td>--> +<!-- <td style="width: 20%">{{getApplicationName(bulk?.details)}}</td>--> +<!-- <td style="width: 20%">{{getInstancesNumber(bulk?.details)}}</td>--> +<!-- <td style="width: 15%">{{bulk?.creationDate | date: 'dd-MM-yyyy HH:mm'}}</td>--> +<!-- <td style="width: 20%">{{'BULK.STATE.' + bulk?.state | translate}}</td>--> +<!-- <td style="width: 5%" class="text-right">--> +<!-- <span class="dropdown">--> +<!-- <a style="display: inline-block" class="dropdown-toggle " aria-expanded="false" aria-haspopup="true"--> +<!-- data-toggle="dropdown" href="#" role="button">--> +<!-- <em class="fas fa-cog icon-black icon-bigger"></em>--> +<!-- </a>--> +<!-- <ul class="dropdown-menu pull-right-drop">--> +<!-- <li *ngIf="mode === bulkTypeApp">--> +<!-- <a [routerLink]="['/admin/apps/bulks/', bulk?.id]">{{ 'BULK.LIST.DETAILS' | translate }}</a>--> +<!-- </li>--> +<!-- <li *ngIf="mode === bulkTypeApp && bulk?.state !== 'REMOVED'">--> +<!-- <a (click)="getAppBulkDetails(bulk?.id)"> {{"BULK.APP.DOWNLOAD_CSV" | translate}}</a>--> +<!-- </li>--> +<!-- <li *ngIf="mode === bulkTypeApp && !(bulk?.state === 'REMOVED' || bulk?.deleted )">--> +<!-- <a (click)="modal.show(); removeBulkId=bulk?.id">{{ 'BULK.LIST.REMOVE' | translate }}</a>--> +<!-- </li>--> +<!-- </ul>--> +<!-- </span>--> +<!-- </td>--> +<!-- </tr>--> +<!-- </ng-template>--> +<!-- </tbody>--> +<!-- </table>--> + + </div> + +<!-- <pagination-controls class="text-right" (pageChange)="p = $event"--> +<!-- previousLabel="{{ 'PAGINATION.PREVIOUS' | translate }}"--> +<!-- nextLabel="{{ 'PAGINATION.NEXT' | translate }}"--> +<!-- screenReaderPaginationLabel="{{ 'PAGINATION.SCREEN_READER.PAGINATION' | translate }}"--> +<!-- screenReaderPageLabel="{{ 'PAGINATION.SCREEN_READER.PAGE' | translate }}"--> +<!-- screenReaderCurrentLabel="{{ 'PAGINATION.SCREEN_READER.CURRENT' | translate }}"></pagination-controls>--> </div> diff --git a/src/app/appmarket/bulkDeployment/bulk-view/bulk-view.component.html b/src/app/appmarket/bulkDeployment/bulk-view/bulk-view.component.html index 9c870590051db4025f7b3d0dffbaeee499a71785..eabf5c0f2d06498168af5f2f5d6fe8efc599a9d5 100644 --- a/src/app/appmarket/bulkDeployment/bulk-view/bulk-view.component.html +++ b/src/app/appmarket/bulkDeployment/bulk-view/bulk-view.component.html @@ -1,285 +1,288 @@ -<div class="col-sm-12 col-sm-offset-1 col-sm-10 col-md-offset-1 col-md-10"> - <div *ngIf="bulk && bulkType === 'DOMAIN' "> - <h3>{{'BULK.DOMAIN.HEADER_VIEW' | translate}}</h3> - <div class="" style="padding-bottom: 5rem; margin-top: 3rem"> - <label for="id" class="col-sm-2 control-label text-right mt-2">{{ 'BULK.LIST.ID' | translate }}</label> - <div class="col-sm-10"> - <input type="text" class="form-control" id="id" name="id" [disabled]="true" - [(ngModel)]="bulk.id" #name="ngModel"> +<div class=""> + <div class="background-section"> + <div *ngIf="bulk && bulkType === 'DOMAIN' "> + <h3>{{'BULK.DOMAIN.HEADER_VIEW' | translate}}</h3> + <div class="" style="padding-bottom: 5rem; margin-top: 3rem"> + <label for="id" class="col-sm-2 control-label text-right mt-2">{{ 'BULK.LIST.ID' | translate }}</label> + <div class="col-sm-10"> + <input type="text" class="form-control" id="id" name="id" [disabled]="true" + [(ngModel)]="bulk.id" #name="ngModel"> + </div> </div> - </div> - <div class="" style="padding-bottom: 5rem"> - <label for="creator" - class="col-sm-2 control-label text-right mt-2">{{ 'BULK.LIST.CREATOR' | translate }}</label> - <div class="col-sm-10"> - <input type="text" class="form-control" id="creator" name="creator" [disabled]="true" - placeholder="{{bulk.creator.username}} ({{bulk.creator.firstname}} {{bulk.creator.lastname}})"> + <div class="" style="padding-bottom: 5rem"> + <label for="creator" + class="col-sm-2 control-label text-right mt-2">{{ 'BULK.LIST.CREATOR' | translate }}</label> + <div class="col-sm-10"> + <input type="text" class="form-control" id="creator" name="creator" [disabled]="true" + placeholder="{{bulk.creator.username}} ({{bulk.creator.firstname}} {{bulk.creator.lastname}})"> + </div> </div> - </div> - <div class="" style="padding-bottom: 5rem"> - <label for="date" - class="col-sm-2 control-label text-right mt-2">{{ 'BULK.LIST.CREATION_DATE' | translate }}</label> - <div class="col-sm-10"> - <input type="text" class="form-control" id="date" name="date" [disabled]="true" - placeholder="{{bulk.creationDate | date: 'dd-MM-yyyy HH:mm'}}"> + <div class="" style="padding-bottom: 5rem"> + <label for="date" + class="col-sm-2 control-label text-right mt-2">{{ 'BULK.LIST.CREATION_DATE' | translate }}</label> + <div class="col-sm-10"> + <input type="text" class="form-control" id="date" name="date" [disabled]="true" + placeholder="{{bulk.creationDate | date: 'dd-MM-yyyy HH:mm'}}"> + </div> </div> - </div> - <div class="form-group" style="padding-bottom: 5rem"> - <label for="state" - class="col-sm-2 control-label text-right mt-2">{{ 'BULK.LIST.STATE' | translate }}</label> - <div class="col-sm-10"> - <input type="text" class="form-control" id="state" name="state" [disabled]="true" - placeholder="{{'BULK.STATE.' + bulk.state | translate}}"> + <div class="form-group" style="padding-bottom: 5rem"> + <label for="state" + class="col-sm-2 control-label text-right mt-2">{{ 'BULK.LIST.STATE' | translate }}</label> + <div class="col-sm-10"> + <input type="text" class="form-control" id="state" name="state" [disabled]="true" + placeholder="{{'BULK.STATE.' + bulk.state | translate}}"> + </div> </div> - </div> - <div class="panel panel-default" style="margin-top: 3rem"> - <div class="panel-heading"> + <div class="panel panel-default" style="margin-top: 3rem"> + <div class="panel-heading"> - <div style="display: flex; justify-content: start; align-items: center"> - <div> - {{ 'BULK.DOMAIN.DEPLOYMENTS' | translate }} + <div style="display: flex; justify-content: start; align-items: center"> + <div> + {{ 'BULK.DOMAIN.DEPLOYMENTS' | translate }} + </div> </div> </div> - </div> - <div class="panel-body"> - <table class="table table-hover table-condensed" aria-describedby="Domains in Group table"> - <thead> - <tr> - <th scope="col">{{'BULK.LIST.STATE' | translate}}</th> - <th scope="col">{{'BULK.LIST.CREATED' | translate}}</th> - <th scope="col">{{'BULK.LIST.DOMAIN_ID' | translate}}</th> - <th scope="col">{{'BULK.LIST.DOMAIN_NAME' | translate}}</th> - <th scope="col">{{'BULK.LIST.DOMAIN_CODENAME' | translate}}</th> - <th style="width: 5%" scope="col"></th> - </tr> - <ng-template ngFor let-response [ngForOf]="bulk.entries" let-i="index"> - <tr *ngIf="response.type === 'DOMAIN'" class="table-row"> - <td>{{'BULK.STATE.' + response.state | translate}}</td> - <td>{{response.created}}</td> - <td>{{getDomainId(response)}}</td> - <td>{{getDomainName(response)}}</td> - <td>{{getDomainCodeName(response)}}</td> - <td style="width: 5%" class="text-right" *ngIf="bulk.state !== 'REMOVED'"> - <i *ngIf="response.type === 'DOMAIN'" class="pi pi-search" style="font-size: 1.8rem; cursor: pointer" [routerLink]="['/admin/domains/view/', response?.details['domainId']]"></i> - <i *ngIf="response.type === 'USER'" class="pi pi-search" style="font-size: 1.8rem; cursor: pointer" [routerLink]="['/admin/users/view', response?.details['userId']]"></i> - <!-- <span class="dropdown"> - <a style="display: inline-block" class="dropdown-toggle " aria-expanded="false" aria-haspopup="true" - data-toggle="dropdown" href="#" role="button"> - <em class="fas fa-cog icon-black icon-bigger"></em> - </a> - <ul class="dropdown-menu pull-right-drop"> - <li *ngIf="response.type === 'DOMAIN'"> - <a [routerLink]="['/admin/domains/view/', response?.details['domainId']]">{{ 'BULK.LIST.MOVE_DOMAIN' | translate }}</a> - </li> - <li *ngIf="response.type === 'USER'"> - <a [routerLink]="['/admin/users/view', response?.details['userId']]">{{ 'BULK.LIST.MOVE_USER' | translate }}</a> - </li> - </ul> - </span> --> - </td> + <div class="panel-body"> + <table class="table table-hover table-condensed" aria-describedby="Domains in Group table"> + <thead> + <tr> + <th scope="col">{{'BULK.LIST.STATE' | translate}}</th> + <th scope="col">{{'BULK.LIST.CREATED' | translate}}</th> + <th scope="col">{{'BULK.LIST.DOMAIN_ID' | translate}}</th> + <th scope="col">{{'BULK.LIST.DOMAIN_NAME' | translate}}</th> + <th scope="col">{{'BULK.LIST.DOMAIN_CODENAME' | translate}}</th> + <th style="width: 5%" scope="col"></th> </tr> - </ng-template> - </thead> - </table> + <ng-template ngFor let-response [ngForOf]="bulk.entries" let-i="index"> + <tr *ngIf="response.type === 'DOMAIN'" class="table-row"> + <td>{{'BULK.STATE.' + response.state | translate}}</td> + <td>{{response.created}}</td> + <td>{{getDomainId(response)}}</td> + <td>{{getDomainName(response)}}</td> + <td>{{getDomainCodeName(response)}}</td> + <td style="width: 5%" class="text-right" *ngIf="bulk.state !== 'REMOVED'"> + <i *ngIf="response.type === 'DOMAIN'" class="pi pi-search" style="font-size: 1.8rem; cursor: pointer" [routerLink]="['/admin/domains/view/', response?.details['domainId']]"></i> + <i *ngIf="response.type === 'USER'" class="pi pi-search" style="font-size: 1.8rem; cursor: pointer" [routerLink]="['/admin/users/view', response?.details['userId']]"></i> + <!-- <span class="dropdown"> + <a style="display: inline-block" class="dropdown-toggle " aria-expanded="false" aria-haspopup="true" + data-toggle="dropdown" href="#" role="button"> + <em class="fas fa-cog icon-black icon-bigger"></em> + </a> + <ul class="dropdown-menu pull-right-drop"> + <li *ngIf="response.type === 'DOMAIN'"> + <a [routerLink]="['/admin/domains/view/', response?.details['domainId']]">{{ 'BULK.LIST.MOVE_DOMAIN' | translate }}</a> + </li> + <li *ngIf="response.type === 'USER'"> + <a [routerLink]="['/admin/users/view', response?.details['userId']]">{{ 'BULK.LIST.MOVE_USER' | translate }}</a> + </li> + </ul> + </span> --> + </td> + </tr> + </ng-template> + </thead> + </table> + </div> </div> - </div> - <div class="panel panel-default" style="margin-top: 3rem"> - <div class="panel-heading"> + <div class="panel panel-default" style="margin-top: 3rem"> + <div class="panel-heading"> - <div style="display: flex; justify-content: start; align-items: center"> - <div> - {{'BULK.USER.DEPLOYMENTS' | translate }} + <div style="display: flex; justify-content: start; align-items: center"> + <div> + {{'BULK.USER.DEPLOYMENTS' | translate }} + </div> </div> </div> - </div> - <div class="panel-body"> - <table class="table table-hover table-condensed" aria-describedby="Domains in Group table"> - <thead> - <tr> - <th scope="col">{{'BULK.LIST.STATE' | translate}}</th> - <th scope="col">{{'BULK.LIST.CREATED' | translate}}</th> - <th scope="col">{{'BULK.LIST.USER_ID' | translate}}</th> - <th scope="col">{{'BULK.LIST.USER_NAME' | translate}}</th> - <th scope="col">{{'BULK.LIST.EMAIL' | translate}}</th> - <th style="width: 5%" scope="col"></th> - </tr> - <ng-template ngFor let-response [ngForOf]="bulk.entries" let-i="index"> - <tr *ngIf="response.type === 'USER'" class="table-row"> - <td>{{'BULK.STATE.' + response.state | translate}}</td> - <td>{{response.created}}</td> - <td>{{getUserId(response)}}</td> - <td>{{getUsername(response)}}</td> - <td>{{getEmail(response)}}</td> - <td style="width: 5%" class="text-right"> - <i *ngIf="response.type === 'DOMAIN'" class="pi pi-search" style="font-size: 1.8rem; cursor: pointer" [routerLink]="['/admin/domains/view/', response?.details['domainId']]"></i> - <i *ngIf="response.type === 'USER'" class="pi pi-search" style="font-size: 1.8rem; cursor: pointer" [routerLink]="['/admin/users/view', response?.details['userId']]"></i> - - <!-- <span class="dropdown"> - <a style="display: inline-block" class="dropdown-toggle " aria-expanded="false" aria-haspopup="true" - data-toggle="dropdown" href="#" role="button"> - <em class="fas fa-cog icon-black icon-bigger"></em> - </a> - <ul class="dropdown-menu pull-right-drop"> - <li *ngIf="response.type === 'DOMAIN'"> - <a [routerLink]="['/admin/domains/view/', response?.details['domainId']]">{{ 'BULK.LIST.MOVE_DOMAIN' | translate }}</a> - </li> - <li *ngIf="response.type === 'USER'"> - <a [routerLink]="['/admin/users/view', response?.details['userId']]">{{ 'BULK.LIST.MOVE_USER' | translate }}</a> - </li> - </ul> - </span> --> - </td> + <div class="panel-body"> + <table class="table table-hover table-condensed" aria-describedby="Domains in Group table"> + <thead> + <tr> + <th scope="col">{{'BULK.LIST.STATE' | translate}}</th> + <th scope="col">{{'BULK.LIST.CREATED' | translate}}</th> + <th scope="col">{{'BULK.LIST.USER_ID' | translate}}</th> + <th scope="col">{{'BULK.LIST.USER_NAME' | translate}}</th> + <th scope="col">{{'BULK.LIST.EMAIL' | translate}}</th> + <th style="width: 5%" scope="col"></th> </tr> - </ng-template> - </thead> - </table> + <ng-template ngFor let-response [ngForOf]="bulk.entries" let-i="index"> + <tr *ngIf="response.type === 'USER'" class="table-row"> + <td>{{'BULK.STATE.' + response.state | translate}}</td> + <td>{{response.created}}</td> + <td>{{getUserId(response)}}</td> + <td>{{getUsername(response)}}</td> + <td>{{getEmail(response)}}</td> + <td style="width: 5%" class="text-right"> + <i *ngIf="response.type === 'DOMAIN'" class="pi pi-search" style="font-size: 1.8rem; cursor: pointer" [routerLink]="['/admin/domains/view/', response?.details['domainId']]"></i> + <i *ngIf="response.type === 'USER'" class="pi pi-search" style="font-size: 1.8rem; cursor: pointer" [routerLink]="['/admin/users/view', response?.details['userId']]"></i> - </div> - </div> + <!-- <span class="dropdown"> + <a style="display: inline-block" class="dropdown-toggle " aria-expanded="false" aria-haspopup="true" + data-toggle="dropdown" href="#" role="button"> + <em class="fas fa-cog icon-black icon-bigger"></em> + </a> + <ul class="dropdown-menu pull-right-drop"> + <li *ngIf="response.type === 'DOMAIN'"> + <a [routerLink]="['/admin/domains/view/', response?.details['domainId']]">{{ 'BULK.LIST.MOVE_DOMAIN' | translate }}</a> + </li> + <li *ngIf="response.type === 'USER'"> + <a [routerLink]="['/admin/users/view', response?.details['userId']]">{{ 'BULK.LIST.MOVE_USER' | translate }}</a> + </li> + </ul> + </span> --> + </td> + </tr> + </ng-template> + </thead> + </table> + </div> + </div> - </div> - <div *ngIf="bulk && bulkType === 'APPLICATION' "> - <div class="flex justify-content-between"> - <h3>{{'BULK.APP.VIEW_HEADER' | translate}}</h3> - <img alt="App logo" style="width: 50px" - [src]="(appImagesService.getAppLogoUrl(bulk.details['appId']) | secure) || 'assets/images/app-logo-example.png'"/> </div> - - <div class="" style="padding-bottom: 5rem; margin-top: 3rem"> - <label for="id" class="col-sm-2 control-label text-right mt-2">{{ 'BULK.LIST.ID' | translate }}</label> - <div class="col-sm-10"> - <input type="text" class="form-control" id="id" name="id" [disabled]="true" - [(ngModel)]="bulk.id" #name="ngModel"> + + <div *ngIf="bulk && bulkType === 'APPLICATION' "> + <div class="flex justify-content-between"> + <h3>{{'BULK.APP.VIEW_HEADER' | translate}}</h3> + <img alt="App logo" style="width: 50px" + [src]="(appImagesService.getAppLogoUrl(bulk.details['appId']) | secure) || 'assets/images/app-logo-example.png'"/> </div> - </div> - <div class="" style="padding-bottom: 5rem;"> - <label for="id" class="col-sm-2 control-label text-right mt-2">{{ 'BULK.LIST.APP_NAME' | translate }}</label> - <div class="col-sm-10"> - <input type="text" class="form-control" id="id" name="id" [disabled]="true" - [(ngModel)]="bulk.details['appName']" #name="ngModel"> + <div class="" style="padding-bottom: 5rem; margin-top: 3rem"> + <label for="id" class="col-sm-2 control-label text-right mt-2">{{ 'BULK.LIST.ID' | translate }}</label> + <div class="col-sm-10"> + <input type="text" class="form-control" id="id" name="id" [disabled]="true" + [(ngModel)]="bulk.id" #name="ngModel"> + </div> </div> - - </div> - <div class="" style="padding-bottom: 5rem"> - <label for="creator" - class="col-sm-2 control-label text-right mt-2">{{ 'BULK.LIST.CREATOR' | translate }}</label> - <div class="col-sm-10"> - <input type="text" class="form-control" id="creator" name="creator" [disabled]="true" - placeholder="{{bulk.creator.username}} ({{bulk.creator.firstname}} {{bulk.creator.lastname}})"> + <div class="" style="padding-bottom: 5rem;"> + <label for="id" class="col-sm-2 control-label text-right mt-2">{{ 'BULK.LIST.APP_NAME' | translate }}</label> + <div class="col-sm-10"> + <input type="text" class="form-control" id="id" name="id" [disabled]="true" + [(ngModel)]="bulk.details['appName']" #name="ngModel"> + </div> + </div> - </div> - <div class="" style="padding-bottom: 5rem"> - <label for="date" - class="col-sm-2 control-label text-right mt-2">{{ 'BULK.LIST.CREATION_DATE' | translate }}</label> - <div class="col-sm-10"> - <input type="text" class="form-control" id="date" name="date" [disabled]="true" - placeholder="{{bulk.creationDate | date: 'dd-MM-yyyy HH:mm'}}"> + <div class="" style="padding-bottom: 5rem"> + <label for="creator" + class="col-sm-2 control-label text-right mt-2">{{ 'BULK.LIST.CREATOR' | translate }}</label> + <div class="col-sm-10"> + <input type="text" class="form-control" id="creator" name="creator" [disabled]="true" + placeholder="{{bulk.creator.username}} ({{bulk.creator.firstname}} {{bulk.creator.lastname}})"> + </div> </div> - </div> - <div class="" style="padding-bottom: 5rem"> - <label for="state" - class="col-sm-2 control-label text-right mt-2">{{ 'BULK.LIST.STATE' | translate }}</label> - <div class="col-sm-10"> - <input type="text" class="form-control" id="state" name="state" [disabled]="true" - placeholder="{{'BULK.STATE.' + bulk.state | translate}}"> + <div class="" style="padding-bottom: 5rem"> + <label for="date" + class="col-sm-2 control-label text-right mt-2">{{ 'BULK.LIST.CREATION_DATE' | translate }}</label> + <div class="col-sm-10"> + <input type="text" class="form-control" id="date" name="date" [disabled]="true" + placeholder="{{bulk.creationDate | date: 'dd-MM-yyyy HH:mm'}}"> + </div> </div> - </div> - <div class="" style="padding-bottom: 5rem"> - <label for="completionDate" - class="col-sm-2 control-label text-right mt-2">{{ 'BULK.LIST.COMPLETION_DATE' | translate }}</label> - <div class="col-sm-10"> - <input type="text" class="form-control" id="completionDate" name="completionDate" [disabled]="true" - placeholder="{{completionDate}}"> + <div class="" style="padding-bottom: 5rem"> + <label for="state" + class="col-sm-2 control-label text-right mt-2">{{ 'BULK.LIST.STATE' | translate }}</label> + <div class="col-sm-10"> + <input type="text" class="form-control" id="state" name="state" [disabled]="true" + placeholder="{{'BULK.STATE.' + bulk.state | translate}}"> + </div> + </div> + + <div class="" style="padding-bottom: 5rem"> + <label for="completionDate" + class="col-sm-2 control-label text-right mt-2">{{ 'BULK.LIST.COMPLETION_DATE' | translate }}</label> + <div class="col-sm-10"> + <input type="text" class="form-control" id="completionDate" name="completionDate" [disabled]="true" + placeholder="{{completionDate}}"> + </div> </div> - </div> - - <div *ngIf="bulk.state !== 'REMOVED'" class="flex justify-content-end" style="padding-right: 1.5rem"> - <button class="btn btn-primary mr-2" (click)="refreshStates()">{{'BULK.APP.REFRESH' | translate}}</button> - <button *ngIf="bulk.state !== 'FAILED'" class="btn btn-primary" (click)="getAppBulkDetails(this.bulkId)">{{'BULK.APP.DOWNLOAD_CSV' | translate}}</button> - </div> - <div class="mt-4"> - <p-progressBar [mode]="progressBarMode"[value]="progressBarValue" id="progressBarr" [style]="{ height: '18px' }" [styleClass]="jobDone ? 'job-done-bar' : 'normal-bar'"> - <ng-template pTemplate="content" let-value> - <span>{{queueDetails?.jobDone}} <span *ngIf="queueDetails?.jobDone !== bulk.entries.length">(+{{queueDetails?.jobInProcess}})</span> / {{bulk.entries.length}}</span> + <div *ngIf="bulk.state !== 'REMOVED'" class="flex justify-content-end" style="padding-right: 1.5rem"> + <button class="btn btn-primary mr-2" (click)="refreshStates()">{{'BULK.APP.REFRESH' | translate}}</button> + + <button *ngIf="bulk.state !== 'FAILED'" class="btn btn-primary" (click)="getAppBulkDetails(this.bulkId)">{{'BULK.APP.DOWNLOAD_CSV' | translate}}</button> + </div> + <div class="mt-4"> + <p-progressBar [mode]="progressBarMode"[value]="progressBarValue" id="progressBarr" [style]="{ height: '18px' }" [styleClass]="jobDone ? 'job-done-bar' : 'normal-bar'"> + <ng-template pTemplate="content" let-value> + <span>{{queueDetails?.jobDone}} <span *ngIf="queueDetails?.jobDone !== bulk.entries.length">(+{{queueDetails?.jobInProcess}})</span> / {{bulk.entries.length}}</span> </ng-template> - </p-progressBar> + </p-progressBar> - </div> - <div class="panel panel-default" style="margin-top: 1rem"> - <div class="panel-heading"> + </div> + <div class="panel panel-default" style="margin-top: 1rem"> + <div class="panel-heading"> - <div style="display: flex; justify-content: start; align-items: center"> - <div> - {{ 'BULK.APP.DEPLOYMENTS' | translate }} + <div style="display: flex; justify-content: start; align-items: center"> + <div> + {{ 'BULK.APP.DEPLOYMENTS' | translate }} + </div> </div> </div> - </div> - <div class="panel-body"> - - - <table class="table table-hover table-condensed" aria-describedby="Domains in Group table"> - <thead> - <tr #column> - <th scope="col">{{'BULK.LIST.STATE' | translate}}</th> - <th scope="col">{{'BULK.APP.INSTANCE_ID' | translate}}</th> - <th scope="col">{{'BULK.APP.INSTANCE_NAME' | translate}}</th> - <th scope="col">{{'BULK.APP.DOMAIN' | translate}}</th> - <th style="width: 5%" scope="col"></th> - </tr> - <ng-template ngFor let-response [ngForOf]="bulk.entries" let-i="index"> - <tr *ngIf="response.type === 'APPLICATION'" class="table-row"> - <td>{{'BULK.STATE.' + response.state | translate}} <em - *ngIf="response.state == 'PROCESSING'" - class="pi pi-spin pi-spinner ml-1" - style="font-size: 1.4rem"></em> - <em *ngIf="response.state == 'FAILED' && response?.details['appInstanceId'] === undefined" class="pi pi-info-circle" - style="font-size: 1.4rem" - pTooltip="{{response?.details['errorMessage']}}" - tooltipStyleClass="p-tooltip-width " [fitContent]="false"></em></td> - <td>{{getAppInstanceId(response)}}</td> - <td>{{getAppInstanceName(response)}}</td> - <td>{{getDomainCodeName(response)}}</td> - <td style="width: 5%" class="text-right" > - <i *ngIf="response?.details['appInstanceId'] !== undefined" class="pi pi-search" style="font-size: 1.8rem; cursor: pointer" [routerLink]="['/instances/', response?.details['appInstanceId']]"></i> - <!-- <span *ngIf="response?.details['appInstanceId'] !== undefined" class="dropdown"> - <a style="display: inline-block" class="dropdown-toggle " aria-expanded="false" aria-haspopup="true" - data-toggle="dropdown" href="#" role="button"> - <em class="fas fa-cog icon-black icon-bigger"></em> - </a> - <ul class="dropdown-menu pull-right-drop"> - <li *ngIf="response.type === 'APPLICATION' && response?.details['appInstanceId'] !== undefined"> - <a [routerLink]="['/instances/', response?.details['appInstanceId']]">{{ 'BULK.LIST.MOVE_APP' | translate }}</a> - </li> - <li *ngIf="response.type === 'APPLICATION' && response.state !== 'COMPLETED'"> - <a>{{ 'BULK.APP.CHECK_STATE' | translate }}</a> - </li> - </ul> - </span> --> - </td> + <div class="panel-body"> + + + <table class="table table-hover table-condensed" aria-describedby="Domains in Group table"> + <thead> + <tr #column> + <th scope="col">{{'BULK.LIST.STATE' | translate}}</th> + <th scope="col">{{'BULK.APP.INSTANCE_ID' | translate}}</th> + <th scope="col">{{'BULK.APP.INSTANCE_NAME' | translate}}</th> + <th scope="col">{{'BULK.APP.DOMAIN' | translate}}</th> + <th style="width: 5%" scope="col"></th> </tr> - </ng-template> - </thead> - </table> + <ng-template ngFor let-response [ngForOf]="bulk.entries" let-i="index"> + <tr *ngIf="response.type === 'APPLICATION'" class="table-row"> + <td>{{'BULK.STATE.' + response.state | translate}} <em + *ngIf="response.state == 'PROCESSING'" + class="pi pi-spin pi-spinner ml-1" + style="font-size: 1.4rem"></em> + <em *ngIf="response.state == 'FAILED' && response?.details['appInstanceId'] === undefined" class="pi pi-info-circle" + style="font-size: 1.4rem" + pTooltip="{{response?.details['errorMessage']}}" + tooltipStyleClass="p-tooltip-width " [fitContent]="false"></em></td> + <td>{{getAppInstanceId(response)}}</td> + <td>{{getAppInstanceName(response)}}</td> + <td>{{getDomainCodeName(response)}}</td> + <td style="width: 5%" class="text-right" > + <i *ngIf="response?.details['appInstanceId'] !== undefined" class="pi pi-search" style="font-size: 1.8rem; cursor: pointer" [routerLink]="['/instances/', response?.details['appInstanceId']]"></i> + <!-- <span *ngIf="response?.details['appInstanceId'] !== undefined" class="dropdown"> + <a style="display: inline-block" class="dropdown-toggle " aria-expanded="false" aria-haspopup="true" + data-toggle="dropdown" href="#" role="button"> + <em class="fas fa-cog icon-black icon-bigger"></em> + </a> + <ul class="dropdown-menu pull-right-drop"> + <li *ngIf="response.type === 'APPLICATION' && response?.details['appInstanceId'] !== undefined"> + <a [routerLink]="['/instances/', response?.details['appInstanceId']]">{{ 'BULK.LIST.MOVE_APP' | translate }}</a> + </li> + <li *ngIf="response.type === 'APPLICATION' && response.state !== 'COMPLETED'"> + <a>{{ 'BULK.APP.CHECK_STATE' | translate }}</a> + </li> + </ul> + </span> --> + </td> + </tr> + </ng-template> + </thead> + </table> + </div> </div> - </div> + </div> </div> + </div> diff --git a/src/app/appmarket/bulkDeployment/domainDeployment/domainupload/domainupload.component.css b/src/app/appmarket/bulkDeployment/domainDeployment/domainupload/domainupload.component.css new file mode 100644 index 0000000000000000000000000000000000000000..f2c387a9db7ec1e2c9aeefc41a92b4d6336c9fdb --- /dev/null +++ b/src/app/appmarket/bulkDeployment/domainDeployment/domainupload/domainupload.component.css @@ -0,0 +1,38 @@ +:host ::ng-deep input[type=file]{ + display:none; +} +:host ::ng-deep .p-button{ + width: unset; + margin-right: 5px; + background: var(--primary-button-color); + color: var(--button-text-color); +} +:host ::ng-deep .p-button:hover{ + background: var(--primary-button-hover); + border:none; +} +:host ::ng-deep .p-button-label{ + font-weight: normal; +} +:host ::ng-deep .p-fileupload .p-fileupload-buttonbar{ + border: none; + background: transparent; + margin-bottom: 10px; + padding: 0; +} +:host ::ng-deep .p-fileupload .p-fileupload-content{ + border: none; + padding: 0; + border-radius: 3px; + +} +:host ::ng-deep .p-fileupload-content .p-progressbar{ + display: none; +} +textarea{ + border-color: #ccc; +} +:host ::ng-deep .p-inputtext:enabled:focus{ + box-shadow: none; + border-color: var(--l-text-color); +} diff --git a/src/app/appmarket/bulkDeployment/domainDeployment/domainupload/domainupload.component.html b/src/app/appmarket/bulkDeployment/domainDeployment/domainupload/domainupload.component.html index 71d0eff138b7dc6da3d9f249f33ce97de8f63dc2..3a56ddc0d02e4864d2cd351ce33f7d06bdbbb772 100644 --- a/src/app/appmarket/bulkDeployment/domainDeployment/domainupload/domainupload.component.html +++ b/src/app/appmarket/bulkDeployment/domainDeployment/domainupload/domainupload.component.html @@ -1,28 +1,31 @@ -<div style="width: 80%; margin: auto; margin-top: 4rem;"> - <div style="margin-bottom: 2rem"> - <h2>{{'BULK.DOMAIN.NAVIGATION' | translate}}</h2> - <p class="mt-4">{{'BULK.DOMAIN.UPLOAD' | translate}}</p> - </div> - <p-fileUpload name="file[]" customUpload="true" (uploadHandler)="myUploader($event)" accept=".csv" - multiple="false" uploadLabel="{{'BULK.BUTTON' | translate}}"></p-fileUpload> +<div style="width: 100%; margin: auto; margin-top: 4rem;"> + <h3>{{'BULK.DOMAIN.NAVIGATION' | translate}}</h3> + <div class="background-section"> + <div style="margin-bottom: 2rem"> - <div style="margin-top: 1.5rem; display: flex; justify-content: center; flex-direction: column"> - <div style="margin-top: 1.5rem; margin-bottom: 1rem"> - <p>{{'BULK.APP.UPLOAD_TEXT' | translate}}</p> + <p class="">{{'BULK.DOMAIN.UPLOAD' | translate}}</p> </div> - <textarea pInputTextarea [(ngModel)]="csvText" rows="10" cols="127" (keyup)="changeDetector = true"></textarea> - </div> + <p-fileUpload name="file[]" (uploadHandler)="myUploader($event)" accept=".csv" + multiple="false" uploadLabel="{{'BULK.BUTTON' | translate}}" ></p-fileUpload> - <div *ngIf="errorMessage !== ''" style="margin-top: 1rem; display: flex; justify-content: start; color: indianred"> - <p>{{errorMessage}}</p> - </div> + <div style="margin-top: 1.5rem; display: flex; justify-content: center; flex-direction: column"> + <div style="margin-top: 1.5rem; margin-bottom: 1rem"> + <p>{{'BULK.APP.UPLOAD_TEXT' | translate}}</p> + </div> + <textarea pInputTextarea [(ngModel)]="csvText" rows="10" cols="127" [autoResize]="true" (keyup)="changeDetector = true"></textarea> + </div> - <div style="margin-top: 2rem; display: flex; justify-content: center;" > + <div *ngIf="errorMessage !== ''" style="margin-top: 1rem; display: flex; justify-content: start; color: indianred"> + <p>{{errorMessage}}</p> + </div> + + <div *ngIf="showProgressBar" style="margin-top: 20px;"> + <p>{{ 'BULK.DOMAIN.DEPLOYMENT_IN_PROGRESS' | translate }}</p> + <p-progressBar mode="indeterminate" [style]="{height : '8px'}"></p-progressBar> + </div> + </div> + <div style="margin-top: 2rem; display: flex; justify-content: end;" > <button class="btn btn-primary" (click)="uploadText(); this.errorMessage=''" [disabled]="csvText === '' || !changeDetector"> {{'BULK.UPLOAD_BUTTON' | translate}}</button> </div> - <div *ngIf="showProgressBar" style="margin-top: 20px;"> - <p>{{ 'BULK.DOMAIN.DEPLOYMENT_IN_PROGRESS' | translate }}</p> - <p-progressBar mode="indeterminate" [style]="{height : '8px'}"></p-progressBar> - </div> </div> diff --git a/src/app/appmarket/bulkDeployment/domainDeployment/domainupload/domainupload.component.ts b/src/app/appmarket/bulkDeployment/domainDeployment/domainupload/domainupload.component.ts index 7919ba6ecd003708201643d666a6c445a99516ba..cc11875e86b51b7446c66f0834dd38d6c6c82893 100644 --- a/src/app/appmarket/bulkDeployment/domainDeployment/domainupload/domainupload.component.ts +++ b/src/app/appmarket/bulkDeployment/domainDeployment/domainupload/domainupload.component.ts @@ -7,7 +7,7 @@ import {DomainService} from '../../../../service'; @Component({ selector: 'app-domainupload', templateUrl: './domainupload.component.html', - styleUrls: [] + styleUrls: ['./domainupload.component.css'] }) export class DomainuploadComponent { diff --git a/src/app/appmarket/domains/domain-annotations/domain-annotations.component.html b/src/app/appmarket/domains/domain-annotations/domain-annotations.component.html index b7a1c8994df0487f51786029da9a476fb7173f56..45ab00bcd295953589a3e5c2ddc03f9bc7d2b0aa 100644 --- a/src/app/appmarket/domains/domain-annotations/domain-annotations.component.html +++ b/src/app/appmarket/domains/domain-annotations/domain-annotations.component.html @@ -1,7 +1,7 @@ -<div class="col-sm-12 col-sm-offset-1 col-sm-10 col-md-offset-1 col-md-10"> +<div class=""> <h3> {{'DOMAINS.ANNOTATIONS.HEADER' | translate}}</h3> <p>{{'DOMAINS.ANNOTATIONS.SUBHEADER' | translate}} </p> - <div class="flex flex-grow-1 mt-6 col-sm-12" style="width: 100% !important;"> + <div class="" style="width: 100% !important;"> <app-domain-namespace-annotations [annotationRead]="annotations" [globalSettings]="true" (annotations)="handleAnnotationsUpdate($event)" (trigerDelete)="handleDelete($event)" class="flex flex-grow-1"></app-domain-namespace-annotations> </div> diff --git a/src/app/appmarket/domains/domain-group-view/domain-group-view.component.html b/src/app/appmarket/domains/domain-group-view/domain-group-view.component.html index 884fd53dc9108fdcc1b1e2a57db7b608a576f303..56d68346cc403a477bb1ce1a6a05d258bbaa0e54 100644 --- a/src/app/appmarket/domains/domain-group-view/domain-group-view.component.html +++ b/src/app/appmarket/domains/domain-group-view/domain-group-view.component.html @@ -1,40 +1,49 @@ -<div class="col-sm-12 col-sm-offset-1 col-sm-10 col-md-offset-1 col-md-10"> - <h3>{{ 'DOMAINS.LIST.GROUP' | translate }}</h3> +<div class=""> + <h4 class="header">{{ 'DOMAINS.LIST.GROUP' | translate }}</h4> <form *ngIf="domainGroup" (submit)="submit(false)" class="form-horizontal" #domainForm="ngForm"> - <div class="form-group"> - <label for="name" class="col-sm-2 control-label">{{ 'DOMAIN_DETAILS.NAME' | translate }}</label> - <div class="col-sm-10"> - <input type="text" class="form-control" id="name" name="name" - [(ngModel)]="domainGroup.name" #name="ngModel" required> - <div *ngIf="name.invalid && (name.dirty || name.touched)" - class="alert alert-danger"> - <div *ngIf="name.errors.required">{{ 'DOMAIN_DETAILS.NAME_IS_REQUIRED_MESSAGE' | translate }}</div> + <div class="background-section"> + <div class="form-group"> + <label for="name" class="col-sm-2 control-label">{{ 'DOMAIN_DETAILS.NAME' | translate }}</label> + <div class="col-sm-10"> + <input type="text" class="form-control" id="name" name="name" + [(ngModel)]="domainGroup.name" #name="ngModel" required> + <div *ngIf="name.invalid && (name.dirty || name.touched)" + class="alert alert-danger"> + <div *ngIf="name.errors.required">{{ 'DOMAIN_DETAILS.NAME_IS_REQUIRED_MESSAGE' | translate }}</div> + </div> </div> </div> - </div> - <div class="form-group" *ngIf="!isInMode(ComponentMode.EDIT)"> - <label for="codename" class="col-sm-2 control-label">{{ 'DOMAIN_DETAILS.CODE_NAME' | translate }}</label> - <div class="col-sm-10"> - <input type="text" class="form-control" id="codename" - name="codename" pattern="[a-zA-Z0-9-]*" - [(ngModel)]="domainGroup.codename" #codename="ngModel" minlength="2" maxlength="20" required> - <div *ngIf="codename.invalid && (codename.dirty || codename.touched)" - class="alert alert-danger"> + <div class="form-group" *ngIf="!isInMode(ComponentMode.EDIT)"> + <label for="codename" class="col-sm-2 control-label">{{ 'DOMAIN_DETAILS.CODE_NAME' | translate }}</label> + <div class="col-sm-10"> + <input type="text" class="form-control" id="codename" + name="codename" pattern="[a-zA-Z0-9-]*" + [(ngModel)]="domainGroup.codename" #codename="ngModel" minlength="2" maxlength="20" required> + <div *ngIf="codename.invalid && (codename.dirty || codename.touched)" + class="alert alert-danger"> - <div *ngIf="codename.errors.required">{{ 'DOMAIN_DETAILS.CODE_NAME_IS_REQUIRED_MESSAGE' | translate }}</div> - <div *ngIf="codename.errors.pattern">{{ 'DOMAIN_DETAILS.CODE_NAME_PATTERN_MESSAGE_1' | translate }}</div> - <div *ngIf="codename.errors.minlength || codename.errors.maxlength">{{ 'DOMAIN_DETAILS.CODE_NAME_PATTERN_MESSAGE_2' | translate }}</div> + <div *ngIf="codename.errors.required">{{ 'DOMAIN_DETAILS.CODE_NAME_IS_REQUIRED_MESSAGE' | translate }}</div> + <div *ngIf="codename.errors.pattern">{{ 'DOMAIN_DETAILS.CODE_NAME_PATTERN_MESSAGE_1' | translate }}</div> + <div *ngIf="codename.errors.minlength || codename.errors.maxlength">{{ 'DOMAIN_DETAILS.CODE_NAME_PATTERN_MESSAGE_2' | translate }}</div> + </div> </div> </div> </div> - <div *ngIf="!this.addingMode" class="panel panel-default" style="margin-top: 3rem"> + + <div *ngIf="!this.addingMode" class="background-section" style="margin-top: 3rem"> <div class="panel-heading"> - <div style="display: flex; justify-content: start; align-items: center"> - <div> + <div style="display: flex; justify-content:space-between"> + <h4 style="font-size:15px; font-weight: bold"> {{ 'DOMAINS.GROUP.ACCESS_USER' | translate }} + </h4> + <div *roles="['ROLE_SYSTEM_ADMIN']" style="display: flex; justify-content: end"> + <button type="button" class="btn btn-text" (click)="showModalUser()">{{'DOMAINS.GROUP.ADD_USERS' | translate}}</button> + </div> + <div *roles="['ROLE_VL_MANAGER']" style="display: flex; justify-content: end"> + <button type="button" class="btn btn-warning" (click)="removeMyAccess()">{{'DOMAINS.GROUP.DELETE_MYSELF' | translate}}</button> </div> </div> </div> @@ -211,8 +220,8 @@ </table> </div> <div class="nmaas-modal-footer"> - <button type="button" class="btn btn-primary" (click)="closeModal()" [disabled]="false">{{'DOMAINS.LIST.ADD' | translate}}</button> <button type="button" class="btn btn-secondary" (click)="modal.hide()">{{'APP_CHANGE_STATE_MODAL.CANCEL_BUTTON' | translate}}</button> + <button type="button" class="btn btn-primary" (click)="closeModal()" [disabled]="false">{{'DOMAINS.LIST.ADD' | translate}}</button> </div> </nmaas-modal> @@ -258,7 +267,7 @@ </table> </div> <div class="nmaas-modal-footer"> - <button type="button" class="btn btn-primary" (click)="saveUsers()" [disabled]="false">{{'DOMAINS.GROUP.ADD_USERS' | translate}}</button> <button type="button" class="btn btn-secondary" (click)="closeModalUserAccess()">{{'APP_CHANGE_STATE_MODAL.CANCEL_BUTTON' | translate}}</button> + <button type="button" class="btn btn-primary" (click)="saveUsers()" [disabled]="false">{{'DOMAINS.GROUP.ADD_USERS' | translate}}</button> </div> </nmaas-modal> diff --git a/src/app/appmarket/domains/domain-groups/domain-groups.component.css b/src/app/appmarket/domains/domain-groups/domain-groups.component.css index 7cc4c1ecabca30691fff692308eb4bbbd24dd7fe..e7436baab1c633ac509d2387be02c6255cfeea48 100644 --- a/src/app/appmarket/domains/domain-groups/domain-groups.component.css +++ b/src/app/appmarket/domains/domain-groups/domain-groups.component.css @@ -21,3 +21,64 @@ margin-left: 5px; margin-right: 5px; } + +:host ::ng-deep .p-datatable .p-datatable-thead > tr > th{ + border: 1px solid #E0E2E5; + background:transparent; + border-width: 0 0 1px 0; +} +:host ::ng-deep .p-datatable .p-datatable-tbody > tr > td { + text-align: left; + border: 1px solid #E0E2E5; + border-width: 0 0 1px 0; + padding: 1rem 1rem; +} +:host ::ng-deep .p-datatable .p-paginator-bottom{ + height: 40px; + background: transparent; + border: none; + margin-top:10px; +} +:host ::ng-deep .p-datatable .p-datatable-tbody > tr{ + background: transparent; +} + +:host ::ng-deep .p-paginator .p-paginator-pages .p-paginator-page{ + transition: unset; + border-radius: 50%; + min-width:3.5rem; + height:3.5rem; + margin:0 5px; + font-size: 14px; +} + +:host ::ng-deep .p-paginator-element{ + border-radius:50%; + margin:0 5px; + min-width:3.5rem; + height:3.5rem; + font-size: 14px; +} +:host ::ng-deep .p-paginator .p-dropdown{ + height:3rem; +} +:host ::ng-deep .p-paginator-icon{ + height: 1.5rem; + width: 1.5rem; +} +:host ::ng-deep .p-paginator .p-dropdown .p-dropdown-label{ + padding-right: 10px; +} + +label{ + padding-left:5px; + display: unset; + margin-bottom: 0; + font-weight: unset; +} +:host ::ng-deep .p-paginator .p-paginator-pages .p-paginator-page.p-highlight{ + background: var(--user-button-background-hover); +} +:host ::ng-deep .p-datatable>.p-datatable-wrapper { + overflow: visible; +} diff --git a/src/app/appmarket/domains/domain-groups/domain-groups.component.html b/src/app/appmarket/domains/domain-groups/domain-groups.component.html index 00e87a76702570d97b6ef962c5a6a0b83893f8b1..c3834f6720d71b648f8871e43118615242b9decd 100644 --- a/src/app/appmarket/domains/domain-groups/domain-groups.component.html +++ b/src/app/appmarket/domains/domain-groups/domain-groups.component.html @@ -1,50 +1,60 @@ -<div class="col-sm-12 col-sm-offset-1 col-sm 10 col-md-offset-1 col-md-10"> - <h3>{{'DOMAINS.LIST.GROUPS' | translate}}</h3> - <div class="flex space-between"> - <div class="flex"> - <a *roles="['ROLE_SYSTEM_ADMIN', 'ROLE_GROUP_MANAGER']" [routerLink]="['/admin/domains/groups/add']" class="btn btn-primary" - role="button">{{'DOMAINS.ADD_BUTTON' | translate}}</a> - </div> - <div class="flex"> +<div style="display: flex; align-items: center; margin-top:20px"> + <div style="margin-right:20px"> + <span class="p-input-icon-right" style="width: 100%"> + <i class="pi pi-search" style="font-size: 13px; top: 16px; margin-right: 5px;"></i> <input pInputText class="flex" name="search" id="search" placeholder="Search" type="text" style="height: 34px" [(ngModel)]="searchValue"> - </div> + </span> </div> - <table class="table table-hover table-condensed" aria-describedby="Apps management table" style="margin-top: 20px;"> - <thead> - <tr> - <th scope="col"></th> - <th scope="col">{{'APPS_MANAGEMENT.NAME' | translate}}</th> - <th scope="col">{{'DOMAINS.CODE_NAME' | translate}}</th> - <th scope="col">{{'DOMAINS.LIST.DOMAIN_NAME' | translate}}</th> - <th scope="col">{{'DOMAINS.LIST.DOMAIN_CODE_NAME' | translate}}</th> - </tr> - </thead> - - <tbody> - <ng-template ngFor let-domainGroup [ngForOf]="groups | searchDomainGroup: searchValue" let-i="index"> - <tr class="table-row" > - <td style="width: 5%" (click)="clickTableRow(i)" *ngIf="!domainsRowVisible[i]"><span class="glyphicon glyphicon-chevron-right"></span></td> - <td style="width: 5%" (click)="clickTableRow(i)" *ngIf="domainsRowVisible[i]"><span class="glyphicon glyphicon-chevron-down"></span></td> + <div class="flex" style="margin-right:20px"> + <button *roles="['ROLE_SYSTEM_ADMIN', 'ROLE_VL_MANAGER']" [routerLink]="['/admin/domains/groups/add']" class="btn btn-primary" + role="button">{{'DOMAINS.ADD_BUTTON' | translate}}</button> + </div> +</div> +<h4 class="header">{{'DOMAINS.LIST.GROUPS' | translate}}</h4> + +<div class="background-section"> + + <p-table [value]="groups | searchDomainGroup: searchValue" [rowHover]="true"> + <ng-template pTemplate="header"> + <tr> + <th></th> + <th>{{'APPS_MANAGEMENT.NAME' | translate}}</th> + <th>{{'DOMAINS.CODE_NAME' | translate}}</th> + <th>{{'DOMAINS.LIST.DOMAIN_NAME' | translate}}</th> + <th>{{'DOMAINS.LIST.DOMAIN_CODE_NAME' | translate}}</th> + <th></th> + </tr> + </ng-template> + + <ng-template pTemplate="body" let-domainGroup let-i="rowIndex"> + <tr class="table-row"> + <td style="width: 5%" (click)="clickTableRow(i)" *ngIf="!domainsRowVisible[i]"> + <span class="pi pi-chevron-right"></span> + </td> + <td style="width: 5%" (click)="clickTableRow(i)" *ngIf="domainsRowVisible[i]"> + <span class="pi pi-chevron-down"></span> + </td> <td style="width: 25%" (click)="clickTableRow(i)">{{domainGroup?.name}}</td> <td style="width: 20%" (click)="clickTableRow(i)">{{domainGroup?.codename}}</td> <td style="width: 15%" (click)="clickTableRow(i)"></td> <td style="width: 15%" (click)="clickTableRow(i)"></td> <td style="width: 20%" class="text-right"> <span class="dropdown"> - <a style="display: inline-block" class="dropdown-toggle " aria-expanded="false" aria-haspopup="true" data-toggle="dropdown" href="#" role="button"> - <em class="fas fa-cog icon-black icon-bigger"></em> - </a> - <ul class="dropdown-menu pull-right-drop"> - <li> - <a [routerLink]="['/admin/domains/groups/', domainGroup?.id]">{{ 'APPS_MANAGEMENT.EDIT_BUTTON' | translate }}</a> - </li> - <li> - <a (click)="deleteDomainGroup(domainGroup?.id)">{{ 'APP_INSTANCE.REMOVE_BUTTON' | translate }}</a> - </li> - </ul> - </span> + <a style="display: inline-block" class="dropdown-toggle" aria-expanded="false" aria-haspopup="true" data-toggle="dropdown" href="#" role="button"> + <em class="pi pi-cog" style="font-size: 1.8rem; color: var(--l-text-color)"></em> + </a> + <ul class="dropdown-menu pull-right-drop"> + <li> + <a [routerLink]="['/admin/domains/groups/', domainGroup?.id]">{{ 'APPS_MANAGEMENT.EDIT_BUTTON' | translate }}</a> + </li> + <li> + <a (click)="deleteDomainGroup(domainGroup?.id)">{{ 'APP_INSTANCE.REMOVE_BUTTON' | translate }}</a> + </li> + </ul> + </span> </td> </tr> + <ng-template ngFor let-domain [ngForOf]="domainGroup.domains"> <tr *ngIf="domainsRowVisible[i]" class="table-row pointer" [routerLink]="['/admin/domains/view/', domain.id]"> <td></td> @@ -52,12 +62,62 @@ <td></td> <td>{{domain.name}}</td> <td>{{domain.codename}}</td> - <td class="text-right"> - </td> + <td class="text-right"></td> </tr> </ng-template> </ng-template> - </tbody> - </table> + </p-table> </div> + +<!-- <table class="table table-hover table-condensed" aria-describedby="Apps management table" style="margin-top: 20px;">--> +<!-- <thead>--> +<!-- <tr>--> +<!-- <th scope="col"></th>--> +<!-- <th scope="col">{{'APPS_MANAGEMENT.NAME' | translate}}</th>--> +<!-- <th scope="col">{{'DOMAINS.CODE_NAME' | translate}}</th>--> +<!-- <th scope="col">{{'DOMAINS.LIST.DOMAIN_NAME' | translate}}</th>--> +<!-- <th scope="col">{{'DOMAINS.LIST.DOMAIN_CODE_NAME' | translate}}</th>--> +<!-- </tr>--> +<!-- </thead>--> + +<!-- <tbody>--> +<!-- <ng-template ngFor let-domainGroup [ngForOf]="groups | searchDomainGroup: searchValue" let-i="index">--> +<!-- <tr class="table-row" >--> +<!-- <td style="width: 5%" (click)="clickTableRow(i)" *ngIf="!domainsRowVisible[i]"><span class="glyphicon glyphicon-chevron-right"></span></td>--> +<!-- <td style="width: 5%" (click)="clickTableRow(i)" *ngIf="domainsRowVisible[i]"><span class="glyphicon glyphicon-chevron-down"></span></td>--> +<!-- <td style="width: 25%" (click)="clickTableRow(i)">{{domainGroup?.name}}</td>--> +<!-- <td style="width: 20%" (click)="clickTableRow(i)">{{domainGroup?.codename}}</td>--> +<!-- <td style="width: 15%" (click)="clickTableRow(i)"></td>--> +<!-- <td style="width: 15%" (click)="clickTableRow(i)"></td>--> +<!-- <td style="width: 20%" class="text-right">--> +<!-- <span class="dropdown">--> +<!-- <a style="display: inline-block" class="dropdown-toggle " aria-expanded="false" aria-haspopup="true" data-toggle="dropdown" href="#" role="button">--> +<!-- <em class="fas fa-cog icon-black icon-bigger"></em>--> +<!-- </a>--> +<!-- <ul class="dropdown-menu pull-right-drop">--> +<!-- <li>--> +<!-- <a [routerLink]="['/admin/domains/groups/', domainGroup?.id]">{{ 'APPS_MANAGEMENT.EDIT_BUTTON' | translate }}</a>--> +<!-- </li>--> +<!-- <li>--> +<!-- <a (click)="deleteDomainGroup(domainGroup?.id)">{{ 'APP_INSTANCE.REMOVE_BUTTON' | translate }}</a>--> +<!-- </li>--> +<!-- </ul>--> +<!-- </span>--> +<!-- </td>--> +<!-- </tr>--> +<!-- <ng-template ngFor let-domain [ngForOf]="domainGroup.domains">--> +<!-- <tr *ngIf="domainsRowVisible[i]" class="table-row pointer" [routerLink]="['/admin/domains/view/', domain.id]">--> +<!-- <td></td>--> +<!-- <td></td>--> +<!-- <td></td>--> +<!-- <td>{{domain.name}}</td>--> +<!-- <td>{{domain.codename}}</td>--> +<!-- <td class="text-right">--> +<!-- </td>--> +<!-- </tr>--> +<!-- </ng-template>--> +<!-- </ng-template>--> +<!-- </tbody>--> +<!-- </table>--> + diff --git a/src/app/appmarket/domains/domain/domain.component.css b/src/app/appmarket/domains/domain/domain.component.css index 2c587d11ef0fc19812ddea12ab21bb9029db6350..5bcb6ebc246def5aa232ad2b49d95b88155ff827 100644 --- a/src/app/appmarket/domains/domain/domain.component.css +++ b/src/app/appmarket/domains/domain/domain.component.css @@ -55,3 +55,8 @@ input.ng-dirty.ng-invalid { .no-padding-top { padding-top: 0!important; } +.form-control[disabled], fieldset[disabled] .form-control{ + background: transparent; + border:none; + box-shadow: none; +} diff --git a/src/app/appmarket/domains/domain/domain.component.html b/src/app/appmarket/domains/domain/domain.component.html index c9c32db1e1e83a214897894a8ebca1958fc6dacb..f77fd8bec8a4ad459efb19b5016650ce6a5e65a0 100644 --- a/src/app/appmarket/domains/domain/domain.component.html +++ b/src/app/appmarket/domains/domain/domain.component.html @@ -1,108 +1,111 @@ -<div class="col-sm-12 col-sm-offset-1 col-sm-10 col-md-offset-1 col-md-10"> +<div class=""> <h3>{{ 'DOMAIN_DETAILS.TITLE' | translate }}</h3> + <form *ngIf="domain" (submit)="submit()" class="form-horizontal" #domainForm="ngForm"> - <div class="form-group"> - <label for="name" class="col-sm-2 control-label">{{ 'DOMAIN_DETAILS.NAME' | translate }}</label> - <div class="col-sm-10"> - <input type="text" class="form-control" [disabled]="isInMode(ComponentMode.VIEW) || authService.hasRole('ROLE_OPERATOR')" id="name" name="name" - [(ngModel)]="domain.name" #name="ngModel" required> - <div *ngIf="name.invalid && (name.dirty || name.touched)" - class="alert alert-danger"> - <div *ngIf="name.errors.required">{{ 'DOMAIN_DETAILS.NAME_IS_REQUIRED_MESSAGE' | translate }}</div> + <div class="background-section"> + <div class="form-group"> + <label for="name" class="col-sm-2 control-label">{{ 'DOMAIN_DETAILS.NAME' | translate }}</label> + <div class="col-sm-10"> + <input type="text" class="form-control" [disabled]="isInMode(ComponentMode.VIEW) || authService.hasRole('ROLE_OPERATOR')" id="name" name="name" + [(ngModel)]="domain.name" #name="ngModel" required> + <div *ngIf="name.invalid && (name.dirty || name.touched)" + class="alert alert-danger"> + <div *ngIf="name.errors.required">{{ 'DOMAIN_DETAILS.NAME_IS_REQUIRED_MESSAGE' | translate }}</div> + </div> </div> </div> - </div> - <div class="form-group" *ngIf="!isInMode(ComponentMode.EDIT)"> - <label for="codename" class="col-sm-2 control-label">{{ 'DOMAIN_DETAILS.CODE_NAME' | translate }}</label> - <div class="col-sm-10"> - <input type="text" class="form-control" [disabled]="isInMode(ComponentMode.VIEW)" id="codename" - name="codename" pattern="[a-z0-9-]*" - [(ngModel)]="domain.codename" #codename="ngModel" minlength="2" maxlength="12" required> - <div *ngIf="codename.invalid && (codename.dirty || codename.touched)" - class="alert alert-danger"> + <div class="form-group" *ngIf="!isInMode(ComponentMode.EDIT)"> + <label for="codename" class="col-sm-2 control-label">{{ 'DOMAIN_DETAILS.CODE_NAME' | translate }}</label> + <div class="col-sm-10"> + <input type="text" class="form-control" [disabled]="isInMode(ComponentMode.VIEW)" id="codename" + name="codename" pattern="[a-z0-9-]*" + [(ngModel)]="domain.codename" #codename="ngModel" minlength="2" maxlength="12" required> + <div *ngIf="codename.invalid && (codename.dirty || codename.touched)" + class="alert alert-danger"> - <div *ngIf="codename.errors.required">{{ 'DOMAIN_DETAILS.CODE_NAME_IS_REQUIRED_MESSAGE' | translate }}</div> - <div *ngIf="codename.errors.pattern">{{ 'DOMAIN_DETAILS.CODE_NAME_PATTERN_MESSAGE_1' | translate }}</div> - <div *ngIf="codename.errors.minlength || codename.errors.maxlength">{{ 'DOMAIN_DETAILS.CODE_NAME_PATTERN_MESSAGE_2' | translate }}</div> + <div *ngIf="codename.errors.required">{{ 'DOMAIN_DETAILS.CODE_NAME_IS_REQUIRED_MESSAGE' | translate }}</div> + <div *ngIf="codename.errors.pattern">{{ 'DOMAIN_DETAILS.CODE_NAME_PATTERN_MESSAGE_1' | translate }}</div> + <div *ngIf="codename.errors.minlength || codename.errors.maxlength">{{ 'DOMAIN_DETAILS.CODE_NAME_PATTERN_MESSAGE_2' | translate }}</div> + </div> </div> </div> - </div> - <div class="form-group" *ngIf="!isInMode(ComponentMode.CREATE) && (authService.hasRole('ROLE_SYSTEM_ADMIN') || authService.hasRole('ROLE_OPERATOR'))"> - <label class="col-sm-2 control-label">{{ 'DOMAIN_DETAILS.ID_IN_DB' | translate }}</label> - <div class="col-sm-10"> - <p class="form-control-static">{{domain.id}}</p> + <div class="form-group" *ngIf="!isInMode(ComponentMode.CREATE) && (authService.hasRole('ROLE_SYSTEM_ADMIN') || authService.hasRole('ROLE_OPERATOR'))"> + <label class="col-sm-2 control-label">{{ 'DOMAIN_DETAILS.ID_IN_DB' | translate }}</label> + <div class="col-sm-10"> + <p class="form-control-static">{{domain.id}}</p> + </div> + </div> + <hr/> + + <div class="form-group"> + <label for="kubernetesNamespace" class="col-sm-2 control-label">{{ 'DOMAIN_DETAILS.KUBERNETES_NAMESPACE' | translate }}</label> + <div class="col-sm-10"> + <input type="text" class="form-control" [disabled]="isInMode(ComponentMode.VIEW)" id="kubernetesNamespace" pattern="[a-z0-9-]*" maxlength="64" #namespace="ngModel" + name="kubernetesNamespace" [(ngModel)]="domain.domainTechDetails.kubernetesNamespace" placeholder="{{'DOMAIN_DETAILS.KUBERNETES_NAMESPACE_PLACEHOLDER' | translate}}"> + <div *ngIf="namespace.invalid && (namespace.dirty || namespace.touched)" class="alert alert-danger"> + <div *ngIf="namespace.errors.pattern">{{ 'DOMAIN_DETAILS.NAMESPACE_PATTERN_VALIDATION_MESSAGE' | translate }}</div> + <div *ngIf="namespace.errors.maxlength">{{ 'DOMAIN_DETAILS.NAMESPACE_MAX_LENGTH_VALIDATION_MESSAGE' | translate }}</div> + </div> + </div> </div> - </div> - - <hr/> - <div class="form-group"> - <label for="kubernetesNamespace" class="col-sm-2 control-label">{{ 'DOMAIN_DETAILS.KUBERNETES_NAMESPACE' | translate }}</label> - <div class="col-sm-10"> - <input type="text" class="form-control" [disabled]="isInMode(ComponentMode.VIEW)" id="kubernetesNamespace" pattern="[a-z0-9-]*" maxlength="64" #namespace="ngModel" - name="kubernetesNamespace" [(ngModel)]="domain.domainTechDetails.kubernetesNamespace" placeholder="{{'DOMAIN_DETAILS.KUBERNETES_NAMESPACE_PLACEHOLDER' | translate}}"> - <div *ngIf="namespace.invalid && (namespace.dirty || namespace.touched)" class="alert alert-danger"> - <div *ngIf="namespace.errors.pattern">{{ 'DOMAIN_DETAILS.NAMESPACE_PATTERN_VALIDATION_MESSAGE' | translate }}</div> - <div *ngIf="namespace.errors.maxlength">{{ 'DOMAIN_DETAILS.NAMESPACE_MAX_LENGTH_VALIDATION_MESSAGE' | translate }}</div> + <div class="form-group" *ngIf="domain.id !== domainService.getGlobalDomainId()"> + <label for="kubernetesStorageClass" class="col-sm-2 control-label">{{ 'DOMAIN_DETAILS.KUBERNETES_STORAGE_CLASS' | translate }}</label> + <div class="col-sm-10"> + <input type="text" class="form-control" [disabled]="isInMode(ComponentMode.VIEW)" id="kubernetesStorageClass" + name="kubernetesStorageClass" [(ngModel)]="domain.domainTechDetails.kubernetesStorageClass"> </div> </div> - </div> - <div class="form-group" *ngIf="domain.id !== domainService.getGlobalDomainId()"> - <label for="kubernetesStorageClass" class="col-sm-2 control-label">{{ 'DOMAIN_DETAILS.KUBERNETES_STORAGE_CLASS' | translate }}</label> - <div class="col-sm-10"> - <input type="text" class="form-control" [disabled]="isInMode(ComponentMode.VIEW)" id="kubernetesStorageClass" - name="kubernetesStorageClass" [(ngModel)]="domain.domainTechDetails.kubernetesStorageClass"> - </div> - </div> - - <div class="form-group" *ngIf="domain.id !== domainService.getGlobalDomainId()"> - <label for="kubernetesIngressClass" class="col-sm-2 control-label">{{'DOMAIN_DETAILS.KUBERNETES_INGRESS_CLASS' | translate }}</label> - <div class="col-sm-10"> - <input type="text" class="form-control" [disabled]="isInMode(ComponentMode.VIEW)" id="kubernetesIngressClass" - name="kubernetesIngressClass" [(ngModel)]="domain.domainTechDetails.kubernetesIngressClass"> + <div class="form-group" *ngIf="domain.id !== domainService.getGlobalDomainId()"> + <label for="kubernetesIngressClass" class="col-sm-2 control-label">{{'DOMAIN_DETAILS.KUBERNETES_INGRESS_CLASS' | translate }}</label> + <div class="col-sm-10"> + <input type="text" class="form-control" [disabled]="isInMode(ComponentMode.VIEW)" id="kubernetesIngressClass" + name="kubernetesIngressClass" [(ngModel)]="domain.domainTechDetails.kubernetesIngressClass"> + </div> </div> - </div> - <div class="form-group" *ngIf="domain.id !== domainService.getGlobalDomainId()"> - <label for="externalServiceDomain " class="col-sm-2 control-label">{{ 'DOMAIN_DETAILS.EXTERNAL_SERVICE_DOMAIN' | translate }}</label> - <div class="col-sm-10"> - <input type="text" class="form-control" [disabled]="isInMode(ComponentMode.VIEW)" id="externalServiceDomain " - name="externalServiceDomain " [(ngModel)]="domain.domainTechDetails.externalServiceDomain"> + <div class="form-group" *ngIf="domain.id !== domainService.getGlobalDomainId()"> + <label for="externalServiceDomain " class="col-sm-2 control-label">{{ 'DOMAIN_DETAILS.EXTERNAL_SERVICE_DOMAIN' | translate }}</label> + <div class="col-sm-10"> + <input type="text" class="form-control" [disabled]="isInMode(ComponentMode.VIEW)" id="externalServiceDomain " + name="externalServiceDomain " [(ngModel)]="domain.domainTechDetails.externalServiceDomain"> + </div> </div> - </div> - <div class="form-group" *ngIf="domain?.id !== domainService.getGlobalDomainId()"> - <label class="col-sm-2 control-label text-right" for="dcnDeploymentType">{{'DOMAIN_DETAILS.DCN_DEPLOYMENT_TYPE' | translate }}</label> - <div class="col-sm-10"> - <select class="form-control" id="dcnDeploymentType" name="dcnDeploymentType" [(ngModel)]="domain.domainDcnDetails.dcnDeploymentType" [disabled]="isInMode(ComponentMode.VIEW)" required> - <option *ngFor="let type of keys" [value]="type">{{type | titlecase}}</option> - </select> + <div class="form-group" *ngIf="domain?.id !== domainService.getGlobalDomainId()"> + <label class="col-sm-2 control-label text-right" for="dcnDeploymentType">{{'DOMAIN_DETAILS.DCN_DEPLOYMENT_TYPE' | translate }}</label> + <div class="col-sm-10"> + <select class="form-control" id="dcnDeploymentType" name="dcnDeploymentType" [(ngModel)]="domain.domainDcnDetails.dcnDeploymentType" [disabled]="isInMode(ComponentMode.VIEW)" required> + <option *ngFor="let type of keys" [value]="type">{{type | titlecase}}</option> + </select> + </div> </div> - </div> - <div class="form-group" *ngIf="domain?.id !== domainService.getGlobalDomainId() && isInMode(ComponentMode.VIEW) && isManual()"> - <label class="col-sm-2 control-label text-right" for="configured-status">{{ 'DOMAIN_DETAILS.DCN_STATUS' | translate }}</label> - <div class="col-sm-10" id="configured-status" style="padding-top: 6px;"> - <p *ngIf="domain?.domainDcnDetails?.dcnConfigured">{{ 'DOMAIN_DETAILS.CONFIGURED_VIEW' | translate }}</p> - <p *ngIf="!domain?.domainDcnDetails?.dcnConfigured">{{ 'DOMAIN_DETAILS.NOT_CONFIGURED_VIEW' | translate }}</p> + <div class="form-group" *ngIf="domain?.id !== domainService.getGlobalDomainId() && isInMode(ComponentMode.VIEW) && isManual()"> + <label class="col-sm-2 control-label text-right" for="configured-status">{{ 'DOMAIN_DETAILS.DCN_STATUS' | translate }}</label> + <div class="col-sm-10" id="configured-status" style="padding-top: 6px;"> + <p *ngIf="domain?.domainDcnDetails?.dcnConfigured">{{ 'DOMAIN_DETAILS.CONFIGURED_VIEW' | translate }}</p> + <p *ngIf="!domain?.domainDcnDetails?.dcnConfigured">{{ 'DOMAIN_DETAILS.NOT_CONFIGURED_VIEW' | translate }}</p> + </div> </div> - </div> - <div class="form-group" *ngIf="domain.id !== domainService.getGlobalDomainId() && !isInMode(ComponentMode.VIEW) && isManual()"> - <label for="dcnConfigured" class="col-sm-2 control-label">{{ 'DOMAIN_DETAILS.DCN_CONFIGURED' | translate }}</label> - <div class="col-sm-10"> - <input type="checkbox" class="btn btn-default" [disabled]="isInMode(ComponentMode.VIEW)" id="dcnConfigured" - name="dcnConfigured" [(ngModel)]="domain.domainDcnDetails.dcnConfigured" (change)="changeDcnFieldUpdatedFlag()"> + <div class="form-group" *ngIf="domain.id !== domainService.getGlobalDomainId() && !isInMode(ComponentMode.VIEW) && isManual()"> + <label for="dcnConfigured" class="col-sm-2 control-label">{{ 'DOMAIN_DETAILS.DCN_CONFIGURED' | translate }}</label> + <div class="col-sm-10"> + <input type="checkbox" class="btn btn-default" [disabled]="isInMode(ComponentMode.VIEW)" id="dcnConfigured" + name="dcnConfigured" [(ngModel)]="domain.domainDcnDetails.dcnConfigured" (change)="changeDcnFieldUpdatedFlag()"> + </div> </div> </div> - <div class="panel panel-default" *ngIf="isInMode(ComponentMode.VIEW) && !authService.hasRole('ROLE_OPERATOR')"> - <div class="panel-heading">{{ 'DOMAIN_DETAILS.DOMAIN_USERS' | translate }}</div> + + <div class="background-section" *ngIf="isInMode(ComponentMode.VIEW) && !authService.hasRole('ROLE_OPERATOR')"> + <h4 style="font-size:15px; font-weight: bold">{{ 'DOMAIN_DETAILS.DOMAIN_USERS' | translate }}</h4> <div class="panel-body"> <table class="table table-hover table-condensed" aria-describedby="Domains details table"> <thead> @@ -147,8 +150,8 @@ <app-domain-namespace-annotations [annotationRead]="annotations" (annotations)="handleAnnotationsChange($event)" (trigerDelete)="handleAnnotationDelete($event)"></app-domain-namespace-annotations> </div> - <div *ngIf="isInMode(ComponentMode.VIEW)" class="panel panel-default"> - <div class="panel-heading">{{ 'DOMAIN_DETAILS.APP_STATUS' | translate }}</div> + <div *ngIf="isInMode(ComponentMode.VIEW)" class="background-section"> + <h4 style="font-size:15px; font-weight: bold">{{ 'DOMAIN_DETAILS.APP_STATUS' | translate }}</h4> <div class="panel-body"> <table class="table table-hover table-condensed" aria-describedby="Domain details table"> <thead> @@ -207,8 +210,8 @@ </div> </div> - <div class="panel panel-default" *ngIf="displayCustomerNetworksSection && domain.id !== domainService.getGlobalDomainId()"> - <div class="panel-heading">{{'DOMAIN_DETAILS.CUSTOMER_NETWORKS' | translate}}</div> + <div class="background-section" *ngIf="displayCustomerNetworksSection && domain.id !== domainService.getGlobalDomainId()"> + <h4 style="font-size:15px; font-weight: bold">{{'DOMAIN_DETAILS.CUSTOMER_NETWORKS' | translate}}</h4> <div class="panel-body"> <div class="text-center" *ngIf="isInMode(ComponentMode.VIEW) && domain.domainDcnDetails.customerNetworks.length == 0"> <h5>{{'DOMAIN_DETAILS.CUSTOMER_NETWORKS_EMPTY_LIST_MESSAGE' | translate}}</h5> @@ -233,8 +236,8 @@ - <div class="panel panel-default" *ngIf="isInMode(ComponentMode.VIEW) && !authService.hasRole('ROLE_OPERATOR')"> - <div class="panel-heading">{{ 'DOMAINS.LIST.GROUP' | translate }}</div> + <div class="background-section" *ngIf="isInMode(ComponentMode.VIEW) && !authService.hasRole('ROLE_OPERATOR')"> + <h4 style="font-size:15px; font-weight: bold">{{ 'DOMAINS.LIST.GROUP' | translate }}</h4> <div class="panel-body"> <table class="table table-hover table-condensed" aria-describedby="Domain group list"> <thead> diff --git a/src/app/appmarket/domains/domains.routes.ts b/src/app/appmarket/domains/domains.routes.ts index c99b22f037941a04df1c9e17f75d39a52ef6f085..65701e24f5b398eecaa6d87baf0bee021ffb024c 100644 --- a/src/app/appmarket/domains/domains.routes.ts +++ b/src/app/appmarket/domains/domains.routes.ts @@ -12,46 +12,46 @@ import { DomainAnnotationsComponent } from './domain-annotations/domain-annotati export const DomainsRoutes: Route[] = [ { - path: 'admin/domains', component: DomainsListComponent, canActivate: [AuthGuard, RoleGuard], + path: 'domains', component: DomainsListComponent, canActivate: [AuthGuard, RoleGuard], data: {roles: ['ROLE_SYSTEM_ADMIN', 'ROLE_DOMAIN_ADMIN', 'ROLE_OPERATOR', 'ROLE_GROUP_DOMAIN_ADMIN', 'ROLE_GROUP_MANAGER']} }, { - path: 'admin/domains/add', component: DomainComponent, canActivate: [AuthGuard, RoleGuard], + path: 'domains/add', component: DomainComponent, canActivate: [AuthGuard, RoleGuard], data: {mode: ComponentMode.CREATE, roles: ['ROLE_SYSTEM_ADMIN']} }, { - path: 'admin/domains/annotations', component: DomainAnnotationsComponent, canActivate: [AuthGuard, RoleGuard], + path: 'domains/annotations', component: DomainAnnotationsComponent, canActivate: [AuthGuard, RoleGuard], data: {mode: ComponentMode.CREATE, roles: ['ROLE_SYSTEM_ADMIN']} }, { - path: 'admin/domains/view/:id', component: DomainComponent, canActivate: [AuthGuard, RoleGuard], + path: 'domains/view/:id', component: DomainComponent, canActivate: [AuthGuard, RoleGuard], data: {mode: ComponentMode.VIEW, roles: ['ROLE_SYSTEM_ADMIN', 'ROLE_DOMAIN_ADMIN', 'ROLE_OPERATOR', 'ROLE_GROUP_DOMAIN_ADMIN']} }, { - path: 'admin/domains/edit/:id', component: DomainComponent, canActivate: [AuthGuard, RoleGuard], + path: 'domains/edit/:id', component: DomainComponent, canActivate: [AuthGuard, RoleGuard], data: {mode: ComponentMode.EDIT, roles: ['ROLE_SYSTEM_ADMIN', 'ROLE_OPERATOR']} }, { - path: 'admin/domains/groups', component: DomainGroupsComponent, canActivate: [AuthGuard, RoleGuard], + path: 'domains/groups', component: DomainGroupsComponent, canActivate: [AuthGuard, RoleGuard], data: {roles: ['ROLE_SYSTEM_ADMIN', 'ROLE_GROUP_MANAGER']} }, { - path: 'admin/domains/groups/add', component: DomainGroupViewComponent, canActivate: [AuthGuard, RoleGuard], + path: 'domains/groups/add', component: DomainGroupViewComponent, canActivate: [AuthGuard, RoleGuard], data: {mode: ComponentMode.CREATE, roles: ['ROLE_SYSTEM_ADMIN', 'ROLE_GROUP_MANAGER']} }, { - path: 'admin/domains/groups/:id', component: DomainGroupViewComponent, canActivate: [AuthGuard, RoleGuard], + path: 'domains/groups/:id', component: DomainGroupViewComponent, canActivate: [AuthGuard, RoleGuard], data: {mode: ComponentMode.VIEW, roles: ['ROLE_SYSTEM_ADMIN', 'ROLE_GROUP_MANAGER']} }, { - path: 'admin/domains/bulks/new', component: DomainuploadComponent, + path: 'domains/bulks/new', component: DomainuploadComponent, data: {mode: ComponentMode.VIEW, roles: ['ROLE_SYSTEM_ADMIN', 'ROLE_GROUP_MANAGER']}}, { - path: 'admin/domains/bulks', component: BulkDomainListComponent, canActivate: [AuthGuard, RoleGuard], + path: 'domains/bulks', component: BulkDomainListComponent, canActivate: [AuthGuard, RoleGuard], data: {roles: ['ROLE_SYSTEM_ADMIN', 'ROLE_GROUP_MANAGER']} }, { - path: 'admin/domains/bulks/:id', component: BulkViewComponent, canActivate: [AuthGuard, RoleGuard], + path: 'domains/bulks/:id', component: BulkViewComponent, canActivate: [AuthGuard, RoleGuard], data: {roles: ['ROLE_SYSTEM_ADMIN', 'ROLE_GROUP_MANAGER']} } ]; diff --git a/src/app/appmarket/domains/list/domainslist.component.css b/src/app/appmarket/domains/list/domainslist.component.css index 18c9766fc0548b48444a5c54bd07673287a01195..321cf6dcfea2a53ade3fd4e2fb74f9f6d693abc1 100644 --- a/src/app/appmarket/domains/list/domainslist.component.css +++ b/src/app/appmarket/domains/list/domainslist.component.css @@ -13,11 +13,72 @@ tr.clickable { cursor: pointer; } -.dropdown:hover .dropdown-menu { - display: block; -} +/*.dropdown:hover .dropdown-menu {*/ +/* display: block;*/ +/*}*/ .space-between { display: flex; justify-content: space-between; } + +:host ::ng-deep .p-datatable .p-datatable-thead > tr > th{ + border: 1px solid #E0E2E5; + background:transparent; + border-width: 0 0 1px 0; +} +:host ::ng-deep .p-datatable .p-datatable-tbody > tr > td { + text-align: left; + border: 1px solid #E0E2E5; + border-width: 0 0 1px 0; + padding: 1rem 1rem; +} +:host ::ng-deep .p-datatable .p-paginator-bottom{ + height: 40px; + background: transparent; + border: none; + margin-top:10px; +} +:host ::ng-deep .p-datatable .p-datatable-tbody > tr{ + background: transparent; +} + +:host ::ng-deep .p-paginator .p-paginator-pages .p-paginator-page{ + transition: unset; + border-radius: 50%; + min-width:3.5rem; + height:3.5rem; + margin:0 5px; + font-size: 14px; +} + +:host ::ng-deep .p-paginator-element{ + border-radius:50%; + margin:0 5px; + min-width:3.5rem; + height:3.5rem; + font-size: 14px; +} +:host ::ng-deep .p-paginator .p-dropdown{ + height:3rem; +} +:host ::ng-deep .p-paginator-icon{ + height: 1.5rem; + width: 1.5rem; +} +:host ::ng-deep .p-paginator .p-dropdown .p-dropdown-label{ + padding-right: 10px; +} + +label{ + padding-left:5px; + display: unset; + margin-bottom: 0; + font-weight: unset; +} +:host ::ng-deep .p-paginator .p-paginator-pages .p-paginator-page.p-highlight{ + background: var(--user-button-background-hover); +} +:host ::ng-deep .p-datatable>.p-datatable-wrapper { + overflow: visible; +} diff --git a/src/app/appmarket/domains/list/domainslist.component.html b/src/app/appmarket/domains/list/domainslist.component.html index 0e6e766792619153fc9668fda83660da110c6c2d..5a279e5cd877cd3b2c0ba1978ecc8549aa6e3dd8 100644 --- a/src/app/appmarket/domains/list/domainslist.component.html +++ b/src/app/appmarket/domains/list/domainslist.component.html @@ -1,91 +1,81 @@ -<div class="col-sm-12 col-sm-offset-1 col-sm-10 col-md-offset-1 col-md-10"> - <h3>{{ 'DOMAINS.TITLE' | translate }}</h3> - <div class="flex space-between align-content-center" style="height: 50px;"> - <div class="flex" style="height: 45px;"> - <a *roles="['ROLE_SYSTEM_ADMIN']" [routerLink]="['add']" class="btn btn-primary" style="margin-top: 10px;" - role="button">{{'DOMAINS.ADD_BUTTON' | translate}}</a> - </div> - <div *roles="['ROLE_SYSTEM_ADMIN']" class="flex" style="align-content: center; height: 35px; margin-top: 10px;"> - <button class="btn btn-primary" [routerLink]="['annotations']">{{'DOMAINS.ANNOTATIONS.EDIT' | translate}}</button> - </div> - <div *roles="['ROLE_SYSTEM_ADMIN']" class="flex" style="align-content: center; height: 35px; margin-top: 8px;"> - <span style="align-content: center; margin-right: 5px; margin-top: 10px;"> {{'DOMAINS.NOTACTIVE' | translate}}</span> - <p-checkbox id="showNotActive" [binary]="true" [(ngModel)]=" showNotActive" class="flex align-content-start" ></p-checkbox> - </div> - <div class="flex align-content-center" style="align-content: center; height: 35px; margin-top: 8px;"> - <p class="flex align-content-center" style="margin-top: 10px;">{{ 'DOMAINS.ITEMS_PER_PAGE' | translate }}:</p> - <span id="selectionItems" class="dropdown flex align-content-center" style="vertical-align: middle; display: inline-block; margin-right: 1rem;"> - <button class="dropdown-toggle btn" data-toggle="dropdown" data-close-others="true"> - <span class="flex mt-2 mr-1">{{maxItemsOnPage}}</span> - </button> - <ul class="dropdown-menu"> - <li *ngFor="let item of itemsPerPage" [ngClass]="{'active': maxItemsOnPage == item}"> - <a (click)="setItems(item)"> - <span>{{item.toString()}}</span> - </a> - </li> - </ul> - </span> - <input pInputText class="flex" name="search" id="search" placeholder="Search" type="text" style="height: 34px" (keydown)="changelog()" [(ngModel)]="searchValue"> - </div> +<div style="display: flex; align-items: center; margin-top:20px"> + <div style="margin-right:20px"> + <span class="p-input-icon-right" style="width: 100%"> + <i class="pi pi-search" style="font-size: 13px; top: 16px; margin-right: 5px;"></i> + <input pInputText type="text" name="search" class="form-control" style="width: 100%" + [(ngModel)]="searchValue" placeholder="{{'SEARCH' | translate}}"> + </span> + </div> + <div class="flex" style="margin-right:20px"> + <button *roles="['ROLE_SYSTEM_ADMIN']" [routerLink]="['add']" class="btn btn-primary" + role="button">{{'DOMAINS.ADD_BUTTON' | translate}}</button> + </div> + <div *roles="['ROLE_SYSTEM_ADMIN']" class="flex" style="margin-right:20px" > + <button class="btn btn-primary" [routerLink]="['annotations']">{{'DOMAINS.ANNOTATIONS.EDIT' | translate}}</button> + </div> + <div *roles="['ROLE_SYSTEM_ADMIN']" class="flex" > + <p-checkbox id="showNotActive" [binary]="true" [(ngModel)]=" showNotActive" class="flex" ></p-checkbox> + <label for="showNotActive"> {{'DOMAINS.NOTACTIVE' | translate}}</label> </div> - <br> - <table #table class="table table-hover table-condensed" aria-describedby="Domains list" sortable-table (sorted)="onSort($event)"> - <thead> - <tr> - <th scope="col" class="column-sortable" sortable-column="codename" sort-direction="asc">{{ 'DOMAINS.CODE_NAME' | translate }} - </th> - <th scope="col" class="column-sortable" sortable-column="name" >{{ 'DOMAINS.NAME' | translate }} - </th> - <th scope="col" class="column-sortable" sortable-column="active" >{{ 'DOMAINS.ACTIVATE' | translate }} - </th> - <th scope="col"> </th> - </tr> - </thead> - - <tbody> - <ng-template ngFor let-domain - [ngForOf]="domains | async | searchDomain: searchValue: showNotActive | paginate: {itemsPerPage: maxItemsOnPage, currentPage: p}"> - <tr *ngIf="!domain.deleted" class="clickable" [routerLink]="['view/', domain.id]"> - <td>{{domain?.codename}}</td> - <td>{{domain?.name}}</td> - <td><span class="glyphicon glyphicon-ok" *ngIf="domain?.active"></span><span - class="glyphicon glyphicon-remove" *ngIf="!(domain?.active)"></span></td> - <td class="text-right"> - <span class="dropdown"> - <a style="display: inline-block; text-align:right" class="dropdown-toggle " aria-expanded="false" - aria-haspopup="true" - data-toggle="dropdown" href="#" role="button"> - <em class="fas fa-cog icon-black icon-bigger"></em> - </a> - <ul class="dropdown-menu pull-right-drop"> - <li><a [routerLink]="['view/', domain.id]" class=""> - {{ 'DOMAINS.DETAILS_BUTTON' | translate }}</a> - </li> - <li><a *roles="['ROLE_SYSTEM_ADMIN', 'ROLE_OPERATOR']" [routerLink]="['edit/', domain.id]" - class="">{{ 'DOMAINS.EDIT_BUTTON' | translate }}</a> - </li> - <li><a *roles="['ROLE_SYSTEM_ADMIN']" (click)="$event.stopPropagation(); changeState(domain)" - class="">{{ getStateLabel(domain?.active) }}</a> - </li> - <li><a *roles="['ROLE_SYSTEM_ADMIN']" (click)="$event.stopPropagation(); openRemovalModal(domain)" - class="">{{ 'DOMAINS.DELETE_BUTTON' | translate }}</a> - </li> - </ul> - </span> - </td> - </tr> - </ng-template> - </tbody> - </table> - <pagination-controls class="text-right" (pageChange)="p = $event" - previousLabel="{{ 'PAGINATION.PREVIOUS' | translate }}" - nextLabel="{{ 'PAGINATION.NEXT' | translate }}" - screenReaderPaginationLabel="{{ 'PAGINATION.SCREEN_READER.PAGINATION' | translate }}" - screenReaderPageLabel="{{ 'PAGINATION.SCREEN_READER.PAGE' | translate }}" - screenReaderCurrentLabel="{{ 'PAGINATION.SCREEN_READER.CURRENT' | translate }}"></pagination-controls> - </div> + <h4 class="header">{{ 'DOMAINS.TITLE' | translate }}</h4> + + <div class="background-section"> + <p-table [value]="domains | async | searchDomain: searchValue: showNotActive" + [paginator]="true" + [rows]="maxItemsOnPage" + [rowsPerPageOptions]="[15, 20, 25, 30, 50]" + [responsiveLayout]="'scroll'"> + <ng-template pTemplate="header"> + <tr> + <th pSortableColumn="codename"> + {{ 'DOMAINS.CODE_NAME' | translate }} + <p-sortIcon field="codename"></p-sortIcon> + </th> + <th pSortableColumn="name"> + {{ 'DOMAINS.NAME' | translate }} + <p-sortIcon field="name"></p-sortIcon> + </th> + <th pSortableColumn="active"> + {{ 'DOMAINS.ACTIVATE' | translate }} + <p-sortIcon field="active"></p-sortIcon> + </th> + <th></th> + </tr> + </ng-template> + <ng-template pTemplate="body" let-domain> + <tr *ngIf="!domain.deleted"> + <td [routerLink]="['view/', domain.id]">{{domain?.codename}}</td> + <td>{{domain?.name}}</td> + <td> + <span class="glyphicon glyphicon-ok" *ngIf="domain?.active"></span> + <span class="glyphicon glyphicon-remove" *ngIf="!(domain?.active)"></span> + </td> + <td class="text-right"> + <span class="dropdown"> + <a style="display: inline-block" class="dropdown-toggle" aria-expanded="false" aria-haspopup="true" data-toggle="dropdown" href="#" role="button"> + <em class="pi pi-cog" style="font-size: 1.8rem; color: var(--l-text-color)"></em> + </a> + <ul class="dropdown-menu pull-right-drop" [appendTo]="'body'" > + <li><a [routerLink]="['view/', domain.id]" class=""> + {{ 'DOMAINS.DETAILS_BUTTON' | translate }}</a> + </li> + <li><a *roles="['ROLE_SYSTEM_ADMIN', 'ROLE_OPERATOR']" [routerLink]="['edit/', domain.id]" + class="">{{ 'DOMAINS.EDIT_BUTTON' | translate }}</a> + </li> + <li><a *roles="['ROLE_SYSTEM_ADMIN']" (click)="$event.stopPropagation(); changeState(domain)" + class="">{{ getStateLabel(domain?.active) }}</a> + </li> + <li><a *roles="['ROLE_SYSTEM_ADMIN']" (click)="$event.stopPropagation(); openRemovalModal(domain)" + class="">{{ 'DOMAINS.DELETE_BUTTON' | translate }}</a> + </li> + </ul> + </span> + </td> + </tr> + </ng-template> + </p-table> + </div> <nmaas-removal-confirmation-modal [object]="domainToRemove" [disableButton]="false" (onConfirm)="softRemoveDomain(domainToRemove.id)" [header]="'DOMAIN_DETAILS.MODAL.HEADER'" [description]="'DOMAIN_DETAILS.MODAL.DESCRIPTION'"></nmaas-removal-confirmation-modal> diff --git a/src/app/appmarket/domains/list/domainslist.component.ts b/src/app/appmarket/domains/list/domainslist.component.ts index b6256b314fd0729197b3c9cf8861033cd4ff4bd0..75fbeb6a8889d6bd1b6f58bd5030984e06d252b0 100644 --- a/src/app/appmarket/domains/list/domainslist.component.ts +++ b/src/app/appmarket/domains/list/domainslist.component.ts @@ -35,9 +35,9 @@ export class DomainsListComponent implements OnInit { public searchValue = ''; p: number; - public pageNumber = 1; - public paginatorName = 'paginator-identifier'; - public itemsPerPage: number[] = [15, 20, 25, 30, 50]; + // public pageNumber = 1; + // public paginatorName = 'paginator-identifier'; + // public itemsPerPage: number[] = [15, 20, 25, 30, 50]; public maxItemsOnPage = 15; public showNotActive = false; @@ -49,11 +49,11 @@ export class DomainsListComponent implements OnInit { } ngOnInit() { - const i = sessionStorage.getItem(this.users_item_number_key) - if (i) { - this.maxItemsOnPage = +i; - } - this.update(); + // const i = sessionStorage.getItem(this.users_item_number_key) + // if (i) { + // this.maxItemsOnPage = +i; + // } + this.update(); } protected getDomainsObservable(): Observable<Domain[]> { @@ -128,11 +128,11 @@ export class DomainsListComponent implements OnInit { } - public setItems(item) { - // store max items per page value in this session - sessionStorage.setItem(this.users_item_number_key, item); - this.maxItemsOnPage = item; - } + // public setItems(item) { + // // store max items per page value in this session + // sessionStorage.setItem(this.users_item_number_key, item); + // this.maxItemsOnPage = item; + // } onSorted(event: any) { diff --git a/src/app/appmarket/users/list/userslist.component.html b/src/app/appmarket/users/list/userslist.component.html index 76236c39205a2e4fe779f033f276151a3b12b0e8..b5540012889ac83ee00f113ef2a46c7e17626cf0 100644 --- a/src/app/appmarket/users/list/userslist.component.html +++ b/src/app/appmarket/users/list/userslist.component.html @@ -1,4 +1,4 @@ -<div class="col-sm-12" *ngIf="!domainMode" > +<div class="" *ngIf="!domainMode" > <div *roles="['ROLE_SYSTEM_ADMIN']" > <nmaas-userslist *ngIf="!isInAddToDomainMode" [users]="allUsers" [allowedModes]="[ComponentMode.VIEW, ComponentMode.DELETE]" (onUserRoleChange)="onUserRoleChange($event)" (onView)="onUserView($event)" (onModeChange)="onModeChange($event)" (onDelete)="onUserDelete($event)" (onRemoveFromDomain)="onRemoveRole($event)"> @@ -10,7 +10,7 @@ </div> -<div class="col-sm-12" *ngIf="domainMode"> +<div class="" *ngIf="domainMode"> <div *roles="['ROLE_DOMAIN_ADMIN', 'ROLE_GROUP_MANAGER']"> <nmaas-userslist *ngIf="!isInAddToDomainMode" [users]="allUsers" [allowedModes]="[ComponentMode.VIEW, ComponentMode.DELETE]" [domainMode]="true" (onUserRoleChange)="onUserRoleChange($event)" (onView)="onUserView($event)" (onModeChange)="onModeChange($event)" (onDelete)="onUserDelete($event)" (onRemoveFromDomain)="onRemoveRole($event)"> diff --git a/src/app/appmarket/users/userdetails/userdetails.component.html b/src/app/appmarket/users/userdetails/userdetails.component.html index f728a79ce158c48bcbe6db89c05d4452d03a47c3..64eeb8ac7800129a795c0e1648aded5819dadbc5 100644 --- a/src/app/appmarket/users/userdetails/userdetails.component.html +++ b/src/app/appmarket/users/userdetails/userdetails.component.html @@ -1,5 +1,5 @@ <div class="col-sm-12 col-sm-12 col-md-12"> - <div class="page-header"> + <div class=""> <h3> {{'USER_DETAILS.USER' | translate}} {{user?.username}} </h3> diff --git a/src/app/appmarket/users/users.routes.ts b/src/app/appmarket/users/users.routes.ts index 82be539df3e5deed04c5375cb9d52fabc5602bc8..5c91077d5ce0107de3baff5206b3277b5f4401bd 100644 --- a/src/app/appmarket/users/users.routes.ts +++ b/src/app/appmarket/users/users.routes.ts @@ -5,10 +5,10 @@ import {RoleGuard} from '../../auth/role.guard'; import {ComponentMode} from '../../shared/common/componentmode'; export const UsersRoutes: Route[] = [ - { path: 'admin/users', component: UsersListComponent, canActivate: [AuthGuard, RoleGuard], + { path: 'users', component: UsersListComponent, canActivate: [AuthGuard, RoleGuard], data: {roles: ['ROLE_SYSTEM_ADMIN']}}, - { path: 'admin/users/view/:id', component: UserDetailsComponent, canActivate: [AuthGuard, RoleGuard], + { path: 'users/view/:id', component: UserDetailsComponent, canActivate: [AuthGuard, RoleGuard], data: {mode: ComponentMode.VIEW, roles: ['ROLE_SYSTEM_ADMIN']} }, { path: 'domain/users', component: UsersListComponent, canActivate: [AuthGuard, RoleGuard], - data: {roles: ['ROLE_DOMAIN_ADMIN', 'ROLE_GROUP_MANAGER', 'ROLE_GROUP_MANAGER']}}, + data: {roles: ['ROLE_DOMAIN_ADMIN', 'ROLE_GROUP_MANAGER']}}, ]; diff --git a/src/app/auth/auth.guard.ts b/src/app/auth/auth.guard.ts index a0578db6eb36a04c7c1458465f54678545a43810..f0c2074acada30084334a6ff28f2cd1b0686c995 100644 --- a/src/app/auth/auth.guard.ts +++ b/src/app/auth/auth.guard.ts @@ -4,7 +4,7 @@ import {AuthService} from './auth.service'; import {ConfigurationService} from '../service'; import { debounceTime } from 'rxjs'; -@Injectable() +@Injectable() export class AuthGuard { constructor(private auth: AuthService, private router: Router, private maintenanceService: ConfigurationService) {} diff --git a/src/app/auth/auth.service.ts b/src/app/auth/auth.service.ts index 6156c8e479cfd788ed7808556017f30cb57bc40f..a83e22bd0a686f3cca1c7344e934bc8ebec14bc5 100644 --- a/src/app/auth/auth.service.ts +++ b/src/app/auth/auth.service.ts @@ -356,6 +356,13 @@ export class AuthService { }) } + get isLoggedIn$(): Observable<boolean> { + this.isLoggedInSubject.next(this.isLogged()); + return this.isLoggedInSubject.pipe( + debounceTime(100), // use debounceTime to aggregate multiple emissions https://rxjs.dev/api/operators/debounceTime + ); + } + public isLogged(): boolean { const token = this.getToken(); if (token == null) { diff --git a/src/app/service-unavailable/service-unavailable.component.html b/src/app/service-unavailable/service-unavailable.component.html index e491fa18040e8e81faf3a3b618e4abc3ee141e8d..7bd70f40926b51a88b70fd93a120c443806ac548 100644 --- a/src/app/service-unavailable/service-unavailable.component.html +++ b/src/app/service-unavailable/service-unavailable.component.html @@ -18,7 +18,7 @@ <a (click)="useLanguage(lang)"> <img alt="lang flag" class="lang-circle-icon" src="assets/images/country/{{lang}}_circle.png"/> - <span>{{lang.toUpperCase()}}</span> + <span>{{lang.toUpperCase()}}</span>, </a> </li> </ul> diff --git a/src/app/service/dashboard.service.spec.ts b/src/app/service/dashboard.service.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..24d7a1a55c4c188c015e11bf161b50823ba4ab3c --- /dev/null +++ b/src/app/service/dashboard.service.spec.ts @@ -0,0 +1,42 @@ +import { TestBed, inject} from '@angular/core/testing'; + +import { DashboardService } from './dashboard.service'; +import {HttpClient, HttpHandler} from '@angular/common/http'; +import {Observable, of} from 'rxjs'; +import {Configuration} from '../model/configuration'; +import {AppConfigService} from './appconfig.service'; + +class MockConfigurationService{ + protected uri:string; + + constructor() { + this.uri = 'http://localhost/api'; + } + + public getApiUrl(): string { + return 'http://localhost/api'; + } + + public getConfiguration():Observable<Configuration>{ + return of<Configuration>(); + } + + public updateConfiguration(configuration:Configuration):Observable<any>{ + return of<Configuration>(); + } +} + +describe('DashboardService', () => { + let service: DashboardService; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [DashboardService, HttpHandler, HttpClient, {provide: AppConfigService, useClass: MockConfigurationService}] + }); + service = TestBed.inject(DashboardService); + }); + + it('should be created', inject([DashboardService], (service: DashboardService) =>{ + expect(service).toBeTruthy(); + })); +}); diff --git a/src/app/service/dashboard.service.ts b/src/app/service/dashboard.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..eadf24747dfe0e98f2aca7dc637916e917fa4f03 --- /dev/null +++ b/src/app/service/dashboard.service.ts @@ -0,0 +1,22 @@ +import { Injectable } from '@angular/core'; +import {HttpClient} from '@angular/common/http'; +import {AppConfigService} from './appconfig.service'; +import {GenericDataService} from './genericdata.service'; + +@Injectable({ + providedIn: 'root' +}) +export class DashboardService extends GenericDataService { + + constructor(http: HttpClient, appConfig: AppConfigService) { + super(http, appConfig); + } + + public getAdmin() { + return this.get(this.appConfig.getApiUrl() + '/dashboard/admin') + } + + public getDomainAdmin(domainId?: number) { + return this.get(this.appConfig.getApiUrl() + '/dashboard/domain/' + domainId) + } +} diff --git a/src/app/shared/about/about.component.html b/src/app/shared/about/about.component.html index df6d0eb479e590e7436abc90906638ecbe4fb3d0..fbfc641bef0ddc923167316b5eb540faacb61225 100644 --- a/src/app/shared/about/about.component.html +++ b/src/app/shared/about/about.component.html @@ -26,12 +26,12 @@ <p class="form-control-static">{{gitInfo?.commitName}}</p> </div> </div> - <div class="row"> - <label class="control-label col-sm-2">{{'GIT_INFO.BRANCH_NAME' | translate}}:</label> - <div class="col-sm-10"> - <p class="form-control-static">{{gitInfo?.branchName}}</p> - </div> - </div> +<!-- <div class="row">--> +<!-- <label class="control-label col-sm-2">{{'GIT_INFO.BRANCH_NAME' | translate}}:</label>--> +<!-- <div class="col-sm-10">--> +<!-- <p class="form-control-static">{{gitInfo?.branchName}}</p>--> +<!-- </div>--> +<!-- </div>--> </div> </form> </div> diff --git a/src/app/shared/admin-dashboard/admin-dashboard.component.css b/src/app/shared/admin-dashboard/admin-dashboard.component.css new file mode 100644 index 0000000000000000000000000000000000000000..fdab80cb4c0d886552fe88c52387d4e9616f0493 --- /dev/null +++ b/src/app/shared/admin-dashboard/admin-dashboard.component.css @@ -0,0 +1,69 @@ +td{ + padding: 10px; + background: transparent; +} +th{ + padding: 10px; +} +:host ::ng-deep .p-datatable .p-datatable-thead > tr > th{ + border: 1px solid #E0E2E5; + background:transparent; + border-width: 0 0 1px 0; +} +:host ::ng-deep .p-datatable .p-datatable-tbody > tr > td { + text-align: left; + border: 1px solid #E0E2E5; + border-width: 0 0 1px 0; + padding: 1rem 1rem; +} +:host ::ng-deep .p-datatable .p-datatable-tbody > tr{ + background: transparent; +} +:host ::ng-deep .p-datatable .p-paginator-bottom{ + height: 40px; + background: transparent; + border: none; + margin-top:10px; +} +:host ::ng-deep .p-datatable .p-datatable-tbody > tr{ + background: transparent; +} + +:host ::ng-deep .p-paginator .p-paginator-pages .p-paginator-page{ + transition: unset; + border-radius: 50%; + min-width:3.5rem; + height:3.5rem; + margin:0 5px; + font-size: 14px; +} + +:host ::ng-deep .p-paginator-element{ + border-radius:50%; + margin:0 5px; + min-width:3.5rem; + height:3.5rem; + font-size: 14px; +} +:host ::ng-deep .p-paginator-icon{ + height: 1.5rem; + width: 1.5rem; +} +:host ::ng-deep .p-paginator .p-paginator-pages .p-paginator-page.p-highlight{ + background: var(--user-button-background-hover); +} +:host ::ng-deep .p-datatable-wrapper { + max-height: 50vh +} +:host ::ng-deep .p-datatable.p-datatable-scrollable > .p-datatable-wrapper > .p-datatable-table > .p-datatable-thead, .p-datatable.p-datatable-scrollable > .p-datatable-wrapper > .p-datatable-table > .p-datatable-tfoot, .p-datatable.p-datatable-scrollable > .p-datatable-wrapper > .p-scroller-viewport > .p-scroller > .p-datatable-table > .p-datatable-thead, .p-datatable.p-datatable-scrollable > .p-datatable-wrapper > .p-scroller-viewport > .p-scroller > .p-datatable-table > .p-datatable-tfoot{ + background: var(--app-background-color); +} +.width-50 { + width: 49% +} +@media screen and (max-width: 1390px){ + .width-50 { + width:100% + } +} + diff --git a/src/app/shared/admin-dashboard/admin-dashboard.component.html b/src/app/shared/admin-dashboard/admin-dashboard.component.html new file mode 100644 index 0000000000000000000000000000000000000000..b5e72786990f7cd04b73149b71c0554f90c99594 --- /dev/null +++ b/src/app/shared/admin-dashboard/admin-dashboard.component.html @@ -0,0 +1,112 @@ +<div *roles="['ROLE_SYSTEM_ADMIN']"> + <div style="display: flex; flex-direction:column"> + <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> + </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> + </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> + </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> + <p-table [value]="instanceCountInPeriodDetails" [scrollable]="true" [style]="{'width': '100%', 'max-height': '50vh'}"> + <ng-template pTemplate="header"> + <tr> + <th></th> + <th>Name</th> + <th>Version</th> + <th>Domain</th> + </tr> + </ng-template> + <ng-template pTemplate="body" let-instance> + <tr> + <td><img style="height: 40px" src="../../../assets/images/app-logo-example.png"/></td> + <td>{{instance.applicationName}}</td> + <td>{{instance.applicationVersion}}</td> + <td>{{instance.domainName}}</td> + </tr> + </ng-template> + </p-table> + </div> + <div class="width-50" > + <div class="background-section" style=""> + <h5 style="font-weight: bold">Most popular applications</h5> + <p-chart type="bar" [data]="popularAppsChartData" [options]="basicOptions" width="100%" height="50vh" /> + </div> + </div> + </div> + </div> +</div> +<div *roles="['ROLE_DOMAIN_ADMIN','ROLE_SYSTEM_ADMIN']"> + <div *ngIf="domainId !== 1"> + <div style="display: flex"> + <div class="background-section" style="flex: 1 1 50%; margin-right: 20px"> + <h5 style="font-weight: bold">Last login to the domain </h5> + <p-table [value]="domainAdminData?.userLogins | keyvalue"> + <ng-template pTemplate="header"> + <tr> + <th>User</th> + <th>Last login</th> + </tr> + </ng-template> + <ng-template pTemplate="body" let-login> + <tr> + <td>{{ login.key }}</td> + <td>{{ formatDate(login.value) }}</td> + </tr> + </ng-template> + </p-table> + </div> + <div class="background-section" style="flex: 1 1 50%;"> + <h5 style="font-weight: bold">Number of deployed applications per user </h5> + <p-table [value]="domainAdminData?.applicationDeployed | keyvalue"> + <ng-template pTemplate="header"> + <tr> + <th>User</th> + <th>Deployment</th> + </tr> + </ng-template> + <ng-template pTemplate="body" let-deployment> + <tr> + <td>{{ deployment.key }}</td> + <td>{{ deployment.value }}</td> + </tr> + </ng-template> + </p-table> + </div> + </div> + <div class="background-section" style=""> + <h5 style="font-weight: bold">Application status</h5> + <p-table [value]="applicationUpgradeStatus" [paginator]="true" [rows]="4" [scrollable]="true" [style]="{'width': '100%'}"> + <ng-template pTemplate="header"> + <tr> + <th></th> + <th>Name</th> + <th>Id</th> + <th>Instance name</th> + <th>Version</th> + <th>Need upgrade</th> + </tr> + </ng-template> + <ng-template pTemplate="body" let-app> + <tr> + <td><img style="height: 40px" src="../../../assets/images/app-logo-example.png"/></td> + <td>{{app.appName}}</td> + <td>{{app.appId}}</td> + <td>{{app.instanceName}}</td> + <td>{{app.appVersion}}</td> + <td>{{app.upgradePossible}}</td> + </tr> + </ng-template> + </p-table> + </div> + </div> +</div> diff --git a/src/app/shared/admin-dashboard/admin-dashboard.component.spec.ts b/src/app/shared/admin-dashboard/admin-dashboard.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..4157205c8bc5ec3c19b19b2b5ed3e17062c6c697 --- /dev/null +++ b/src/app/shared/admin-dashboard/admin-dashboard.component.spec.ts @@ -0,0 +1,23 @@ +// import { ComponentFixture, TestBed } from '@angular/core/testing'; +// +// import { AdminDashboardComponent } from './admin-dashboard.component'; +// +// describe('AdminDashboardComponent', () => { +// let component: AdminDashboardComponent; +// let fixture: ComponentFixture<AdminDashboardComponent>; +// +// beforeEach(async () => { +// await TestBed.configureTestingModule({ +// declarations: [AdminDashboardComponent] +// }) +// .compileComponents(); +// +// fixture = TestBed.createComponent(AdminDashboardComponent); +// component = fixture.componentInstance; +// fixture.detectChanges(); +// }); +// +// it('should create', () => { +// expect(component).toBeTruthy(); +// }); +// }); diff --git a/src/app/shared/admin-dashboard/admin-dashboard.component.ts b/src/app/shared/admin-dashboard/admin-dashboard.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..3ae9e16672ced2b5176ef5633728141465828473 --- /dev/null +++ b/src/app/shared/admin-dashboard/admin-dashboard.component.ts @@ -0,0 +1,106 @@ +import { Component } from '@angular/core'; +import {DashboardService} from '../../service/dashboard.service'; +import {UserDataService} from '../../service/userdata.service'; + +@Component({ + selector: 'app-admin-dashboard', + templateUrl: './admin-dashboard.component.html', + styleUrl: './admin-dashboard.component.css' +}) +export class AdminDashboardComponent { + popularAppsChartData: any; + + basicOptions: any; + adminData: any; + domainAdminData: any; + instanceCountInPeriodDetails: any[] = []; + applicationUpgradeStatus: any[] = []; + domainId; + + constructor(protected dashboardService: DashboardService, + private userDataService: UserDataService) { + } + + + ngOnInit() { + 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'); + const surfaceBorder = documentStyle.getPropertyValue('--surface-border'); + + + this.basicOptions = { + plugins: { + legend: { + labels: { + color: textColor + } + } + }, + scales: { + y: { + beginAtZero: true, + ticks: { + color: textColorSecondary, + callback: function(value) { + return Number(value).toFixed(0); + } + }, + grid: { + color: surfaceBorder, + drawBorder: false + } + }, + x: { + ticks: { + color: textColorSecondary + }, + grid: { + color: surfaceBorder, + drawBorder: false + } + } + } + }; + } + + chartData() { + const appNames = Object.keys(this.adminData.popularApps); + const appValues = Object.values(this.adminData.popularApps); + + this.popularAppsChartData = { + labels: appNames, + datasets: [ + { + label: 'Count of deployments', + data: appValues, + borderColor: '#42A5F5', + backgroundColor: ['rgba(66, 165, 245, 0.2)', 'rgba(255, 208, 208, 0.7)', 'rgba(115, 104, 193, 0.7)', 'rgba(255, 193, 130, 0.7)', 'rgba(140, 193, 104, 0.7)'], + fill: true + } + ] + }; + } + formatDate(date: any): string { + return new Date(date).toLocaleString(); + } + getDomainAdmin() { + this.dashboardService.getDomainAdmin(this.domainId).subscribe( + (response) => { + this.domainAdminData = response; + this.applicationUpgradeStatus = this.domainAdminData.applicationUpgradeStatus; + } + ) + } +} diff --git a/src/app/shared/admin-left-menu/admin-left-menu.component.css b/src/app/shared/admin-left-menu/admin-left-menu.component.css new file mode 100644 index 0000000000000000000000000000000000000000..afd8e55a067061f61d251e45aee4ab6787b740e5 --- /dev/null +++ b/src/app/shared/admin-left-menu/admin-left-menu.component.css @@ -0,0 +1,62 @@ +.menu { + background-color: var(--menu-color); + color: var(--l-text-color); + /*height: calc(100vh - 2rem);*/ + /*position: static;*/ + /*top: 0;*/ + /*left: 0;*/ + display: flex; + flex-direction: column; + padding: 1rem; + } + .menu ul { + list-style: none; + padding: 0; + } + .menu li { + padding: 10px 10px; + margin: 0.5rem 0; + border-radius: 4px; + } +.menu li:hover { + padding: 10px 5px; + background: var(--background); + border-left: 5px solid var(--menu-pink) +} +.menu li.active{ + padding: 10px 5px; +} +.menu li.active:hover{ + padding: 10px 5px; +} +.active{ + padding: 10px 5px; + background: white; + border-left: 5px solid var(--menu-pink) +} + .menu a { + color: var(--l-text-color); + text-decoration: none; + } +:host ::ng-deep .p-button{ + padding: 8px 10px; + width:100%; + background: var(--user-button-background); + border: none; +} +:host ::ng-deep .p-button:hover{ + background: var(--user-button-background-hover) +} +:host ::ng-deep .p-menu.p-menu-overlay{ + position: static; + width:100%; + margin-bottom:5px; + border:none; +} +:host ::ng-deep .p-menu .p-menuitem > .p-menuitem-content .p-menuitem-link { + text-decoration: none; + padding: 1.3rem; +} +:host ::ng-deep .p-menu .p-menuitem:not(.p-highlight):not(.p-disabled) > .p-menuitem-content:hover{ + background:var(--user-button-background-hover); +} diff --git a/src/app/shared/admin-left-menu/admin-left-menu.component.html b/src/app/shared/admin-left-menu/admin-left-menu.component.html new file mode 100644 index 0000000000000000000000000000000000000000..5f787a0b75252eaf9cf74e511a8df9c4bca5f8bc --- /dev/null +++ b/src/app/shared/admin-left-menu/admin-left-menu.component.html @@ -0,0 +1,54 @@ +<!-- <div class="flex flex-column justify-content-between "> + <div class="menu flex"> + <div> + <img src="../../../assets/images/logo-small.png" width="250px"> + </div> + <div style="margin-top: 30px"> + <nmaas-domain-filter class="drop-domain"></nmaas-domain-filter> + </div> + <ul style="margin-top: 30px"> + <li [routerLinkActiveOptions]="{exact:true}" [routerLinkActive]="['active']" > + <a style="display: flex; align-items: center;" [routerLink]="['/dashboard']"> + <i class="pi pi-chart-bar" style="margin-right:10px; font-size: 15px"></i>Dashboard</a> + </li> + <li *roles="['ROLE_DOMAIN_ADMIN', 'ROLE_SYSTEM_ADMIN', 'ROLE_OPERATOR', 'ROLE_VL_MANAGER', 'ROLE_VL_DOMAIN_ADMIN']" + [routerLinkActiveOptions]="{exact:true}" [routerLinkActive]="['active']" > + <a style="display: flex; align-items: center;" [routerLink]="['/admin/domains']"> + <i class="pi pi-server" style="margin-right:10px; font-size: 15px"></i>{{ 'NAVBAR.DOMAINS' | translate }}</a> + </li> + <li *roles="['ROLE_SYSTEM_ADMIN']" [routerLinkActiveOptions]="{exact:true}" [routerLinkActive]="['active']" > + <a style="display: flex; align-items: center;" [routerLink]="['/admin/users']"> + <i class="pi pi-users" style="margin-right:10px; font-size: 15px"></i>{{ 'NAVBAR.USERS' | translate }}</a> + </li> + <li *roles="['ROLE_SYSTEM_ADMIN', 'ROLE_TOOL_MANAGER']" [routerLinkActiveOptions]="{exact:true}" [routerLinkActive]="['active']" > + <a style="display: flex; align-items: center;" [routerLink]="['/']"> + <i class="pi pi-th-large" style="margin-right:10px; font-size: 15px"></i>Catalog</a> + </li> + <li *roles="['ROLE_SYSTEM_ADMIN']" [routerLinkActiveOptions]="{exact:true}" [routerLinkActive]="['active']" > + <a style="display: flex; align-items: center;" [routerLink]="['/admin/configuration']"> + <i class="pi pi-cog" style="margin-right:10px; font-size: 15px"></i>Settings</a> + </li> + <li *roles="['ROLE_SYSTEM_ADMIN']" [routerLinkActiveOptions]="{exact:true}" [routerLinkActive]="['active']" > + <a style="display: flex; align-items: center;" [routerLink]="['/admin/languages']"> + <i class="pi pi-tags" style="margin-right:10px; font-size: 15px"></i>{{'NAVBAR.LANGUAGES' | translate }}</a> + </li> + <li *roles="['ROLE_SYSTEM_ADMIN', 'ROLE_OPERATOR']" [routerLinkActiveOptions]="{exact:true}" [routerLinkActive]="['active']" > + <a style="display: flex; align-items: center;" [routerLink]="['/admin/monitor']"> + <i class="pi pi-chart-line" style="margin-right:10px; font-size: 15px"></i>{{ 'NAVBAR.MONITOR' | translate }}</a> + </li> + + </ul> + </div> + <div class="menu flex"> + <p-menu #menu [model]="items" [popup]="true" class="test" /> + <p-button *ngIf="!toggleAdmin" (onClick)="menu.toggle($event)" class="user-button"><i class="pi pi-user" style="font-size: 13px; margin-right:10px"></i> User</p-button> + <p-button *ngIf="!toggleAdmin" style="margin-top:10px"><i class="pi pi-sign-in" style="margin-right:10px; font-size: 15px"></i>Go to admin panel</p-button> + <p-button *ngIf="toggleAdmin" style="margin-top:10px"><i class="pi pi-sign-in" style="margin-right:10px; font-size: 15px"></i>Go to user panel</p-button> + </div> +</div> --> + +<div style="margin:40px"> + <router-outlet></router-outlet> +</div> + + diff --git a/src/app/shared/admin-left-menu/admin-left-menu.component.spec.ts b/src/app/shared/admin-left-menu/admin-left-menu.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..a1b9df7593065ac1bf629c4f81b7406a23fbbae7 --- /dev/null +++ b/src/app/shared/admin-left-menu/admin-left-menu.component.spec.ts @@ -0,0 +1,30 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AdminLeftMenuComponent } from './admin-left-menu.component'; +import { MessageService } from 'primeng/api'; +import { CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA } from '@angular/core'; + +describe('AdminLeftMenuComponent', () => { + let component: AdminLeftMenuComponent; + let fixture: ComponentFixture<AdminLeftMenuComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [AdminLeftMenuComponent], + providers: [MessageService], + schemas: [ + CUSTOM_ELEMENTS_SCHEMA, + NO_ERRORS_SCHEMA + ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(AdminLeftMenuComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/shared/admin-left-menu/admin-left-menu.component.ts b/src/app/shared/admin-left-menu/admin-left-menu.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..b14f03859bb36cb68b2030ef212d7b15cfcfe7dd --- /dev/null +++ b/src/app/shared/admin-left-menu/admin-left-menu.component.ts @@ -0,0 +1,16 @@ +import { Component } from '@angular/core'; +import { MenuItem } from 'primeng/api'; +import { ToastContainerComponent } from '../toast-container/toast-container.component'; +import { Router } from '@angular/router'; + +@Component({ + selector: 'app-admin-left-menu', + templateUrl: './admin-left-menu.component.html', + styleUrl: './admin-left-menu.component.css' +}) +export class AdminLeftMenuComponent { + + constructor() { + } + +} diff --git a/src/app/shared/admin/clusters/details/clusterdetails.component.html b/src/app/shared/admin/clusters/details/clusterdetails.component.html index 8d086868fe9beadb049eb635e58a6153ec82e304..9e092c6860ba94d5a82c8efc45dad687db697c47 100644 --- a/src/app/shared/admin/clusters/details/clusterdetails.component.html +++ b/src/app/shared/admin/clusters/details/clusterdetails.component.html @@ -1,5 +1,5 @@ -<div class="panel panel-default"> - <div class="panel-heading">{{ 'CLUSTERS.TITLE' | translate }}</div> +<div class="background-section"> + <h4 style="font-size:15px; font-weight: bold">{{ 'CLUSTERS.TITLE' | translate }}</h4> <div class="panel-body"> <form *ngIf="cluster" (submit)="submit()" class="form-horizontal" #clusterForm="ngForm"> <div class="panel-default panel-heading">{{ 'CLUSTERS.INGRESS' | translate }}</div> @@ -243,9 +243,10 @@ <div *ngIf="this.error" class="alert alert-danger"> <p>{{this.error}}</p> </div> - - <button *ngIf="!isInMode(ComponentMode.VIEW)" [disabled]="!clusterForm.form.valid" type="submit" - class="btn btn-primary">{{ 'CLUSTERS.SUBMIT_BUTTON' | translate }}</button> + <div class="flex justify-content-end"> + <button *ngIf="!isInMode(ComponentMode.VIEW)" [disabled]="!clusterForm.form.valid" type="submit" + class="btn btn-primary">{{ 'CLUSTERS.SUBMIT_BUTTON' | translate }}</button> + </div> </form> </div> </div> diff --git a/src/app/shared/applications/applications.component.html b/src/app/shared/applications/applications.component.html index 164d5047060e5ed13ecf123267e1b6af6195f671..840b0e5448d17aa65e8bb0445b661d5992a57484 100644 --- a/src/app/shared/applications/applications.component.html +++ b/src/app/shared/applications/applications.component.html @@ -1,34 +1,44 @@ -<div class="col-sm-12 col-sm-12 col-md-12"> - <div class="col-xs-12 col-sm-3 col-md-3 col-lg-3"> +<div class="col-sm-12 col-sm-12 col-md-12" style="display: flex; align-items: flex-end; flex-wrap: wrap;"> + <div class="col-xs-12 col-sm-12 col-md-4 col-lg-3"> <nmaas-inline-search [value]="searchedAppName" (changed)="filterAppsByName($event)"></nmaas-inline-search> </div> - <div class="col-xs-12 col-sm-4 col-md-4 col-lg-4"> + <div class="col-xs-12 col-sm-12 col-md-4 col-lg-3"> <nmaas-tag-filter (changed)="filterAppsByTag($event)"></nmaas-tag-filter> </div> - <div class="col-xs-12 col-sm-3 col-md-3 col-lg-3 form-inline"> - <div class="form-group"> + <div class="col-xs-12 col-sm-12 col-md-4 col-lg-3 form-inline"> + <div class="form-group" style="width: 100%"> <label for="sortMode" style="margin-right: 6px;" >{{'APP_INSTANCES.SORT' | translate}}</label> - <select id="sortMode" name="sortMode" class="form-control" [(ngModel)]="sortMode" (ngModelChange)="onSort()"> + <select id="sortMode" name="sortMode" class="form-control" [(ngModel)]="sortMode" (ngModelChange)="onSort()" style="width: 100%"> <option *ngFor="let opt of sortModeList" [value]="opt">{{('APP_INSTANCES.SORT_MODE.' + opt) | translate}}</option> </select> </div> </div> - <div class="col-xs-12 col-sm-2 col-md-2 col-lg-2"> - <div class="btn-toolbar" role="toolbar"> - <div class="btn-group pull-right"> - <button href="#tab-grid" class="btn btn-default" data-toggle="tab" [class.active]="selectedListType === ListType.GRID" (click)="selectedListType = ListType.GRID"> - <span class="glyphicon glyphicon-th"></span> - </button> - <button href="#tab-list" class="btn btn-default" data-toggle="tab" [class.active]="selectedListType === ListType.TABLE" (click)="selectedListType = ListType.TABLE"> - <span class="glyphicon glyphicon-th-list"></span> - </button> - </div> - </div> + <div class="col-xs-12 col-sm-12 col-md-4 col-lg-3" style="padding: 8px 15px; display: flex"> + <p-checkbox + inputId="subscribed" + binary="true" + [(ngModel)]="showSubscribed" + [ngModelOptions]="{standalone: true}" + id="subscribed" + ngDefaultControl/> + <label style="margin: 0; padding-left: 5px; font-weight: unset; text-wrap: nowrap" for="subscribed">Show subscribed only</label> </div> +<!-- <div class="col-xs-12 col-sm-2 col-md-2 col-lg-2">--> +<!-- <div class="btn-toolbar" role="toolbar">--> +<!-- <div class="btn-group pull-right">--> +<!-- <button href="#tab-grid" class="btn btn-default" data-toggle="tab" [class.active]="selectedListType === ListType.GRID" (click)="selectedListType = ListType.GRID">--> +<!-- <span class="glyphicon glyphicon-th"></span>--> +<!-- </button>--> +<!-- <button href="#tab-list" class="btn btn-default" data-toggle="tab" [class.active]="selectedListType === ListType.TABLE" (click)="selectedListType = ListType.TABLE">--> +<!-- <span class="glyphicon glyphicon-th-list"></span>--> +<!-- </button>--> +<!-- </div>--> +<!-- </div>--> +<!-- </div>--> </div> <hr> -<nmaas-applist [appView]="appView" [listType]="selectedListType" [applications]="applications" [selected]="selected" [domainId]="domainId" [domain]="domain"></nmaas-applist> +<nmaas-applist [appView]="appView" [listType]="selectedListType" [showSubscribed]="showSubscribed" [applications]="applications" [selected]="selected" [domainId]="domainId" [domain]="domain"></nmaas-applist> diff --git a/src/app/shared/applications/applications.component.spec.ts b/src/app/shared/applications/applications.component.spec.ts index d45f571f8995d6bb44529fd4672c7173b6bf67e7..296500a694818afb5a8b519a9638002e64ebc1fd 100644 --- a/src/app/shared/applications/applications.component.spec.ts +++ b/src/app/shared/applications/applications.component.spec.ts @@ -18,6 +18,7 @@ import {of} from 'rxjs'; import {AppInstallModalComponent} from '../modal/appinstall'; import {ModalComponent} from '../modal'; import {Domain} from '../../model/domain'; +import {CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA} from '@angular/core'; describe('ApplicationsComponent', () => { let component: ApplicationsViewComponent; @@ -52,7 +53,8 @@ describe('ApplicationsComponent', () => { } }), ], - providers: [AppsService, AppSubscriptionsService, UserDataService, AppConfigService, TagService, DomainService, AppInstanceService] + providers: [AppsService, AppSubscriptionsService, UserDataService, AppConfigService, TagService, DomainService, AppInstanceService], + schemas: [CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA] }) .compileComponents(); })); diff --git a/src/app/shared/applications/applications.component.ts b/src/app/shared/applications/applications.component.ts index 03a1b40421b93fceead7206aefb4d2e6c9ffcace..0c5ca512ab2e7d7b9d2e5d763290d15628ed62e1 100644 --- a/src/app/shared/applications/applications.component.ts +++ b/src/app/shared/applications/applications.component.ts @@ -60,6 +60,7 @@ export class ApplicationsViewComponent implements OnInit, OnChanges { public sortMode = 'NAME'; private popStats: any = {}; + public showSubscribed = false; constructor(private appsService: AppsService, private appSubsService: AppSubscriptionsService, @@ -76,7 +77,6 @@ export class ApplicationsViewComponent implements OnInit, OnChanges { this.popStats = data; } ) - } ngOnChanges(changes: SimpleChanges) { diff --git a/src/app/shared/applications/list/applist.component.html b/src/app/shared/applications/list/applist.component.html index 4d0aafd6788ff2fa08c866c1756e6428e56e3c09..62290e16b297d01e643bf0bc1c05fc4642aa2f8f 100644 --- a/src/app/shared/applications/list/applist.component.html +++ b/src/app/shared/applications/list/applist.component.html @@ -3,7 +3,7 @@ <div *ngIf="listType === ListType.GRID" class="tab-pane fade in" [class.active]="listType === ListType.GRID" id="tab-grid"> <div class="row auto-clear"> - <nmaas-applist-element *ngFor="let app of applications | async" [app]="app" [domainId]="domainId" [selected]="(selected | async)?.has(app.id)" [domain]="domainObject" ></nmaas-applist-element> + <nmaas-applist-element *ngFor="let app of applications | async" [app]="app" [showSubscribed]="showSubscribed" [domainId]="domainId" [selected]="(selected | async)?.has(app.id)" [domain]="domainObject" ></nmaas-applist-element> </div> </div> <div *ngIf="listType === ListType.TABLE" class="tab-pane fade in" diff --git a/src/app/shared/applications/list/applist.component.ts b/src/app/shared/applications/list/applist.component.ts index 7d8cccabdb8b86d45bdf78ea3825e755ba2f84ea..eef0d2b72ac4589058931269fbdff4cb1a565ea7 100644 --- a/src/app/shared/applications/list/applist.component.ts +++ b/src/app/shared/applications/list/applist.component.ts @@ -42,9 +42,13 @@ export class AppListComponent implements OnInit, OnChanges { @Input() public domain: Observable<Domain>; + @Input() + public showSubscribed: boolean; + public domainObject: Domain = undefined; + constructor(private appSubscriptionService: AppSubscriptionsService, private userDataService: UserDataService, private appConfig: AppConfigService, diff --git a/src/app/shared/applications/list/element/appelement.component.css b/src/app/shared/applications/list/element/appelement.component.css index b154cd875c7fe8e3593cedf1ca63b3ed9d62299d..d44532e7750cb9741dc1f0bcf7ffe5812536d004 100644 --- a/src/app/shared/applications/list/element/appelement.component.css +++ b/src/app/shared/applications/list/element/appelement.component.css @@ -1,47 +1,64 @@ -.subscribed { - background-color: #f7fff9; +body{ + color: var(--text-color) } -hr { - margin-top: 1px; - margin-bottom: 2px; +.app-card{ + background: var(--app-background-color); + border-radius: 10px; + margin:10px 0; + padding:30px 15px; +} +.app-name { + font-size: 14px; + font-weight: bold; + white-space: nowrap; +} +.element-container { + display: flex; + flex-direction: column; + align-items: center; + width: 100%; + height: 100%; + overflow-y: auto; } -.caption { - padding: 5px !important; +.subscribed { + position: absolute; + right: 30px; + top: 25px; + height: 30px; + width: 30px; + border-radius:50%; + background-color: #FFFFFF; + box-shadow: inset 0 0 7px rgba(0, 0, 0, 0.15); +} +.star{ + color: var(--menu-pink); + font-size: 16px; + height: 30px; + width: 30px; + display: flex; + justify-content: center; + align-items: center; } .image-container { - max-width: 160px; - max-height: 80px; + max-width: 150px; + max-height: 100px; } .image-container-outer { - width: 160px; - height: 80px; + width: 150px; + height: 100px; display: flex; flex-direction: row; align-items: center; } -.element-container { - display: flex; - flex-direction: column; - align-items: center; - width: 100%; - - height: 240px; - overflow-y: auto; -} - .description-container { width: 100%; } -.app-name { - white-space: nowrap; -} - .text-two-lines { width: 100%; line-height: 1.5em; @@ -83,9 +100,16 @@ hr { .clickable-tail { cursor: pointer; + transition: all 300ms ease-out; } .clickable-tail:hover { - transform: scale(1.05); - -webkit-transform: scale(1.05); + box-shadow: 0 2px 5px rgba(0,0,0,0.25); + /*transform: scale(1);*/ + /*-webkit-transform: scale(1);*/ +} +@media (min-width:1550px){ + .col-xl-3{ + width: 25% + } } diff --git a/src/app/shared/applications/list/element/appelement.component.html b/src/app/shared/applications/list/element/appelement.component.html index 064782e5d0b640d37ee14660cba7a192839722d6..0a4686b1dd0589b99b239b9987108fd2f48de632 100644 --- a/src/app/shared/applications/list/element/appelement.component.html +++ b/src/app/shared/applications/list/element/appelement.component.html @@ -1,15 +1,34 @@ -<div *ngIf="app && showAppInList" class="col-xs-12 col-sm-6 col-md-3 col-lg-3"> - <div class="thumbnail clickable-tail" [class.subscribed]="selected" [routerLink]="['/apps', app.id]"> - <div class = element-container> +<div *ngIf="app && showAppInList && !showSubscribed" class="col-xs-12 col-sm-12 col-md-6 col-lg-4 col-xl-3"> + <div class="app-card clickable-tail" [routerLink]="['/apps', app.id]"> + <div class = "element-container"> + <div [class.subscribed]="selected"> + <i [class.pi]="selected" [class.pi-star-fill]="selected" [class.star]="selected"></i> + </div> + <div class="image-container-outer"> + <img class="center center-block image-container" alt="App logo" [src]="appImagesService.getAppLogoUrl(app?.id) | secure" + onError="this.src='assets/images/app-logo-example.png';" /> + </div> + <div class="text-center description-container"> + <h3 class="app-name">{{app?.name}}</h3> + <div class="text-two-lines">{{getDescription()?.briefDescription}}</div> + </div> + </div> + </div> +</div> + +<div *ngIf="selected && showAppInList && showSubscribed" class="col-xs-12 col-sm-12 col-md-6 col-lg-4 col-xl-3"> + <div class="app-card clickable-tail" [routerLink]="['/apps', app.id]"> + <div class = "element-container"> + <div [class.subscribed]="selected"> + <i [class.pi]="selected" [class.pi-star-fill]="selected" [class.star]="selected"></i> + </div> <div class="image-container-outer"> <img class="center center-block image-container" alt="App logo" [src]="appImagesService.getAppLogoUrl(app?.id) | secure" onError="this.src='assets/images/app-logo-example.png';" /> </div> - <div class="caption text-center description-container"> + <div class="text-center description-container"> <h3 class="app-name">{{app?.name}}</h3> - <p><rate [pathUrl]="'/apps/' + app?.id + '/rate'" [rate]="app.rate"></rate></p> - <hr> <div class="text-two-lines">{{getDescription()?.briefDescription}}</div> </div> </div> diff --git a/src/app/shared/applications/list/element/appelement.component.ts b/src/app/shared/applications/list/element/appelement.component.ts index 434d17c702c85570022e2b406c17281a856e9960..d18b0371caf3129e34fd486cb02436f80b85b0e3 100644 --- a/src/app/shared/applications/list/element/appelement.component.ts +++ b/src/app/shared/applications/list/element/appelement.component.ts @@ -40,6 +40,9 @@ export class AppElementComponent implements OnInit, OnChanges { @Input() public domain: Domain; + @Input() + public showSubscribed: boolean; + @ViewChild(AppInstallModalComponent) public readonly modal: AppInstallModalComponent; diff --git a/src/app/shared/common/domainfilter/domainfilter.component.css b/src/app/shared/common/domainfilter/domainfilter.component.css index 1378b96bf7f5584580519b4ba5400b50faf2a595..6928a29ea5478461a388558c639d2e6428bad370 100644 --- a/src/app/shared/common/domainfilter/domainfilter.component.css +++ b/src/app/shared/common/domainfilter/domainfilter.component.css @@ -39,3 +39,25 @@ background-color: #233354; background-image: unset; } +:host ::ng-deep .p-dropdown{ + width:100%; + padding:3px; + border-radius: 4px; + border: none; +} +:host ::ng-deep .p-dropdown:hover{ + background: var(--user-button-background-hover); + +} +:host ::ng-deep .p-dropdown-panel .p-dropdown-items .p-dropdown-item.p-highlight.p-focus{ + background: var(--user-button-background-hover); + color: var(--l-text-color); + +} +:host ::ng-deep .p-dropdown-panel .p-dropdown-items .p-dropdown-item{ + padding: 0.8rem; +} +:host ::ng-deep .p-dropdown-panel{ + margin-top:5px; + border: none; +} diff --git a/src/app/shared/common/domainfilter/domainfilter.component.html b/src/app/shared/common/domainfilter/domainfilter.component.html index 01d721b0fc21245b3683b7de61633dedab823eff..a307df6d8ce63368b3b289c83c0cdccf9a2b0e4d 100644 --- a/src/app/shared/common/domainfilter/domainfilter.component.html +++ b/src/app/shared/common/domainfilter/domainfilter.component.html @@ -1,15 +1,38 @@ -<span [routerLinkActiveOptions]="{exact:true}" [routerLinkActive]="['active']" class="dropdown dropdown-domains"> - <a class="dropdown-toggle" data-close-others="true" data-toggle="dropdown" href="#"> - <span style="color: #414F6B;">{{ "FILTER.DOMAIN" | translate }}: {{ getCurrent() }}<span class="caret"></span></span> - </a> - <ul class="dropdown-menu" [ngClass]="{'long-domain-list': (domains | async)?.length > 16}"> - <input *ngIf="(domains | async)?.length > 16" class="search" type="text" [(ngModel)]="searchTerm" - placeholder="{{ 'SEARCH' | translate}}" (input)="updateFilter()"> - <li *ngFor="let domain of filteredDomains | async; let last = isLast" - [ngClass]="{'active': getCurrent() == domain?.name}"> - <a (click)="changeDomain(domain?.id, domain?.name)"> - <span>{{domain?.name}}</span> - </a> - </li> - </ul> -</span> +<!--<span [routerLinkActiveOptions]="{exact:true}" [routerLinkActive]="['active']" class="dropdown dropdown-domains">--> +<!-- <a class="dropdown-toggle" data-close-others="true" data-toggle="dropdown" href="#">--> +<!-- <span style="color: #414F6B;">{{ "FILTER.DOMAIN" | translate }}: {{ getCurrent() }}<span class="caret"></span></span>--> +<!-- </a>--> +<!-- <ul class="dropdown-menu" [ngClass]="{'long-domain-list': (domains | async)?.length > 16}">--> +<!-- <input *ngIf="(domains | async)?.length > 16" class="search" type="text" [(ngModel)]="searchTerm"--> +<!-- placeholder="{{ 'SEARCH' | translate}}" (input)="updateFilter()">--> +<!-- <li *ngFor="let domain of filteredDomains | async; let last = isLast"--> +<!-- [ngClass]="{'active': getCurrent() == domain?.name}">--> +<!-- <a (click)="changeDomain(domain?.id, domain?.name)">--> +<!-- <span>{{domain?.name}}</span>--> +<!-- </a>--> +<!-- </li>--> +<!-- </ul>--> +<!--</span>--> + +<p-dropdown + [options]="domains | async" + [(ngModel)]="selectedDomain" + optionLabel="name" + [filter]="false" + [filterPlaceholder]="'SEARCH' | translate" + (onChange)="changeDomain($event.value.id, $event.value.name)" + appendTo="body"> + + <ng-template pTemplate="selectedItem" let-item> + <span style="color: #414F6B;"> +<!-- {{ "FILTER.DOMAIN" | translate }}: --> + {{ item?.name }} + </span> + </ng-template> + + <ng-template pTemplate="item" let-item> + <span>{{ item?.name }}</span> + </ng-template> +</p-dropdown> + + diff --git a/src/app/shared/common/domainfilter/domainfilter.component.spec.ts b/src/app/shared/common/domainfilter/domainfilter.component.spec.ts index 93f4b150deca4fae65fe4bd4d96b2e0091963483..1dfabffcdf0244881d130a98c96a00ae59ebc875 100644 --- a/src/app/shared/common/domainfilter/domainfilter.component.spec.ts +++ b/src/app/shared/common/domainfilter/domainfilter.component.spec.ts @@ -11,6 +11,7 @@ import {of} from 'rxjs'; import {Domain} from '../../../model/domain'; import {ProfileService} from '../../../service/profile.service'; import {User} from '../../../model'; +import { CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA } from '@angular/core'; describe('DomainFilterComponent', () => { let component: DomainFilterComponent; @@ -82,6 +83,7 @@ describe('DomainFilterComponent', () => { } }), ], + schemas: [CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA], providers: [ {provide: DomainService, useValue: domainServiceSpy}, {provide: AuthService, useValue: authServiceSpy}, diff --git a/src/app/shared/common/domainfilter/domainfilter.component.ts b/src/app/shared/common/domainfilter/domainfilter.component.ts index 48472f0247fb83250824874f96a70d87648e2385..067891a9a4dde0ef9acf41ebe0a0e4b31593c6bf 100644 --- a/src/app/shared/common/domainfilter/domainfilter.component.ts +++ b/src/app/shared/common/domainfilter/domainfilter.component.ts @@ -29,7 +29,9 @@ export class DomainFilterComponent implements OnInit { private filteredDomainsSub = new BehaviorSubject<any[]>([]); - private domainsLocal : Domain[] = []; + private domainsLocal: Domain[] = []; + + selectedDomain: any; public filteredDomains = this.filteredDomainsSub.asObservable(); @@ -54,6 +56,7 @@ export class DomainFilterComponent implements OnInit { this.updateDomains(); this.domains.subscribe(domain => { + this.selectedDomain = domain[0]; this.domainName = domain[0].name; this.userData.selectDomainId(domain[0].id) this.filteredDomainsSub.next(domain); diff --git a/src/app/shared/common/password/password.component.html b/src/app/shared/common/password/password.component.html index dadd98f0bde525ef688faaa9ee82297241ca120d..f6626312e0bb27712eed7d525c5550059aa44bd3 100644 --- a/src/app/shared/common/password/password.component.html +++ b/src/app/shared/common/password/password.component.html @@ -41,8 +41,8 @@ </div> </div> <div class="nmaas-modal-footer"> - <button type="submit" class="btn btn-primary" (click)="submit()">{{'PASSWORD.SUBMIT_BUTTON' | translate}}</button> <button class="btn btn-secondary" (click)="hide()">Cancel</button> + <button type="submit" class="btn btn-primary" (click)="submit()">{{'PASSWORD.SUBMIT_BUTTON' | translate}}</button> </div> </nmaas-modal> diff --git a/src/app/shared/common/search/search.component.html b/src/app/shared/common/search/search.component.html index 401590c483198fadff390b9fc3b7d547065011a0..5bfd9fa34375deab52d67ab764764afd11aec686 100644 --- a/src/app/shared/common/search/search.component.html +++ b/src/app/shared/common/search/search.component.html @@ -1,7 +1,11 @@ <form class="form-inline" role="form" (submit)="onSubmit()"> - <div class="form-group"> - <input type="text" name="search" class="form-control" - [(ngModel)]="value" placeholder="{{'SEARCH' | translate}}" (ngModelChange)="onChange()"> + <div class="form-group" style="width: 100%"> + <span class="p-input-icon-right" style="width: 100%"> + <i class="pi pi-search" style="font-size: 13px; top: 16px; margin-right: 5px;"></i> + <input pInputText type="text" name="search" class="form-control" style="width: 100%" + [(ngModel)]="value" placeholder="{{'SEARCH' | translate}}" (ngModelChange)="onChange()"> + </span> </div> </form> + diff --git a/src/app/shared/common/tagfilter/tagfilter.component.html b/src/app/shared/common/tagfilter/tagfilter.component.html index f62710c5a37833813fceaa3e06908b019acd8e21..38874f055ce0d8026ebb226d7b75daba6a97fe61 100644 --- a/src/app/shared/common/tagfilter/tagfilter.component.html +++ b/src/app/shared/common/tagfilter/tagfilter.component.html @@ -1,8 +1,8 @@ <form class="form-inline" role="form"> - <div class="form-group"> + <div class="form-group" style="width: 100%"> <label style="margin-right: 6px;" for="tag">{{ "FILTER.TAGS" | translate }}</label> - <select class="form-control" id="tag" + <select class="form-control" id="tag" style="width: 100%" [(ngModel)]="value" [ngModelOptions]="{standalone: true}" (ngModelChange)="onChange()"> <option [value]="'all'">all</option> diff --git a/src/app/shared/domain-namespace-annotations/domain-namespace-annotations.component.css b/src/app/shared/domain-namespace-annotations/domain-namespace-annotations.component.css index f28c0dc3b4b161c71b93acbce3445f3e467cb425..1ffc9808117f220b152d39a389897a633805cf4c 100644 --- a/src/app/shared/domain-namespace-annotations/domain-namespace-annotations.component.css +++ b/src/app/shared/domain-namespace-annotations/domain-namespace-annotations.component.css @@ -1,3 +1,4 @@ .border-red { border: 1px solid red; } + diff --git a/src/app/shared/domain-namespace-annotations/domain-namespace-annotations.component.html b/src/app/shared/domain-namespace-annotations/domain-namespace-annotations.component.html index 826defac24c854dbfe46797077b2e1331ea1dc36..b710e9074aaa9c5bfd6b9d37c19c7ffa2f5566f8 100644 --- a/src/app/shared/domain-namespace-annotations/domain-namespace-annotations.component.html +++ b/src/app/shared/domain-namespace-annotations/domain-namespace-annotations.component.html @@ -1,14 +1,15 @@ -<div class="panel panel-default" style="width: 100% !important;" > +<div class="background-section" style="width: 100% !important;" > <div class="panel-heading"> - <div style="display: flex; justify-content: start; align-items: center"> - <div> + <div style="display: flex; justify-content:space-between"> + <h4 style="font-size:15px; font-weight: bold"> {{'DOMAINS.ANNOTATIONS.CREATION' | translate}} - </div> + </h4> + <button type="button" class="btn btn-text" (click)="addAnnotation()">{{'DOMAINS.ANNOTATIONS.ADD'| translate}}</button> </div> </div> <div class="panel-body"> <div style="display: flex; justify-content: end"> - <button type="button" class="btn btn-primary" (click)="addAnnotation()">{{'DOMAINS.ANNOTATIONS.ADD'| translate}}</button> + </div> <div class="grid flex flex-grow-1"> <div class="col-4"> @@ -63,8 +64,8 @@ </div> <div class="nmaas-modal-footer"> - <button type="button" class="btn btn-primary" (click)="closeModal()" [disabled]="isKeyNotUniqueAdd(newAnnotations) || !isKeyPatternCorrect">{{'DOMAINS.ADD_BUTTON' | translate}}</button> <button type="button" class="btn btn-secondary" (click)="modal.hide()">{{'APP_CHANGE_STATE_MODAL.CANCEL_BUTTON' | translate}}</button> + <button type="button" class="btn btn-primary" (click)="closeModal()" [disabled]="isKeyNotUniqueAdd(newAnnotations) || !isKeyPatternCorrect">{{'DOMAINS.ADD_BUTTON' | translate}}</button> </div> </nmaas-modal> @@ -94,7 +95,7 @@ </div> <div class="nmaas-modal-footer"> - <button type="button" class="btn btn-primary" (click)="closeModalEdit()" [disabled]="isEditAnnotationCorrect(editAnnotation) || !isKeyPatternCorrect">{{'DOMAINS.EDIT_BUTTON' | translate}}</button> <button type="button" class="btn btn-secondary" (click)="editModal.hide()">{{'APP_CHANGE_STATE_MODAL.CANCEL_BUTTON' | translate}}</button> + <button type="button" class="btn btn-primary" (click)="closeModalEdit()" [disabled]="isEditAnnotationCorrect(editAnnotation) || !isKeyPatternCorrect">{{'DOMAINS.EDIT_BUTTON' | translate}}</button> </div> </nmaas-modal> diff --git a/src/app/shared/footer/footer.component.css b/src/app/shared/footer/footer.component.css index 1d1b633e0b99bf386b188289e88d4fa62337f785..d1bab419a3afca8ce4d51a601485adb82bfaed8d 100644 --- a/src/app/shared/footer/footer.component.css +++ b/src/app/shared/footer/footer.component.css @@ -1,8 +1,10 @@ footer { flex-shrink: 0; - padding-bottom: 15px; + padding-bottom: 0px; padding-top: 15px; - background-color: #e7e7e7; + background-color: var(--menu-color); + display:flex; + /* margin-left: -1rem; */ } /*explicit text alignment for chrome*/ @@ -17,9 +19,9 @@ a:link{ text-decoration: underline; } -a:hover { - font-weight: 600; -} +/*a:hover {*/ +/* font-weight: 600;*/ +/*}*/ .img-footer{ max-height: 34px; @@ -86,6 +88,6 @@ a:hover { flex-direction: row; } .container-width { - width: 85vw; + width: calc(100vw - var(--left-panel-width)); margin: auto; } diff --git a/src/app/shared/footer/footer.component.html b/src/app/shared/footer/footer.component.html index 58f81ac5538eeb5823019387969a37198e0e38e4..ed9b650118ec98d17e0832b81cc15988dbe4488e 100644 --- a/src/app/shared/footer/footer.component.html +++ b/src/app/shared/footer/footer.component.html @@ -1,29 +1,34 @@ <footer class="footer col-xs-12" id="global-footer"> <div class="container-width"> - <div class="row row-center"> - <div class="col-sm-2"> - <!-- nmaas Logo optionally --> - <a href="https://www.geant.org/"> - <img alt="GÉANT Logo" src="/assets/images/geant-logo.png" width="200" class="image-link"/> - </a> - </div> - <div class="col-sm-3"> - <img alt="EU flag" src="/assets/images/cofunded.png" width="200" style="padding-top: 15px; "/> - </div> - <div class="col-sm-2 col-sm-offset-5"> - <p> - <a *ngIf="landingProfile === 'VNOC'" href="https://docs.nmaas.eu/use-cases/virtual-noc/vnoc-introduction/">Documentation</a> - <a *ngIf="landingProfile !== 'VNOC'" href="https://docs.nmaas.eu/use-cases/virtual-lab/vlab-introduction/">Documentation</a> - </p> - <p><a routerLink="/privacy">{{ 'FOOTER.NOTICE' | translate }}</a></p> - <p><a routerLink="/aup">{{ 'FOOTER.AUP' | translate }}</a></p> - <p><a routerLink="/about">{{ 'FOOTER.CONTACT' | translate }}</a></p> + <div class="" style="display: flex; justify-content: space-between"> + <div style="width:50%; display: flex"> + <div class="" style="margin-right: 20px"> + <!-- nmaas Logo optionally --> + <a href="https://www.geant.org/"> + <img alt="GÉANT Logo" src="/assets/images/geant-logo.png" width="100" class="image-link"/> + </a> + </div> + <div class=""> + <img alt="EU flag" src="/assets/images/cofunded.png" width="100" style="padding-top: 15px; "/> + </div> + </div> + <div class="" style="display: flex; width: 50%; justify-content: end; margin-right: 50px"> + <div style="margin-right: 30px"> + <p> + <a *ngIf="landingProfile === 'VNOC'" href="https://docs.nmaas.eu/use-cases/virtual-noc/vnoc-introduction/">Documentation</a> + <a *ngIf="landingProfile !== 'VNOC'" href="https://docs.nmaas.eu/use-cases/virtual-lab/vlab-introduction/">Documentation</a> + </p> + <p><a routerLink="/about">{{ 'FOOTER.CONTACT' | translate }}</a></p> + </div> + <div> + <p><a routerLink="/privacy">{{ 'FOOTER.NOTICE' | translate }}</a></p> + <p><a routerLink="/aup">{{ 'FOOTER.AUP' | translate }}</a></p> + </div> </div> </div> - - <div class="row"> - <div class="col-xs-10"> - </div> +<!-- <div class="row">--> +<!-- <div class="col-xs-10">--> +<!-- </div>--> <!-- <div class="col-xs-2">--> <!-- <div>--> <!-- <a class="footer-light footer-move-top" (click)="this.moveToTop();">--> @@ -31,7 +36,6 @@ <!-- </a>--> <!-- </div>--> <!-- </div>--> - </div> - +<!-- </div>--> </div> </footer> diff --git a/src/app/shared/left-menu/left-menu.component.css b/src/app/shared/left-menu/left-menu.component.css new file mode 100644 index 0000000000000000000000000000000000000000..0d858374a455923f5cba11191cd16aff7aaefb5b --- /dev/null +++ b/src/app/shared/left-menu/left-menu.component.css @@ -0,0 +1,108 @@ +.menu { + background-color: var(--menu-color); + color: var(--l-text-color); + /*height: calc(100vh - 2rem);*/ + /*position: static;*/ + /*top: 0;*/ + /*left: 0;*/ + display: flex; + flex-direction: column; + padding: 1rem; +} +.menu ul { + list-style: none; + padding: 0; +} +.menu li { + padding: 10px 10px; + margin: 0.5rem 0; + border-radius: 4px; +} + +.menu li:hover { + padding: 10px 5px; + background: var(--background); + border-left: 5px solid var(--menu-pink) +} +.menu li.active{ + padding: 10px 5px; +} +.menu li.active:hover{ + padding: 10px 5px; +} +.collapsed{ + /*width:40px;*/ +} +.active{ + padding: 10px 5px; + background: white; + border-left: 5px solid var(--menu-pink) +} +.menu a { + color: var(--l-text-color); + text-decoration: none; +} +:host ::ng-deep .p-button{ + padding: 8px 10px; + width:100%; + background: var(--user-button-background); + border: none; +} +:host ::ng-deep .p-button:hover{ + background: var(--user-button-background-hover) +} +:host ::ng-deep .p-menu.p-menu-overlay{ + position: static; + width:100%; + margin-bottom:5px; + border:none; +} +:host ::ng-deep .p-menu .p-menuitem > .p-menuitem-content .p-menuitem-link { + text-decoration: none; + padding: 1.3rem; +} +:host ::ng-deep .p-menu .p-menuitem:not(.p-highlight):not(.p-disabled) > .p-menuitem-content:hover{ + background:var(--user-button-background-hover); +} +:host ::ng-deep .p-accordion .p-accordion-header .p-accordion-header-link{ + display:flex; + flex-direction: row-reverse; + justify-content: space-between; + border:none; +} +:host ::ng-deep a { + font-weight: normal; + color: var(--l-text-color) +} +:host ::ng-deep a:hover { + text-decoration:none; + color: var(--l-text-color); + outline:none; +} +:host ::ng-deep a:focus{ + outline: none; +} +:host ::ng-deep .p-accordion .p-accordion-content{ + background: transparent; + border: none; +} + +.toggle-button{ + width: 30px; + height:30px; + background: var(--menu-color); + box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); + border-radius: 50px; + position: absolute; + left: 284px; + display: flex; + align-items: center; + justify-content: center; +} + +.collapsed-user{ + width: 130px; +} +::ng-deep app-modal-notification-send { + +} diff --git a/src/app/shared/left-menu/left-menu.component.html b/src/app/shared/left-menu/left-menu.component.html new file mode 100644 index 0000000000000000000000000000000000000000..7c94bb2215f52e0d219c0cd0bf845d13095a7be1 --- /dev/null +++ b/src/app/shared/left-menu/left-menu.component.html @@ -0,0 +1,167 @@ +<div class="flex flex-column justify-content-between menu-tr" [ngStyle]="{'width': isCollapsed ? '100px' : '300px'}" style="background: var(--menu-color); height: 100%; position: fixed;"> + <div class="menu flex"> + <div style="display: flex; align-items: center"> + <div class="logo-container"> + <img *ngIf="!isCollapsed" class="logo" src="../../../assets/images/logo-small.png" width="250px"> + <img *ngIf="isCollapsed" class="logo" src="../../../assets/images/nmaas-cloud.png" width="50px"> + </div> + <div class="toggle-button" (click)="toggleMenu()" [ngStyle]="{'left': isCollapsed ? '85px' : '284px'}"> + <i style="font-size: 1.8rem" class="pi pi-angle-left"></i> + </div> + </div> + <div style="margin-top: 30px"> + <nmaas-domain-filter class="drop-domain"></nmaas-domain-filter> + </div> + <ul *ngIf="!toggleAdmin" style="margin-top: 30px" > + <li [routerLinkActiveOptions]="{exact:true}" [routerLinkActive]="['active']" [ngClass]="{'collapsed': isCollapsed}"> + <a style="display: flex; align-items: center;" [routerLink]="['/']"> + <i class="pi pi-th-large" style="margin-right:10px; font-size: 15px" title="Application"></i> + <span *ngIf="!isCollapsed"> + Applications + </span> + </a> + </li> + <li [routerLinkActiveOptions]="{exact:true}" [routerLinkActive]="['active']" [ngClass]="{'collapsed': isCollapsed}"> + <a href="#" style="display: flex; align-items: center;" [routerLink]="['/instances']"> + <i class="pi pi-server" style="margin-right:10px; font-size: 15px" title=" Instances"></i> + <span *ngIf="!isCollapsed"> + Instances + </span> + </a> + </li> + </ul> + <ul *ngIf="toggleAdmin" style="margin-top: 30px"> + <li [routerLinkActiveOptions]="{exact:true}" [routerLinkActive]="['active']" [ngClass]="{'collapsed': isCollapsed}"> + <a style="display: flex; align-items: center;" [routerLink]="['admin/dashboard']"> + <i class="pi pi-chart-bar" style="margin-right:10px; font-size: 15px" title="Dashboard"></i> + <span *ngIf="!isCollapsed"> + Dashboard + </span> + </a> + </li> + <p-accordion> + <p-accordionTab *roles="['ROLE_DOMAIN_ADMIN', 'ROLE_SYSTEM_ADMIN', 'ROLE_OPERATOR', 'ROLE_GROUP_MANAGER', 'ROLE_GROUP_DOMAIN_ADMIN']"> + <ng-template pTemplate="header"> + <div> + <i class="pi pi-server" style="margin-right:10px; font-size: 15px" title="{{ 'NAVBAR.DOMAINS' | translate }}"></i> + <span *ngIf="!isCollapsed"> + {{ 'NAVBAR.DOMAINS' | translate }} + </span> + </div> + </ng-template> + <li *roles="['ROLE_DOMAIN_ADMIN', 'ROLE_SYSTEM_ADMIN', 'ROLE_OPERATOR', 'ROLE_GROUP_MANAGER', 'ROLE_GROUP_DOMAIN_ADMIN']" + [routerLinkActiveOptions]="{exact:true}" [routerLinkActive]="['active']" [ngClass]="{'collapsed': isCollapsed}" > + <a style="display: flex; align-items: center;" [routerLink]="['/admin/domains']"> + <i class="pi pi-list" style="margin-right:10px; font-size: 15px" title="List"></i> + <span *ngIf="!isCollapsed"> + List + </span> + </a> + </li> + <li *roles="['ROLE_SYSTEM_ADMIN', 'ROLE_GROUP_MANAGER']" + [routerLinkActiveOptions]="{exact:true}" [routerLinkActive]="['active']" [ngClass]="{'collapsed': isCollapsed}"> + <a style="display: flex; align-items: center;" [routerLink]="['/admin/domains/groups']"> + <i class="pi pi-table" style="margin-right:10px; font-size: 15px" title="Group"></i> + <span *ngIf="!isCollapsed"> + Group + </span> + </a> + </li> + <li *roles="['ROLE_SYSTEM_ADMIN', 'ROLE_GROUP_MANAGER']" + [routerLinkActiveOptions]="{exact:true}" [routerLinkActive]="['active']" [ngClass]="{'collapsed': isCollapsed}"> + <a style="display: flex; align-items: center;" [routerLink]="['/admin/domains/bulks']"> + <i class="pi pi-sitemap" style="margin-right:10px; font-size: 15px" title="Bulk deployments"></i> + <span *ngIf="!isCollapsed"> + Bulk deployments + </span> + </a> + </li> + </p-accordionTab> + </p-accordion> + <li *roles="['ROLE_SYSTEM_ADMIN', 'ROLE_GROUP_MANAGER']" [routerLinkActiveOptions]="{exact:true}" [routerLinkActive]="['active']" [ngClass]="{'collapsed': isCollapsed}"> + <a style="display: flex; align-items: center;" [routerLink]="['/admin/apps/bulks']"> + <i class="pi pi-box" style="margin-right:10px; font-size: 15px" title="{{ 'BULK.APP.HEADER' | translate }}"></i> + <span *ngIf="!isCollapsed"> + {{ 'BULK.APP.HEADER' | translate }} + </span> + </a> + </li> + <li *roles="['ROLE_SYSTEM_ADMIN']" [routerLinkActiveOptions]="{exact:true}" [routerLinkActive]="['active']" [ngClass]="{'collapsed': isCollapsed}"> + <a style="display: flex; align-items: center;" [routerLink]="['/admin/users']"> + <i class="pi pi-users" style="margin-right:10px; font-size: 15px" title="{{ 'NAVBAR.USERS' | translate }}"></i> + <span *ngIf="!isCollapsed"> + {{ 'NAVBAR.USERS' | translate }} + </span> + </a> + </li> + <li *roles="['ROLE_DOMAIN_ADMIN', 'ROLE_GROUP_DOMAIN_ADMIN']" [routerLinkActiveOptions]="{exact:true}" [routerLinkActive]="['active']" [ngClass]="{'collapsed': isCollapsed}"> + <a style="display: flex; align-items: center;" [routerLink]="['/admin/domain/users']"> + <i class="pi pi-users" style="margin-right:10px; font-size: 15px" title="{{ 'NAVBAR.DOMAIN_USERS' | translate }}"></i> + <span *ngIf="!isCollapsed"> + {{ 'NAVBAR.DOMAIN_USERS' | translate }} + </span> + </a> + </li> + <li *roles="['ROLE_SYSTEM_ADMIN', 'ROLE_TOOL_MANAGER']" [routerLinkActiveOptions]="{exact:true}" [routerLinkActive]="['active']" [ngClass]="{'collapsed': isCollapsed}"> + <a style="display: flex; align-items: center;" [routerLink]="['/admin/apps']"> + <i class="pi pi-th-large" style="margin-right:10px; font-size: 15px" title="Catalog"></i> + <span *ngIf="!isCollapsed"> + Catalog + </span> + </a> + </li> + <li *roles="['ROLE_SYSTEM_ADMIN']" [routerLinkActiveOptions]="{exact:true}" [routerLinkActive]="['active']" [ngClass]="{'collapsed': isCollapsed}"> + <a style="display: flex; align-items: center;" [routerLink]="['/admin/configuration']"> + <i class="pi pi-cog" style="margin-right:10px; font-size: 15px" title="Settings"></i> + <span *ngIf="!isCollapsed"> + Settings + </span> + </a> + </li> + <li *roles="['ROLE_SYSTEM_ADMIN']" [routerLinkActiveOptions]="{exact:true}" [routerLinkActive]="['active']" [ngClass]="{'collapsed': isCollapsed}"> + <a style="display: flex; align-items: center;" [routerLink]="['/admin/languages']"> + <i class="pi pi-tags" style="margin-right:10px; font-size: 15px" title=" {{'NAVBAR.LANGUAGES' | translate }}"></i> + <span *ngIf="!isCollapsed"> + {{'NAVBAR.LANGUAGES' | translate }} + </span> + </a> + </li> + <li *roles="['ROLE_SYSTEM_ADMIN', 'ROLE_OPERATOR']" [routerLinkActiveOptions]="{exact:true}" [routerLinkActive]="['active']" [ngClass]="{'collapsed': isCollapsed}"> + <a style="display: flex; align-items: center;" [routerLink]="['/admin/monitor']"> + <i class="pi pi-chart-line" style="margin-right:10px; font-size: 15px" title="{{ 'NAVBAR.MONITOR' | translate }}"></i> + <span *ngIf="!isCollapsed"> + {{ 'NAVBAR.MONITOR' | translate }} + </span> + </a> + </li> + <li *roles="['ROLE_SYSTEM_ADMIN']" (click)="showNotificationModal()" [ngClass]="{'collapsed': isCollapsed}"> + <i class="pi pi-send" style="margin-right:10px; font-size: 15px" title="{{ 'NAVBAR.ALL_USERS' | translate }}"></i> + <span *ngIf="!isCollapsed">{{ 'NAVBAR.ALL_USERS' | translate }}</span> + </li> + + </ul> + </div> + <div class="menu flex"> + <p-menu #menu [model]="items" [popup]="true" class="test" [ngClass]="{'collapsed-user': isCollapsed}"/> + <p-button (onClick)="menu.toggle($event)" class="user-button"><i class="pi pi-user" style="font-size: 13px; margin-right:10px"[ngClass]="{'collapsed': isCollapsed}" title="User"></i> + <span *ngIf="!isCollapsed"> + User + </span> + </p-button> + <div style="margin-top:10px" [routerLink]="['admin/dashboard']"> + <p-button *ngIf="!toggleAdmin" (onClick)="adminPanel()" ><i class="pi pi-sign-in" style="margin-right:10px; font-size: 15px"[ngClass]="{'collapsed': isCollapsed}" title=" Go to admin panel"></i> + <span *ngIf="!isCollapsed"> + Go to admin panel + </span> + </p-button> + </div> + <div [routerLink]="['/']" > + <p-button *ngIf="toggleAdmin" (onClick)="adminPanel()" ><i class="pi pi-sign-in" style="margin-right:10px; font-size: 15px"[ngClass]="{'collapsed': isCollapsed}" title="Go to user panel"></i> + <span *ngIf="!isCollapsed"> + Go to user panel + </span> + </p-button> + </div> + </div> +</div> +<app-modal-notification-send></app-modal-notification-send> diff --git a/src/app/shared/left-menu/left-menu.component.spec.ts b/src/app/shared/left-menu/left-menu.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..320e5ed342b010cabd35a5156b469321d75f65f5 --- /dev/null +++ b/src/app/shared/left-menu/left-menu.component.spec.ts @@ -0,0 +1,30 @@ +// import { ComponentFixture, TestBed } from '@angular/core/testing'; + +// import { LeftMenuComponent } from './left-menu.component'; +// import { CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA } from '@angular/core'; +// import { MessageService } from 'primeng/api'; + +// describe('LeftMenuComponent', () => { +// let component: LeftMenuComponent; +// let fixture: ComponentFixture<LeftMenuComponent>; + +// beforeEach(async () => { +// await TestBed.configureTestingModule({ +// imports: [LeftMenuComponent], +// providers: [MessageService], +// schemas: [ +// CUSTOM_ELEMENTS_SCHEMA, +// NO_ERRORS_SCHEMA +// ] +// }) +// .compileComponents(); + +// fixture = TestBed.createComponent(LeftMenuComponent); +// component = fixture.componentInstance; +// fixture.detectChanges(); +// }); + +// it('should create', () => { +// expect(component).toBeTruthy(); +// }); +// }); diff --git a/src/app/shared/left-menu/left-menu.component.ts b/src/app/shared/left-menu/left-menu.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..84ae20e7257e52be870f5f64f9edda7fbc7362c6 --- /dev/null +++ b/src/app/shared/left-menu/left-menu.component.ts @@ -0,0 +1,73 @@ +import {Component, OnInit, ViewChild} from '@angular/core'; +import { ToastContainerComponent, ToastMode } from '../toast-container/toast-container.component'; +import {ActivatedRoute, NavigationEnd, Router} from '@angular/router'; +import {MenuItem} from 'primeng/api'; +import {ModalNotificationSendComponent} from '../modal/modal-notification-send/modal-notification-send.component'; + +@Component({ + selector: 'app-left-menu', + templateUrl: './left-menu.component.html', + styleUrl: './left-menu.component.css' +}) +export class LeftMenuComponent implements OnInit { + @ViewChild(ModalNotificationSendComponent, {static: true}) + public notificationModal; + + items: MenuItem[]; + toggleAdmin = false; + currentUrl : string ; + isCollapsed = false; + + constructor(private toast: ToastContainerComponent, + public router: Router, + private readonly activeRoute: ActivatedRoute,) { + this.items = [ + { + label: 'Profile', + routerLink: ['/profile'] + }, + { + label: 'About', + routerLink: ['/about'] + }, + { + label: 'Logout', + routerLink: ['/logout'] + } + ] + const storedState = sessionStorage.getItem('menuCollapsed'); + this.isCollapsed = storedState === 'true'; + } + + public ngOnInit(): void { + this.router.events.subscribe(event => { + if (event instanceof NavigationEnd) { + this.currentUrl = event.urlAfterRedirects; + console.log('Aktualny URL:', this.currentUrl); + if(this.currentUrl.includes('admin')) { + this.toggleAdmin = true; + } + } + }) + console.log("test left menu ") + const newWidth = this.isCollapsed ? '100px' : '300px'; + document.documentElement.style.setProperty('--left-panel-width', newWidth); + } + + public showToastTest() { + this.toast.show("Test test", ToastMode.DANGER, "HEADER") + } + adminPanel() { + this.toggleAdmin = !this.toggleAdmin; + } + toggleMenu() { + this.isCollapsed = !this.isCollapsed; + const newWidth = this.isCollapsed ? '100px' : '300px'; + document.documentElement.style.setProperty('--left-panel-width', newWidth); + sessionStorage.setItem('menuCollapsed', this.isCollapsed.toString()); + } + public showNotificationModal(): void { + this.notificationModal.show(); + } + +} diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index ae6442758d5d7eed07600cb1ddacc71b8535d573..a89b1a9a0d66e4d1fad0165fd37d1d8c5c0e7a94 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -61,10 +61,15 @@ import {InputTextModule} from 'primeng/inputtext'; import { DomainNamespaceAnnotationsComponent } from './domain-namespace-annotations/domain-namespace-annotations.component'; import { provideZxvbnServiceForPSM } from 'angular-password-strength-meter/zxcvbn'; import { AccessTokensComponent } from './users/access-token/access-tokens.component'; +import { LeftMenuComponent } from './left-menu/left-menu.component'; +import {TableModule} from 'primeng/table'; +import { AdminDashboardComponent } from './admin-dashboard/admin-dashboard.component'; +import {CheckboxModule} from 'primeng/checkbox'; import { InputGroupModule } from 'primeng/inputgroup'; import { InputGroupAddonModule } from 'primeng/inputgroupaddon'; import { ButtonModule } from 'primeng/button'; import { BrowserModule } from '@angular/platform-browser'; +import {ChartModule} from 'primeng/chart'; import { RolesExcludedDirective } from '../directive/roles-exluded.directive'; @@ -84,10 +89,14 @@ import { RolesExcludedDirective } from '../directive/roles-exluded.directive'; DropdownModule, InputTextModule, FormioModule, + TableModule, + CheckboxModule, InputGroupModule, InputGroupAddonModule, ButtonModule, - RecaptchaV3Module + RecaptchaV3Module, + ButtonModule, + ChartModule ], declarations: [ RateComponent, @@ -134,7 +143,8 @@ import { RolesExcludedDirective } from '../directive/roles-exluded.directive'; PreferencesComponent, SortableHeaderDirective, DomainNamespaceAnnotationsComponent, - AccessTokensComponent + AccessTokensComponent, + AdminDashboardComponent ], providers: [ PasswordValidator, diff --git a/src/app/shared/toast-container/toast-container.component.html b/src/app/shared/toast-container/toast-container.component.html new file mode 100644 index 0000000000000000000000000000000000000000..1c81a5a91a8fe0e3d9c45234347efec6c40c71eb --- /dev/null +++ b/src/app/shared/toast-container/toast-container.component.html @@ -0,0 +1,79 @@ + <p-toast position="bottom-right" key="success" [baseZIndex]="5000" preventOpenDuplicates="true" > + <ng-template let-message pTemplate="message" > + <div class="flex flex-row" *ngIf="message.severity === 'success'"> + <div class="flex flex-shrink-1 mt-2 mr-4 ml-2 mb-2"> + <!-- <img src="../../../assets/icons/toast/toast-success.svg" class="" [alt]="'ALT.TOAST_SUCCESS' | translate"> --> + </div> + <div class="flex" style="flex: 1"> + <div class="flex flex-column flex-grow-1 justify-content-center align-content-center "> + <div class="flex"> + <h3 class="p-toast-message-success text-left mb-1 mt-1"> {{message.summary | translate}}</h3> + </div> + <div class="flex mb-3"> + <small class="p-toast-message-success"> {{message.detail | translate}}</small> + </div> + </div> + </div> + </div> + </ng-template> + </p-toast> + +<p-toast position="bottom-right" key="info" [baseZIndex]="5000" preventOpenDuplicates="true"> + <ng-template let-message pTemplate="message" > + <div class="flex flex-row" *ngIf="message.severity === 'info'"> + <div class="flex flex-shrink-1 mt-2 mr-4 ml-2 mb-2"> + <!-- <img src="assets/icons/toast/toast-info.svg" class="" [alt]="'ALT.TOAST_INFORMATION'| translate"> --> + </div> + <div class="flex" style="flex: 1"> + <div class="flex flex-column flex-grow-1 justify-content-center align-content-center "> + <div class="flex"> + <h3 class="p-toast-message-info text-left mb-1 mt-1"> {{message.summary | translate}}</h3> + </div> + <div class="flex mb-1"> + <small class="p-toast-message-info"> {{message.detail | translate}}</small> + </div> + </div> + </div> + </div> + </ng-template> +</p-toast> + +<p-toast position="bottom-right" key="warn" [baseZIndex]="5000" preventOpenDuplicates="true"> + <ng-template let-message pTemplate="message"> + <div class="flex flex-row" *ngIf="message.severity === 'warn'"> + <div class="flex flex-shrink-1 mt-2 mr-4 ml-2 mb-2"> + <!-- <img src="assets/icons/toast/toast-warning.svg" class="" [alt]="'ALT.TOAST_WARNING' | translate"> --> + </div> + <div class="flex" style="flex: 1"> + <div class="flex flex-column flex-grow-1 justify-content-center align-content-center "> + <div class="flex"> + <h3 class="p-toast-message-warn text-left mb-1 mt-1"> {{message.summary | translate}}</h3> + </div> + <div class="flex mb-1"> + <small class="p-toast-message-warn"> {{message.detail | translate}}</small> + </div> + </div> + </div> + </div> + </ng-template> +</p-toast> + +<p-toast position="bottom-right" key="error" [baseZIndex]="5000" preventOpenDuplicates="true" > + <ng-template let-message pTemplate="message"> + <div class="flex flex-row" *ngIf="message.severity === 'error'"> + <div class="flex flex-shrink-1 mt-2 mr-4 ml-2 mb-2"> + <!-- <img src="assets/icons/toast/toast-error.svg" class="" [alt]="'ALT.ERROR' | translate"> --> + </div> + <div class="flex" style="flex: 1"> + <div class="flex flex-column flex-grow-1 justify-content-center align-content-center "> + <div class="flex"> + <h3 class="p-toast-message-error text-left mb-1 mt-1"> {{message.summary | translate}}</h3> + </div> + <div class="flex mb-1"> + <small class="p-toast-message-error"> {{message.detail | translate}}</small> + </div> + </div> + </div> + </div> + </ng-template> +</p-toast> diff --git a/src/app/shared/toast-container/toast-container.component.scss b/src/app/shared/toast-container/toast-container.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..25969c80a79a3aa61ab2779e390ac1e78d82ab48 --- /dev/null +++ b/src/app/shared/toast-container/toast-container.component.scss @@ -0,0 +1,37 @@ +:host ::ng-deep .p-toast { + margin-bottom: 40px; + margin-right: 10px; + z-index: 999; + width: 20vw !important; +} + + +:host ::ng-deep .p-toast .p-toast-message { + //margin-top: 0; + //padding-top: 0; + //position: absolute; + //bottom: 60px; + right: 10px; + width: 20vw; + border-radius: 12px; +} + +:host ::ng-deep .p-toast .p-toast-message .p-toast-message-content .p-toast-message-text { + margin: 0; +} + +:host ::ng-deep .p-toast .p-toast-message .p-toast-message-content { + padding: 0; + border-width: 0; + text-align: left; +} + +:host ::ng-deep .p-toast .p-toast-message .p-toast-icon-close { + width: 2rem; + height: 2rem; + border-radius: 50%; + background: transparent; + position: absolute; + right: 3px; + transition: background-color 0.2s, color 0.2s, box-shadow 0.2s; +} diff --git a/src/app/shared/toast-container/toast-container.component.spec.ts b/src/app/shared/toast-container/toast-container.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..d33ae354a3d9e0ba95f29432b68ac7f2045bc5b9 --- /dev/null +++ b/src/app/shared/toast-container/toast-container.component.spec.ts @@ -0,0 +1,43 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ToastContainerComponent } from './toast-container.component'; +import {TranslateFakeLoader, TranslateLoader, TranslateModule} from '@ngx-translate/core'; +import {MessageService} from "primeng/api"; +import { CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA } from '@angular/core'; + +describe('ToastContainerComponent', () => { + let component: ToastContainerComponent; + let fixture: ComponentFixture<ToastContainerComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ToastContainerComponent ], + imports: [ + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: TranslateFakeLoader, + } + }) + ], + providers: [ + MessageService + ], + schemas: [ + CUSTOM_ELEMENTS_SCHEMA, + NO_ERRORS_SCHEMA + ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ToastContainerComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/shared/toast-container/toast-container.component.ts b/src/app/shared/toast-container/toast-container.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..649fbf93f23b940005fd7913d5f934a419aede16 --- /dev/null +++ b/src/app/shared/toast-container/toast-container.component.ts @@ -0,0 +1,46 @@ +import {Component, Injectable} from '@angular/core'; +import {MessageService} from "primeng/api"; + +export enum ToastMode { + SUCCESS = 'success', + DANGER = 'error', + NORMAL = 'info', + WARN = 'warn' +} + +export interface Toast { + text?: string; + header?: string; + delay?: number; + mode?: ToastMode; +} + +@Component({ + selector: 'app-toast-container', + templateUrl: './toast-container.component.html', + styleUrls: ['./toast-container.component.scss'], +}) +@Injectable( {providedIn: 'root'}) +export class ToastContainerComponent{ + + public ToastMode = ToastMode; + + public readonly defaultDelay = 3000; + + constructor(private readonly messageService: MessageService){ + } + + show(text: string, mode: ToastMode = ToastMode.NORMAL, header?: string, delay?: number): void { + console.log("Toast shown.") + this.messageService.add({severity: mode, summary: header, detail: text, life: delay, key: mode}) + } + + push(toast: Toast): void { + this.messageService.add({severity: toast.mode, summary: toast.header, detail: toast.text, life: toast.delay, key: toast.mode}) + } + + remove(): void { + this.messageService.clear(); + } + +} diff --git a/src/app/shared/users/access-token/access-tokens.component.html b/src/app/shared/users/access-token/access-tokens.component.html index 6ad21e0418c541ca58a6a394c32520b19778483a..81bd13e6bfcca04309f38b2a289c687c08046962 100644 --- a/src/app/shared/users/access-token/access-tokens.component.html +++ b/src/app/shared/users/access-token/access-tokens.component.html @@ -1,5 +1,9 @@ -<div style="margin-bottom: 15px;" class="panel panel-default"> - <div class="panel-heading">{{'TOKENS.HEADER' | translate}}</div> +<div style="padding-bottom: 15px;" class="background-section"> + <div style="display: flex; justify-content:space-between"> + <h4 style="font-size:15px; font-weight: bold">{{'TOKENS.HEADER' | translate}}</h4> + <button type="button" class="btn btn-text" + (click)="modal.show()">{{'TOKENS.NEW_TOKEN' | translate}}</button> + </div> <div class="panel-body"> <table class="table table-hover" aria-describedby="User access tokens table"> <thead> @@ -29,11 +33,7 @@ </tr> </tbody> </table> - <div> - <button type="button" class="btn btn-success" - (click)="modal.show()">{{'TOKENS.NEW_TOKEN' | translate}}</button> - </div> </div> </div> @@ -54,10 +54,14 @@ <div *ngIf="name.errors.notUnique">{{name.errors.message}}</div> </div> - <input type="submit" class="btn btn-success" value="{{'SSH_KEYS.MODAL.BUTTON_ADD' | translate}}" - [disabled]="!requestForm.valid"> - <button type="button" class="btn btn-primary pull-right" - (click)="modal.hide()">{{'SSH_KEYS.MODAL.BUTTON_CANCEL' | translate}}</button> + <div style="display: flex; justify-content: flex-end"> + <button type="button" class="btn btn-secondary mr-2" + (click)="modal.hide()">{{'SSH_KEYS.MODAL.BUTTON_CANCEL' | translate}}</button> + <input type="submit" class="btn btn-primary" value="{{'SSH_KEYS.MODAL.BUTTON_ADD' | translate}}" + [disabled]="!requestForm.valid"> + </div> + + </form> <div *ngIf="showCopyToken"> @@ -75,4 +79,4 @@ </div> </div> </div> -</nmaas-modal> \ No newline at end of file +</nmaas-modal> diff --git a/src/app/shared/users/details/userdetails.component.css b/src/app/shared/users/details/userdetails.component.css index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..6a1915d3beeb79e4c88933bc59151fe12e530fc1 100644 --- a/src/app/shared/users/details/userdetails.component.css +++ b/src/app/shared/users/details/userdetails.component.css @@ -0,0 +1,5 @@ +.form-control[disabled], fieldset[disabled] .form-control{ + background: transparent; + border:none; + box-shadow: none; +} diff --git a/src/app/shared/users/details/userdetails.component.html b/src/app/shared/users/details/userdetails.component.html index 98a4227aabc594f41570935d5b90fc1f2ccdc115..85a5820a307b424ab9b1704bb93ee845361400ba 100644 --- a/src/app/shared/users/details/userdetails.component.html +++ b/src/app/shared/users/details/userdetails.component.html @@ -1,22 +1,26 @@ -<div style="padding-bottom: 15px;" class="panel panel-default"> - <div class="panel-heading">{{'USER_DETAILS.HEADER' | translate}}</div> +<div style="padding-bottom: 15px;" class="background-section"> + <div class="" style="display: flex; justify-content:space-between"> + <h4 style="font-size:15px; font-weight: bold"> + {{'USER_DETAILS.HEADER' | translate}} + </h4> + + <button *ngIf="isInMode(ComponentMode.VIEW) && isModeAllowed(ComponentMode.EDIT) && authService.hasRole('ROLE_SYSTEM_ADMIN')" type="button" + class="btn btn-text" (click)="onModeChange()">{{'USER_DETAILS.EDIT_BUTTON' | translate}}</button> + </div> <div class="panel-body"> <form *ngIf="user" (submit)="submit()" class="form-horizontal" #userDetailsForm="ngForm"> - <div class="flex justify-content-end mb-4"> - <button *ngIf="!isInMode(ComponentMode.VIEW)" type="button" - class="btn btn-text" (click)="onModeChange()">{{'USER_DETAILS.VIEW_BUTTON' | translate}}</button> - <button *ngIf="!isInMode(ComponentMode.VIEW)" type="submit" [disabled]="userDetailsForm.invalid" - class="btn btn-primary">{{'USER_DETAILS.SUBMIT_BUTTON' | translate}}</button> - <button *ngIf="isInMode(ComponentMode.VIEW) && isModeAllowed(ComponentMode.EDIT) && authService.hasRole('ROLE_SYSTEM_ADMIN')" type="button" - class="btn btn-text" (click)="onModeChange()">{{'USER_DETAILS.EDIT_BUTTON' | translate}}</button> - </div> - +<!-- <div class="flex justify-content-end mb-4">--> +<!-- <button *ngIf="!isInMode(ComponentMode.VIEW)" type="button"--> +<!-- class="btn btn-text" (click)="onModeChange()">{{'USER_DETAILS.VIEW_BUTTON' | translate}}</button>--> +<!-- <button *ngIf="!isInMode(ComponentMode.VIEW)" type="submit" [disabled]="userDetailsForm.invalid"--> +<!-- class="btn btn-primary">{{'USER_DETAILS.SUBMIT_BUTTON' | translate}}</button>--> +<!-- </div>--> <div class="form-group"> <label class="col-sm-2 control-label">{{'USER_DETAILS.USERNAME' | translate}}</label> <div class="col-sm-10"> - <p class="form-control-static">{{user.username}}</p> + <p class="form-control-static" style="padding-left:12px">{{user.username}}</p> </div> </div> @@ -50,10 +54,22 @@ <div *ngIf="user?.id && authService.hasRole('ROLE_SYSTEM_ADMIN')" class="form-group"> <label class="col-sm-2 control-label">{{'USER_DETAILS.ID' | translate}}</label> <div class="col-sm-10"> - <p class="form-control-static">{{user?.id}}</p> + <p style="padding-left:12px" class="form-control-static">{{user?.id}}</p> </div> </div> - <button (click)="passwordModal.show()" class="btn btn-secondary" *ngIf="canChangePassword() && !user.ssoUser" style="float: right;">{{'USER_DETAILS.CHANGE_PASSWORD_BUTTON' | translate}}</button> + <div class="form-group"> + <label class="col-sm-2 control-label">Password</label> + <div class="col-sm-10"> + <button (click)="passwordModal.show()" class="btn btn-text" *ngIf="canChangePassword() && !user.ssoUser" + >{{'USER_DETAILS.CHANGE_PASSWORD_BUTTON' | translate}}</button> + </div> + </div> + <div class="flex justify-content-end mb-4"> + <button *ngIf="!isInMode(ComponentMode.VIEW)" type="button" style="margin-right:10px" + class="btn btn-secondary" (click)="onModeChange()">{{'USER_DETAILS.VIEW_BUTTON' | translate}}</button> + <button *ngIf="!isInMode(ComponentMode.VIEW)" type="submit" [disabled]="userDetailsForm.invalid" + class="btn btn-primary">{{'USER_DETAILS.SUBMIT_BUTTON' | translate}}</button> + </div> </form> <br> <div class="alert alert-danger" *ngIf="errorMessage"> diff --git a/src/app/shared/users/list/userslist.component.css b/src/app/shared/users/list/userslist.component.css index 0553eee556dcb4bd4a017d4aab30e7d9827e3098..ded3a5ff710dfbf80e9c7d86fbcaa91cefc32731 100644 --- a/src/app/shared/users/list/userslist.component.css +++ b/src/app/shared/users/list/userslist.component.css @@ -13,9 +13,9 @@ tr.clickable { cursor: pointer; } -.dropdown:hover .dropdown-menu { - display: block; -} +/*.dropdown:hover .dropdown-menu {*/ +/* display: block;*/ +/*}*/ .align-vertically { display: flex; @@ -23,10 +23,6 @@ tr.clickable { align-items: center; } -:host ::ng-deep .p-dropdown { - width: 170px; -} - .space-between { display: flex; justify-content: space-between; @@ -36,3 +32,56 @@ li::marker { content: ''; font-size: 0em; } +:host ::ng-deep .p-datatable .p-datatable-thead > tr > th{ + border: 1px solid #E0E2E5; + background:transparent; + border-width: 0 0 1px 0; +} +:host ::ng-deep .p-datatable .p-datatable-tbody > tr > td { + text-align: left; + border: 1px solid #E0E2E5; + border-width: 0 0 1px 0; + padding: 1rem 1rem; +} +:host ::ng-deep .p-datatable .p-paginator-bottom{ + height: 40px; + background: transparent; + border: none; + margin-top:10px; +} +:host ::ng-deep .p-datatable .p-datatable-tbody > tr{ + background: transparent; +} + +:host ::ng-deep .p-paginator .p-paginator-pages .p-paginator-page{ + transition: unset; + border-radius: 50%; + min-width:3.5rem; + height:3.5rem; + margin:0 5px; + font-size: 14px; +} + +:host ::ng-deep .p-paginator-element{ + border-radius:50%; + margin:0 5px; + min-width:3.5rem; + height:3.5rem; + font-size: 14px; +} +:host ::ng-deep .p-paginator .p-dropdown{ + height:3rem; +} +:host ::ng-deep .p-paginator-icon{ + height: 1.5rem; + width: 1.5rem; +} +:host ::ng-deep .p-paginator .p-dropdown .p-dropdown-label{ + padding-right: 10px; +} +:host ::ng-deep .p-paginator .p-paginator-pages .p-paginator-page.p-highlight{ + background: var(--user-button-background-hover); +} +:host ::ng-deep .p-datatable>.p-datatable-wrapper { + overflow: visible; +} diff --git a/src/app/shared/users/list/userslist.component.html b/src/app/shared/users/list/userslist.component.html index ab667693d70a8c9238af517b063e3cea502bbca0..669dc950d1e3a5318906bc23c8f64c019f3c62b3 100644 --- a/src/app/shared/users/list/userslist.component.html +++ b/src/app/shared/users/list/userslist.component.html @@ -1,4 +1,4 @@ -<div class="col-sm-12 col-sm-10 col-md-12"> +<!-- <div class="col-sm-12 col-sm-10 col-md-12"> <h3> {{ 'USERS.TITLE' | translate }}</h3> <div class="flex space-between"> @@ -26,6 +26,24 @@ </ul> </span> </div> +<!-- <div *ngIf="isModeAllowed(ComponentMode.DELETE)" class="flex ">--> +<!-- <span class="mt-2 pr-1">{{ 'USERS.ITEMS_PER_PAGE' | translate }}:</span>--> +<!-- <span id="selectionItems" class="dropdown"--> +<!-- style="vertical-align: middle; display: inline-block; margin-right: 1rem;">--> +<!-- <button class="dropdown-toggle btn" data-toggle="dropdown" data-close-others="true">--> +<!-- {{maxItemsOnPage}}--> +<!-- </button>--> +<!-- <ul class="dropdown-menu">--> +<!-- <li *ngFor="let item of itemsPerPage" [ngClass]="{'active': maxItemsOnPage == item}">--> +<!-- <a (click)="setItems(item)">--> +<!-- <span>{{item.toString()}}</span>--> +<!-- </a>--> +<!-- </li>--> +<!-- </ul>--> +<!-- </span>--> +<!-- </div>--> +<div class="" style="display: flex"> + <div *ngIf="isModeAllowed(ComponentMode.EDIT)" style="margin-right: 15px; padding-top: 5px;"> {{'USERS.SEARCH' | translate}}</div> @@ -33,160 +51,304 @@ <input pInputText name="searchTextDomain" id="searchTextDomain" placeholder="Search" type="text" (keyup)="searchUsers($event.target.value)"> </div> - <div *ngIf="isModeAllowed(ComponentMode.DELETE)" class="flex"> - <input pInputText name="searchText" id="searchText" placeholder="Search" type="text" + <div *ngIf="isModeAllowed(ComponentMode.DELETE)" class="flex" style="margin-right:20px"> + <span class="p-input-icon-right" style="width: 100%"> + <i class="pi pi-search" style="font-size: 13px; top: 16px; margin-right: 5px;"></i> + <input pInputText name="searchText" id="searchText" placeholder="Search" type="text" class="form-control" (keyup)="onSearch($event.target.value)"> + </span> </div> - </div> + <button *ngIf="authService.hasDomainRole(domainId, 'ROLE_DOMAIN_ADMIN') || authService.hasDomainRole(domainId, 'ROLE_GROUP_DOMAIN_ADMIN')" + class="btn btn-primary" (click)="changeMode()"> + <span *ngIf="isModeAllowed(ComponentMode.DELETE)" >{{'USERS.ADD_TO_DOMAIN_BUTTON' | translate}}</span> + <span *ngIf="isModeAllowed(ComponentMode.EDIT)">{{'USERS.GO_BACK_BUTTON' | translate}}</span> + </button> + </div> + <h4 class="header"> {{ 'USERS.TITLE' | translate }}</h4> + <div class="background-section"> + <p-table #dt [value]="displayUsers" [paginator]="true" [rows]="maxItemsOnPage" + [rowsPerPageOptions]="[15, 20, 25, 30, 50]" > + <ng-template pTemplate="header"> + <tr> + <th scope="col" class="column-sortable" sortable-column="username" + sort-direction="asc">{{ 'USERS.USER_NAME' | translate }}</th> + <th scope="col" class="column-sortable" sortable-column="lastname">{{'USERS.NAME' | translate}}</th> + <th *ngIf="!domainMode" scope="col" class="column-sortable" + sortable-column="email">{{'USERS.EMAIL' | translate}}</th> + <th scope="col" class="column-sortable" sortable-column="domains" + *ngIf="domainId === domainService.getGlobalDomainId()">{{ 'USERS.DOMAINS' | translate }}</th> + <th scope="col" class="column-sortable" sortable-column="globalRole" + *ngIf="domainId === domainService.getGlobalDomainId()">{{ 'USERS.GLOBAL_ROLE' | translate }}</th> + <th scope="col" class="column-sortable" sortable-column="roles" + *ngIf="domainId !== domainService.getGlobalDomainId() && !isModeAllowed(ComponentMode.EDIT)">{{ 'USERS.ROLES' | translate }}</th> + <th scope="col" class="column-sortable" *ngIf="!isModeAllowed(ComponentMode.EDIT) && !domainMode" + sortable-column="firstLoginDate">{{ 'USERS.FIRST_LOGIN' | translate }}</th> + <th scope="col" class="column-sortable" *ngIf="!isModeAllowed(ComponentMode.EDIT) && !domainMode" + sortable-column="lastSuccessfulLoginDate">{{ 'USERS.LAST_SUCCESSFUL_LOGIN' | translate }}</th> + <th scope="col" class="column-sortable" sortable-column="enabled">{{ 'USERS.ENABLED' | translate }}</th> + <th *ngIf="!isModeAllowed(ComponentMode.EDIT)" scope="col"> </th> + <th *ngIf="isModeAllowed(ComponentMode.EDIT)" scope="col"> </th> + </tr> + </ng-template> + <ng-template pTemplate="body" let-user> + <tr> + <td (click)="view(user.id)">{{ user.username }}</td> + <td>{{(user.firstname || '') + ' ' + (user.lastname || '')}}</td> + <td *ngIf="!domainMode">{{user.email}}</td> + <td *ngIf="domainId === domainService.getGlobalDomainId()"> + <span *ngFor="let role of filterDomainNames(user); let isLast = last"> + {{ getDomainName(role.domainId) | async }}{{ !isLast ? ', ' : '' }} + </span> + </td> + <td *ngIf="domainId === domainService.getGlobalDomainId()"> + <span>{{"ENUM.USER_ROLES." + getGlobalRole(user).toUpperCase() | translate}}</span> + </td> + <td *ngIf="domainId !== domainService.getGlobalDomainId() && !isModeAllowed(ComponentMode.EDIT)"> + <div *roles="['ROLE_DOMAIN_ADMIN'];excluded:['ROLE_SYSTEM_ADMIN']"> + <div *ngIf="!checkUserIfIsCurrentUser(user.username)"> + <li [routerLinkActiveOptions]="{exact:true}" + [routerLinkActive]="['active']" class="dropdown dropdown-domains"> + <a aria-expanded="false" data-close-others="true" aria-haspopup="true" + class="dropdown-toggle" data-toggle="dropdown" + role="button"> + <span style="color: black;">{{"ENUM.USER_ROLES." + getOnlyDomainRoles(user)?.role | translate}} + <strong class="caret"></strong> + </span> + </a> + <ul class="dropdown-menu" style="left: 0; right:unset;"> + <li *ngFor="let role of getAllowedRoles()"> + <a (click)="changeUserRole(user,domainId, {value:role})">{{"ENUM.USER_ROLES." + Role[role].toUpperCase() | translate}}</a> + </li> + </ul> + </li> + </div> + <div *ngIf="checkUserIfIsCurrentUser(user.username)"> + <span> + {{"ENUM.USER_ROLES." + getOnlyDomainRoles(user)?.role | translate}} + </span> + </div> + </div> + <div *roles="['ROLE_SYSTEM_ADMIN']"> + <div> + {{"ENUM.USER_ROLES." + getOnlyDomainRoles(user)?.role | translate}} + </div> + </div> + </td> + <td *ngIf="!isModeAllowed(ComponentMode.EDIT) && !domainMode"> + {{ user.firstLoginDate | date:'dd-MM-yyyy HH:mm' }} + </td> + <td *ngIf="!isModeAllowed(ComponentMode.EDIT) && !domainMode"> + {{ user.lastSuccessfulLoginDate | date:'dd-MM-yyyy HH:mm' }} + </td> + <td> + <i class="pi" [ngClass]="{'pi-check': user?.enabled, 'pi-times': !user?.enabled}"></i> + </td> + <td *ngIf="!domainMode && !isModeAllowed(ComponentMode.EDIT)"> + <span class="dropdown"> + <a style="display: inline-block" class="dropdown-toggle " aria-expanded="false" + aria-haspopup="true" + data-toggle="dropdown" href="#" role="button"> + <em class="pi pi-cog" style="font-size: 1.8rem; color: var(--l-text-color)"></em> + </a> + <ul class="dropdown-menu pull-right-drop"> + <li *ngIf="isModeAllowed(ComponentMode.VIEW)"> + <a (click)="view(user.id);$event.stopPropagation()"> + {{ 'USERS.DETAILS_BUTTON' | translate }} + </a> + </li> + <li *ngIf="user.enabled && authService.hasRole('ROLE_SYSTEM_ADMIN') && user.username!==authService.getUsername()"> + <a (click)="changeUserStatus(user, false);$event.stopPropagation()"> <!--user should not be able to disable himself --> + {{ 'USERS.DISABLE_BUTTON' | translate }} + </a> + </li> + <li *ngIf="!user.enabled && authService.hasRole('ROLE_SYSTEM_ADMIN')"> + <a (click)="changeUserStatus(user, true);$event.stopPropagation()"> + {{ 'USERS.ENABLE_BUTTON' | translate }} + </a> + </li> + <li *ngIf="isModeAllowed(ComponentMode.EDIT) && + domainId != domainService.getGlobalDomainId() && + isGlobalGuestAndHasNoRoleInThisDomain(user)"> + <a (click)="addToCurrentDomain(user);$event.stopPropagation()"> + {{'USERS.GRANT_USER_ROLE_IN_CURRENT_DOMAIN_BUTTON' | translate}} + </a> + </li> + <li *ngIf="isModeAllowed(ComponentMode.DELETE) && domainId != domainService.getGlobalDomainId() && user.username!==authService.getUsername()"> + <a role="button" (click)="onRemoveFromDomain.emit(user);$event.stopPropagation()"> + {{ 'USERS.REMOVE_FROM_DOMAIN_BUTTON' | translate }} + </a> + </li> + <li *ngIf="isModeAllowed(ComponentMode.DELETE) && authService.hasRole('ROLE_SYSTEM_ADMIN') && user.username!==authService.getUsername() && canUserBeDeleted(user)"> + <a role="button" (click)="onDelete.emit(user);$event.stopPropagation()"> + {{ 'USERS.DELETE_BUTTON' | translate }} + </a> + </li> + </ul> + </span> + </td> + <td *ngIf="domainMode && !isModeAllowed(ComponentMode.EDIT)"> + <span *ngIf="!checkUserIfIsCurrentUser(user.username)"> + <a style="display: inline-block" class="" aria-expanded="false" + aria-haspopup="true" role="button" + (click)="onRemoveFromDomain.emit(user);$event.stopPropagation()"> + <em class="fas fa-trash icon-black "></em> + </a> + </span> + </td> + <td *ngIf="isModeAllowed(ComponentMode.EDIT)" style="width: 170px"> + <button class="btn btn-secondary" role="button" + (click)="addToCurrentDomain(user)"> {{'USERS.ADD_TO_DOMAIN_BUTTON' | translate}}</button> + </td> + </tr> + </ng-template> + </p-table> </div> +<!-- <table class="table table-hover table-condensed" sortable-table (sorted)="onSorted($event)"--> +<!-- aria-describedby="Users list" style="margin-top: 15px">--> +<!-- <thead>--> +<!-- <tr>--> +<!-- <th scope="col" class="column-sortable" sortable-column="username"--> +<!-- sort-direction="asc">{{ 'USERS.USER_NAME' | translate }}</th>--> +<!-- <th scope="col" class="column-sortable" sortable-column="lastname">{{'USERS.NAME' | translate}}</th>--> +<!-- <th *ngIf="!domainMode" scope="col" class="column-sortable"--> +<!-- sortable-column="email">{{'USERS.EMAIL' | translate}}</th>--> +<!-- <th scope="col" class="column-sortable" sortable-column="domains"--> +<!-- *ngIf="domainId === domainService.getGlobalDomainId()">{{ 'USERS.DOMAINS' | translate }}</th>--> +<!-- <th scope="col" class="column-sortable" sortable-column="globalRole"--> +<!-- *ngIf="domainId === domainService.getGlobalDomainId()">{{ 'USERS.GLOBAL_ROLE' | translate }}</th>--> +<!-- <th scope="col" class="column-sortable" sortable-column="roles"--> +<!-- *ngIf="domainId !== domainService.getGlobalDomainId() && !isModeAllowed(ComponentMode.EDIT)">{{ 'USERS.ROLES' | translate }}</th>--> +<!-- <th scope="col" class="column-sortable" *ngIf="!isModeAllowed(ComponentMode.EDIT) && !domainMode"--> +<!-- sortable-column="firstLoginDate">{{ 'USERS.FIRST_LOGIN' | translate }}</th>--> +<!-- <th scope="col" class="column-sortable" *ngIf="!isModeAllowed(ComponentMode.EDIT) && !domainMode"--> +<!-- sortable-column="lastSuccessfulLoginDate">{{ 'USERS.LAST_SUCCESSFUL_LOGIN' | translate }}</th>--> +<!-- <th scope="col" class="column-sortable" sortable-column="enabled">{{ 'USERS.ENABLED' | translate }}</th>--> +<!-- <th *ngIf="!isModeAllowed(ComponentMode.EDIT)" scope="col"> </th>--> +<!-- <th *ngIf="isModeAllowed(ComponentMode.EDIT)" scope="col"> </th>--> +<!-- </tr>--> +<!-- </thead>--> - <br> - <table class="table table-hover table-condensed" sortable-table (sorted)="onSorted($event)" - aria-describedby="Users list" style="margin-top: 15px"> - <thead> - <tr> - <th scope="col" class="column-sortable" sortable-column="username" - sort-direction="asc">{{ 'USERS.USER_NAME' | translate }}</th> - <th scope="col" class="column-sortable" sortable-column="lastname">{{'USERS.NAME' | translate}}</th> - <th *ngIf="!domainMode" scope="col" class="column-sortable" - sortable-column="email">{{'USERS.EMAIL' | translate}}</th> - <th scope="col" class="column-sortable" sortable-column="domains" - *ngIf="domainId === domainService.getGlobalDomainId()">{{ 'USERS.DOMAINS' | translate }}</th> - <th scope="col" class="column-sortable" sortable-column="globalRole" - *ngIf="domainId === domainService.getGlobalDomainId()">{{ 'USERS.GLOBAL_ROLE' | translate }}</th> - <th scope="col" class="column-sortable" sortable-column="roles" - *ngIf="domainId !== domainService.getGlobalDomainId() && !isModeAllowed(ComponentMode.EDIT)">{{ 'USERS.ROLES' | translate }}</th> - <th scope="col" class="column-sortable" *ngIf="!isModeAllowed(ComponentMode.EDIT) && !domainMode" - sortable-column="firstLoginDate">{{ 'USERS.FIRST_LOGIN' | translate }}</th> - <th scope="col" class="column-sortable" *ngIf="!isModeAllowed(ComponentMode.EDIT) && !domainMode" - sortable-column="lastSuccessfulLoginDate">{{ 'USERS.LAST_SUCCESSFUL_LOGIN' | translate }}</th> - <th scope="col" class="column-sortable" sortable-column="enabled">{{ 'USERS.ENABLED' | translate }}</th> - <th *ngIf="!isModeAllowed(ComponentMode.EDIT)" scope="col"> </th> - <th *ngIf="isModeAllowed(ComponentMode.EDIT)" scope="col"> </th> - </tr> - </thead> +<!-- <tbody>--> +<!-- <tr *ngFor="let user of displayUsers | paginate: {itemsPerPage: maxItemsOnPage, currentPage: pageNumber, id: paginatorName}"--> +<!-- [ngClass]="{'clickable!' : domainMode}" (click)="view(user.id)">--> +<!-- <td>{{user.username}}</td>--> +<!-- <td>{{(user.firstname || '') + ' ' + (user.lastname || '')}}</td>--> +<!-- <td *ngIf="!domainMode">{{user.email}}</td>--> +<!-- <td *ngIf="domainId === domainService.getGlobalDomainId()">--> +<!-- <div *ngFor="let role of filterDomainNames(user); last as isLast">--> +<!-- <span *ngIf="!isLast" style="float:left;padding-right: 3px">--> +<!-- {{getDomainName(role.domainId) | async}},--> +<!-- </span>--> +<!-- <span *ngIf="isLast" style="float:left;padding-right: 3px">--> +<!-- {{getDomainName(role.domainId) | async}}--> +<!-- </span>--> +<!-- </div>--> +<!-- <br>--> +<!-- </td>--> +<!-- <td *ngIf="domainId === domainService.getGlobalDomainId()">--> +<!-- <span>{{"ENUM.USER_ROLES." + getGlobalRole(user).toUpperCase() | translate}}</span>--> +<!-- </td>--> +<!-- <td *ngIf="domainId !== domainService.getGlobalDomainId() && !isModeAllowed(ComponentMode.EDIT)">--> +<!-- <div *roles="['ROLE_DOMAIN_ADMIN'];excluded:['ROLE_SYSTEM_ADMIN']">--> +<!-- <div *ngIf="!checkUserIfIsCurrentUser(user.username)">--> +<!-- <li [routerLinkActiveOptions]="{exact:true}"--> +<!-- [routerLinkActive]="['active']" class="dropdown dropdown-domains">--> +<!-- <a aria-expanded="false" data-close-others="true" aria-haspopup="true"--> +<!-- class="dropdown-toggle" data-toggle="dropdown"--> +<!-- role="button">--> +<!-- <span style="color: black;">{{"ENUM.USER_ROLES." + getOnlyDomainRoles(user)?.role | translate}}--> +<!-- <strong class="caret"></strong></span></a>--> +<!-- <ul class="dropdown-menu">--> +<!-- <li *ngFor="let role of getAllowedRoles()">--> +<!-- <a (click)="changeUserRole(user,domainId, {value:role})">{{"ENUM.USER_ROLES." + Role[role].toUpperCase() | translate}}</a>--> +<!-- </li>--> +<!-- </ul>--> +<!-- </li>--> +<!-- </div>--> +<!-- <div *ngIf="checkUserIfIsCurrentUser(user.username)">--> +<!-- <span>--> +<!-- {{"ENUM.USER_ROLES." + getOnlyDomainRoles(user)?.role | translate}}--> +<!-- </span>--> +<!-- </div>--> +<!-- </div>--> - <tbody> - <tr *ngFor="let user of displayUsers | paginate: {itemsPerPage: maxItemsOnPage, currentPage: pageNumber, id: paginatorName}" - [ngClass]="{'clickable!' : domainMode}" (click)="view(user.id)"> - <td>{{user.username}}</td> - <td>{{(user.firstname || '') + ' ' + (user.lastname || '')}}</td> - <td *ngIf="!domainMode">{{user.email}}</td> - <td *ngIf="domainId === domainService.getGlobalDomainId()"> - <div *ngFor="let role of filterDomainNames(user); last as isLast"> - <span *ngIf="!isLast" style="float:left;padding-right: 3px"> - {{getDomainName(role.domainId) | async}}, - </span> - <span *ngIf="isLast" style="float:left;padding-right: 3px"> - {{getDomainName(role.domainId) | async}} - </span> - </div> - <br> - </td> - <td *ngIf="domainId === domainService.getGlobalDomainId()"> - <span>{{"ENUM.USER_ROLES." + getGlobalRole(user).toUpperCase() | translate}}</span> - </td> - <td *ngIf="domainId !== domainService.getGlobalDomainId() && !isModeAllowed(ComponentMode.EDIT)"> - <div *roles="['ROLE_DOMAIN_ADMIN'];excluded:['ROLE_SYSTEM_ADMIN']"> - <div *ngIf="!checkUserIfIsCurrentUser(user.username)"> - <li [routerLinkActiveOptions]="{exact:true}" - [routerLinkActive]="['active']" class="dropdown dropdown-domains"> - <a aria-expanded="false" data-close-others="true" aria-haspopup="true" - class="dropdown-toggle" data-toggle="dropdown" - role="button"> - <span style="color: black;">{{"ENUM.USER_ROLES." + getOnlyDomainRoles(user)?.role | translate}} - <strong class="caret"></strong></span></a> - <ul class="dropdown-menu"> - <li *ngFor="let role of getAllowedRoles()"> - <a (click)="changeUserRole(user,domainId, {value:role})">{{"ENUM.USER_ROLES." + Role[role].toUpperCase() | translate}}</a> - </li> - </ul> - </li> - </div> - <div *ngIf="checkUserIfIsCurrentUser(user.username)"> - <span> - {{"ENUM.USER_ROLES." + getOnlyDomainRoles(user)?.role | translate}} - </span> - </div> - </div> +<!-- <div *roles="['ROLE_SYSTEM_ADMIN']">--> +<!-- <div>--> +<!-- {{"ENUM.USER_ROLES." + getOnlyDomainRoles(user)?.role | translate}}--> +<!-- </div>--> +<!-- </div>--> - <div *roles="['ROLE_SYSTEM_ADMIN']"> - <div> - {{"ENUM.USER_ROLES." + getOnlyDomainRoles(user)?.role | translate}} - </div> - </div> +<!-- </td>--> +<!-- <td *ngIf="!isModeAllowed(ComponentMode.EDIT) && !domainMode">{{user.firstLoginDate | date:'dd-MM-yyyy HH:mm'}}</td>--> +<!-- <td *ngIf="!isModeAllowed(ComponentMode.EDIT) && !domainMode">{{user.lastSuccessfulLoginDate | date:'dd-MM-yyyy HH:mm'}}</td>--> +<!-- <td>--> +<!-- <span class="glyphicon glyphicon-ok" *ngIf="user?.enabled"></span>--> +<!-- <span class="glyphicon glyphicon-remove" *ngIf="!(user?.enabled)"></span>--> +<!-- </td>--> +<!-- <td *ngIf="!domainMode && !isModeAllowed(ComponentMode.EDIT)">--> +<!-- <span class="dropdown">--> +<!-- <a style="display: inline-block" class="dropdown-toggle " aria-expanded="false"--> +<!-- aria-haspopup="true"--> +<!-- data-toggle="dropdown" href="#" role="button">--> +<!-- <em class="fas fa-cog icon-black icon-bigger"></em>--> +<!-- </a>--> +<!-- <ul class="dropdown-menu pull-right-drop">--> +<!-- <li *ngIf="isModeAllowed(ComponentMode.VIEW)">--> +<!-- <a (click)="view(user.id);$event.stopPropagation()">--> +<!-- {{ 'USERS.DETAILS_BUTTON' | translate }}--> +<!-- </a>--> +<!-- </li>--> +<!-- <li *ngIf="user.enabled && authService.hasRole('ROLE_SYSTEM_ADMIN') && user.username!==authService.getUsername()">--> +<!-- <a (click)="changeUserStatus(user, false);$event.stopPropagation()"--> +<!-- > <!–user should not be able to disable himself –>--> +<!-- {{ 'USERS.DISABLE_BUTTON' | translate }}</a>--> +<!-- </li>--> +<!-- <li *ngIf="!user.enabled && authService.hasRole('ROLE_SYSTEM_ADMIN')">--> +<!-- <a (click)="changeUserStatus(user, true);$event.stopPropagation()">--> +<!-- {{ 'USERS.ENABLE_BUTTON' | translate }}</a>--> +<!-- </li>--> +<!-- <li *ngIf="isModeAllowed(ComponentMode.EDIT) &&--> +<!-- domainId != domainService.getGlobalDomainId() &&--> +<!-- isGlobalGuestAndHasNoRoleInThisDomain(user)">--> +<!-- <a--> +<!-- (click)="addToCurrentDomain(user);$event.stopPropagation()">{{'USERS.GRANT_USER_ROLE_IN_CURRENT_DOMAIN_BUTTON' | translate}}</a>--> +<!-- </li>--> +<!-- <li *ngIf="isModeAllowed(ComponentMode.DELETE) && domainId != domainService.getGlobalDomainId() && user.username!==authService.getUsername()">--> +<!-- <a role="button"--> - </td> - <td *ngIf="!isModeAllowed(ComponentMode.EDIT) && !domainMode">{{user.firstLoginDate | date:'dd-MM-yyyy HH:mm'}}</td> - <td *ngIf="!isModeAllowed(ComponentMode.EDIT) && !domainMode">{{user.lastSuccessfulLoginDate | date:'dd-MM-yyyy HH:mm'}}</td> - <td> - <span class="glyphicon glyphicon-ok" *ngIf="user?.enabled"></span> - <span class="glyphicon glyphicon-remove" *ngIf="!(user?.enabled)"></span> - </td> - <td *ngIf="!domainMode && !isModeAllowed(ComponentMode.EDIT)"> - <span class="dropdown"> - <a style="display: inline-block" class="dropdown-toggle " aria-expanded="false" - aria-haspopup="true" - data-toggle="dropdown" href="#" role="button"> - <em class="fas fa-cog icon-black icon-bigger"></em> - </a> - <ul class="dropdown-menu pull-right-drop"> - <li *ngIf="isModeAllowed(ComponentMode.VIEW)"> - <a (click)="view(user.id);$event.stopPropagation()"> - {{ 'USERS.DETAILS_BUTTON' | translate }} - </a> - </li> - <li *ngIf="user.enabled && authService.hasRole('ROLE_SYSTEM_ADMIN') && user.username!==authService.getUsername()"> - <a (click)="changeUserStatus(user, false);$event.stopPropagation()" - > <!--user should not be able to disable himself --> - {{ 'USERS.DISABLE_BUTTON' | translate }}</a> - </li> - <li *ngIf="!user.enabled && authService.hasRole('ROLE_SYSTEM_ADMIN')"> - <a (click)="changeUserStatus(user, true);$event.stopPropagation()" - > - {{ 'USERS.ENABLE_BUTTON' | translate }}</a> - </li> - <li *ngIf="isModeAllowed(ComponentMode.EDIT) && - domainId != domainService.getGlobalDomainId() && - isGlobalGuestAndHasNoRoleInThisDomain(user)"> - <a - (click)="addToCurrentDomain(user);$event.stopPropagation()">{{'USERS.GRANT_USER_ROLE_IN_CURRENT_DOMAIN_BUTTON' | translate}}</a> - </li> - <li *ngIf="isModeAllowed(ComponentMode.DELETE) && domainId != domainService.getGlobalDomainId() && user.username!==authService.getUsername()"> - <a role="button" +<!-- (click)="onRemoveFromDomain.emit(user);$event.stopPropagation()">{{ 'USERS.REMOVE_FROM_DOMAIN_BUTTON' | translate }}</a>--> +<!-- </li>--> +<!-- <li *ngIf="isModeAllowed(ComponentMode.DELETE) && authService.hasRole('ROLE_SYSTEM_ADMIN') && user.username!==authService.getUsername() && canUserBeDeleted(user)">--> +<!-- <a role="button"--> +<!-- (click)="onDelete.emit(user);$event.stopPropagation()">{{ 'USERS.DELETE_BUTTON' | translate }}</a>--> +<!-- </li>--> +<!-- </ul>--> +<!-- </span>--> +<!-- </td>--> +<!-- <td *ngIf="domainMode && !isModeAllowed(ComponentMode.EDIT)">--> +<!-- <span *ngIf="!checkUserIfIsCurrentUser(user.username)">--> +<!-- <a style="display: inline-block" class="" aria-expanded="false"--> +<!-- aria-haspopup="true" role="button"--> +<!-- (click)="onRemoveFromDomain.emit(user);$event.stopPropagation()">--> +<!-- <em class="fas fa-trash icon-black "></em>--> +<!-- </a>--> +<!-- </span>--> +<!-- </td>--> +<!-- <td *ngIf="isModeAllowed(ComponentMode.EDIT)" style="width: 170px">--> +<!-- <button class="btn btn-secondary" role="button"--> +<!-- (click)="addToCurrentDomain(user)"> {{'USERS.ADD_TO_DOMAIN_BUTTON' | translate}}</button>--> +<!-- </td>--> +<!-- </tr>--> +<!-- </tbody>--> +<!-- </table>--> + +<!-- <pagination-controls class="text-right" (pageChange)="pageNumber = $event" id="{{ paginatorName }}"--> +<!-- previousLabel="{{ 'PAGINATION.PREVIOUS' | translate }}"--> +<!-- nextLabel="{{ 'PAGINATION.NEXT' | translate }}"--> +<!-- screenReaderPaginationLabel="{{ 'PAGINATION.SCREEN_READER.PAGINATION' | translate }}"--> +<!-- screenReaderPageLabel="{{ 'PAGINATION.SCREEN_READER.PAGE' | translate }}"--> +<!-- screenReaderCurrentLabel="{{ 'PAGINATION.SCREEN_READER.CURRENT' | translate }}"></pagination-controls>--> - (click)="onRemoveFromDomain.emit(user);$event.stopPropagation()">{{ 'USERS.REMOVE_FROM_DOMAIN_BUTTON' | translate }}</a> - </li> - <li *ngIf="isModeAllowed(ComponentMode.DELETE) && authService.hasRole('ROLE_SYSTEM_ADMIN') && user.username!==authService.getUsername() && canUserBeDeleted(user)"> - <a role="button" - (click)="onDelete.emit(user);$event.stopPropagation()">{{ 'USERS.DELETE_BUTTON' | translate }}</a> - </li> - </ul> - </span> - </td> - <td *ngIf="domainMode && !isModeAllowed(ComponentMode.EDIT)"> - <span *ngIf="!checkUserIfIsCurrentUser(user.username)"> - <a style="display: inline-block" class="" aria-expanded="false" - aria-haspopup="true" role="button" - (click)="onRemoveFromDomain.emit(user);$event.stopPropagation()"> - <em class="fas fa-trash icon-black "></em> - </a> - </span> - </td> - <td *ngIf="isModeAllowed(ComponentMode.EDIT)" style="width: 170px"> - <button class="btn btn-secondary" role="button" - (click)="addToCurrentDomain(user)"> {{'USERS.ADD_TO_DOMAIN_BUTTON' | translate}}</button> - </td> - </tr> - </tbody> - </table> - <pagination-controls class="text-right" (pageChange)="pageNumber = $event" id="{{ paginatorName }}" - previousLabel="{{ 'PAGINATION.PREVIOUS' | translate }}" - nextLabel="{{ 'PAGINATION.NEXT' | translate }}" - screenReaderPaginationLabel="{{ 'PAGINATION.SCREEN_READER.PAGINATION' | translate }}" - screenReaderPageLabel="{{ 'PAGINATION.SCREEN_READER.PAGE' | translate }}" - screenReaderCurrentLabel="{{ 'PAGINATION.SCREEN_READER.CURRENT' | translate }}"></pagination-controls> -</div> diff --git a/src/app/shared/users/list/userslist.component.spec.ts b/src/app/shared/users/list/userslist.component.spec.ts index 8123a3e2804de1f09c5a6bf7e552b62f8d934bb5..0d0d696df1d66863dc4b9852f5f06d262fa45fab 100644 --- a/src/app/shared/users/list/userslist.component.spec.ts +++ b/src/app/shared/users/list/userslist.component.spec.ts @@ -11,6 +11,7 @@ import {NgxPaginationModule} from 'ngx-pagination'; import {RouterTestingModule} from '@angular/router/testing'; import {of} from 'rxjs'; import createSpyObj = jasmine.createSpyObj; +import { CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA } from '@angular/core'; describe('UserslistComponent', () => { let component: UsersListComponent; @@ -57,7 +58,8 @@ describe('UserslistComponent', () => { } }, {provide: UserService, useValue: {}}, - ] + ], + schemas: [CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA], }) .compileComponents(); })); diff --git a/src/app/shared/users/new-ssh-key/new-ssh-key.component.html b/src/app/shared/users/new-ssh-key/new-ssh-key.component.html index fa6c415c393941a1004522b79955733164f864e7..176ebdef2d70761f3ffeb7a3d6cfb241e931cb90 100644 --- a/src/app/shared/users/new-ssh-key/new-ssh-key.component.html +++ b/src/app/shared/users/new-ssh-key/new-ssh-key.component.html @@ -1,4 +1,4 @@ -<button type="button" class="btn btn-secondary" (click)="modal.show()">{{'SSH_KEYS.BUTTON_NEW_KEY' | translate}}</button> +<button type="button" class="btn btn-text" (click)="modal.show()">{{'SSH_KEYS.BUTTON_NEW_KEY' | translate}}</button> <nmaas-modal styleModal="info"> <div class="nmaas-modal-header">{{'SSH_KEYS.MODAL.HEADER' | translate}}</div> @@ -26,9 +26,10 @@ <div *ngIf="key.errors.required">{{'SSH_KEYS.MODAL.ERROR.KEY_REQUIRED' | translate}}</div> <div *ngIf="key.errors.pattern">{{'SSH_KEYS.MODAL.ERROR.KEY_PATTERN' | translate}}</div> </div> - - <input type="submit" style="border-radius: 4px" class="btn btn-primary" value="{{'SSH_KEYS.MODAL.BUTTON_ADD' | translate}}" [disabled]="!requestForm.valid"> - <button type="button" class="btn btn-secondary pull-right" (click)="modal.hide()">{{'SSH_KEYS.MODAL.BUTTON_CANCEL' | translate}}</button> + <div style="display: flex; justify-content: flex-end"> + <button type="button" class="btn btn-secondary mr-2" (click)="modal.hide()">{{'SSH_KEYS.MODAL.BUTTON_CANCEL' | translate}}</button> + <input type="submit" style="border-radius: 4px" class="btn btn-primary" value="{{'SSH_KEYS.MODAL.BUTTON_ADD' | translate}}" [disabled]="!requestForm.valid"> + </div> </form> <div *ngIf="error" class="alert alert-danger"> diff --git a/src/app/shared/users/preferences/preferences.component.css b/src/app/shared/users/preferences/preferences.component.css index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..9f602404edb31cc7e1ba9c0ee058bec0de128ce7 100644 --- a/src/app/shared/users/preferences/preferences.component.css +++ b/src/app/shared/users/preferences/preferences.component.css @@ -0,0 +1,9 @@ +.form-control[disabled], +fieldset[disabled] .form-control { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background: transparent; + border:none; + box-shadow: none; +} diff --git a/src/app/shared/users/preferences/preferences.component.html b/src/app/shared/users/preferences/preferences.component.html index 22fe2433fbb7bd015706009efd368458d51b17c1..133f753fc19f0c7e30c573b95e882a7cae49df18 100644 --- a/src/app/shared/users/preferences/preferences.component.html +++ b/src/app/shared/users/preferences/preferences.component.html @@ -1,17 +1,13 @@ -<div style="padding-bottom: 15px;" class="panel panel-default"> - <div class="panel-heading">{{'USER_DETAILS.PREFERENCES' | translate}}</div> +<div style="padding-bottom: 15px;" class="background-section"> + <div style="display: flex; justify-content:space-between"> + <h4 style="font-size:15px; font-weight: bold">{{'USER_DETAILS.PREFERENCES' | translate}}</h4> + <button *ngIf="isInMode(ComponentMode.VIEW) && isModeAllowed(ComponentMode.EDIT)" type="button" + class="btn btn-text" (click)="onModeChange()">{{'USER_DETAILS.EDIT_BUTTON' | translate}}</button> + </div> + <div class="panel-body"> <form *ngIf="user" (submit)="submit()" class="form-horizontal" #userPreferencesForm="ngForm"> - <div class="flex justify-content-end mb-4"> - <button *ngIf="!isInMode(ComponentMode.VIEW)" type="button" - class="btn btn-text" (click)="onModeChange()">{{'USER_DETAILS.VIEW_BUTTON' | translate}}</button> - <button *ngIf="!isInMode(ComponentMode.VIEW)" type="submit" [disabled]="userPreferencesForm.invalid" - class="btn btn-primary">{{'USER_DETAILS.SUBMIT_BUTTON' | translate}}</button> - <button *ngIf="isInMode(ComponentMode.VIEW) && isModeAllowed(ComponentMode.EDIT)" type="button" - class="btn btn-text" (click)="onModeChange()">{{'USER_DETAILS.EDIT_BUTTON' | translate}}</button> - </div> - <div class="form-group" [ngClass]="{'has-error': defaultDomain.invalid && (defaultDomain.dirty || defaultDomain.touched), 'has-success': defaultDomain.valid && (defaultDomain.dirty || defaultDomain.touched)}"> <label for="defaultDomain" class="col-sm-2 control-label">{{'USER_DETAILS.DEFAULT_DOMAIN' | translate}}:</label> @@ -23,6 +19,12 @@ </select> </div> </div> + <div class="flex justify-content-end mb-4"> + <button *ngIf="!isInMode(ComponentMode.VIEW)" type="button" style="margin-right:10px" + class="btn btn-secondary" (click)="onModeChange()">{{'USER_DETAILS.VIEW_BUTTON' | translate}}</button> + <button *ngIf="!isInMode(ComponentMode.VIEW)" type="submit" [disabled]="userPreferencesForm.invalid" + class="btn btn-primary">{{'USER_DETAILS.SUBMIT_BUTTON' | translate}}</button> + </div> </form> </div> diff --git a/src/app/shared/users/privileges/userprivileges.component.html b/src/app/shared/users/privileges/userprivileges.component.html index dbb956d453c4d3176a71e8a2b093ac7c6b926cff..7650923db2f690c9ced1014950efa51b63bedd5c 100644 --- a/src/app/shared/users/privileges/userprivileges.component.html +++ b/src/app/shared/users/privileges/userprivileges.component.html @@ -1,5 +1,5 @@ -<div style="margin-bottom: 15px;" class="panel panel-default"> - <div class="panel-heading">{{'USER_PRIVILEGES.HEADER' | translate}}</div> +<div style="padding-bottom: 15px;" class="background-section"> + <h4 style="font-size:15px; font-weight: bold" >{{'USER_PRIVILEGES.HEADER' | translate}}</h4> <div class="panel-body"> <div *ngIf="isModeAllowed(ComponentMode.CREATE) && user != null && user.username !== authService.getUsername()"> <!-- user should not be able to change his roles --> <form *ngIf="user" [formGroup]="newPrivilegeForm" (submit)="add()" class="form-inline row"> diff --git a/src/app/shared/users/ssh-keys/ssh-keys.component.html b/src/app/shared/users/ssh-keys/ssh-keys.component.html index e9025bddb5cd7796a569b04f68bb8cd26b94c772..7cc7808076580c866bce02ff27d905eafb96c18b 100644 --- a/src/app/shared/users/ssh-keys/ssh-keys.component.html +++ b/src/app/shared/users/ssh-keys/ssh-keys.component.html @@ -1,9 +1,11 @@ -<div style="margin-bottom: 15px;" class="panel panel-default"> - <div class="panel-heading">{{'SSH_KEYS.HEADER' | translate}}</div> +<div style="padding-bottom: 15px;" class="background-section"> + + <div style="display: flex; justify-content:space-between"> + <h4 style="font-size:15px; font-weight: bold" >{{'SSH_KEYS.HEADER' | translate}}</h4> + <app-new-ssh-key [userMode]="userMode" [userId]="userId" (out)="getData()"></app-new-ssh-key> + </div> <div class="panel-body"> - <div class="flex justify-content-end mb-4"> - <app-new-ssh-key [userMode]="userMode" [userId]="userId" (out)="getData()"></app-new-ssh-key> - </div> + <table class="table table-hover" aria-describedby="User ssh keys table"> <thead> <tr> diff --git a/src/app/welcome/profile/profile.component.html b/src/app/welcome/profile/profile.component.html index f8c17f7e5690bf61858490dcb21e28b1035f820b..a62e21156ef7784524d5b4106c17a1671874a87f 100644 --- a/src/app/welcome/profile/profile.component.html +++ b/src/app/welcome/profile/profile.component.html @@ -1,4 +1,4 @@ -<div class="container" *ngIf="user"> +<div class="" *ngIf="user"> <div id="profile-header"> <div class="col-xs-10 col-sm-9 col-md-9 col-lg-9"> <h3>{{'USER_DETAILS.USER' | translate}} {{user?.username}} </h3> diff --git a/src/assets/images/nmaas-cloud.png b/src/assets/images/nmaas-cloud.png new file mode 100644 index 0000000000000000000000000000000000000000..2790aa3f466f627706c59bdf8ecb8ad0e1f6499b Binary files /dev/null and b/src/assets/images/nmaas-cloud.png differ diff --git a/src/styles.css b/src/styles.css index b3eaa02ccce2b1b94b757865e1477ef33b3061c6..412d7d47804d5dcfcf5dc60c985f0e9036b96acf 100644 --- a/src/styles.css +++ b/src/styles.css @@ -6,7 +6,83 @@ /* These classes are copy pasted from bootstrap 4 to work with formio panel display */ /* remove when applying actual bootstrap 4 */ +:root{ + --left-panel-width: 300px; + /*light-mode*/ + --button-text-color: #ffffff; + --primary-button-color: #3767BE; + --primary-button-hover: #32559B; + --primary-text-button-color: #3767BE; + --primary-text-button-text-hover: #32559B; + --primary-text-button-background-hover: #EAF0FF; + --secondary-button-color: #64748B; + --secondary-button-hover: #475569; + --secondary-text-button-color: #64748B; + --secondary-text-button-text-hover: #475569; + --secondary-text-button-background-hover: #E1E9F2; + --danger-button-color: #CB433F; + --danger-button-hover: #A40400; + --danger-text-button-color: #CB433F; + --danger-text-button-text-hover: #A40400; + --danger-text-button-background-hover: #FEF2F2; + --user-button-background: #ffffff; + --user-button-background-hover: #EAF0FF; + + --menu-color: #F2F4F7; + --menu-pink: #C80071; + --card-color: #F6F6F7; + --l-text-color: #233354; + --app-text-color: #233354; + --app-background-color: #F6F6F7; + --tag-color: #F0D7DD; + --background: #ffffff; + + + /*dark-mode*/ + --d-button-text-color: #1C1F27; + --d-primary-button-color: #75AEFF; + --d-primary-button-hover: #4880D0; + --d-primary-text-button-color: #A1CDFF; + --d-primary-text-button-text-hover: #32559B; + --d-primary-text-button-background-hover: #EAF0FF; + --d-secondary-button-color: #DFDFDF; + --d-secondary-button-hover: #B6B6B6; + --d-secondary-text-button-color: #DFDFDF; + --d-secondary-text-button-text-hover: #475569; + --d-secondary-text-button-background-hover: #EAF0FF; + --d-danger-button-color: #E2625F; + --d-danger-button-hover: #CB433F; + --d-danger-text-button-color: #FFACAC; + --d-danger-text-button-text-hover: #C70500; + --d-danger-text-button-background-hover: #FEF2F2; + --d-user-button-background: #1C1F27; + --d-user-button-background-hover: #5B9FFF; + + --d-menu-color: #3C3F47; + --d-menu-pink: #C80071; + --d-card-color: #4D5059; + --d-text-color: #ffffff; + --d-app-text-color: #233354; + --d-app-background-color: #E4E7F1; + --d-tag-color: #F0D7DD; + --d-background: #1C1F27; +} +.form-control:focus { + border-color: var(--l-text-color); + outline: 0; + box-shadow: none; +} +.background-section{ + background: var(--app-background-color); + border-radius: 10px; + margin:20px 0; + padding:30px; +} +.header{ + margin-top:40px; + font-weight: bold +} .card { position: relative; display: -webkit-box; @@ -55,42 +131,48 @@ font-size: 14px; } .btn-primary{ - background:#233354; + background:var(--primary-button-color); border:none; } .btn-primary:hover{ - background: #414f6b; + background: var(--primary-button-hover); border:none; } .btn-primary[disabled]{ text-shadow:none; - background: #414f6b; + background:var(--primary-button-color); cursor: not-allowed; /*color: #61b4f9;*/ } .btn-primary[disabled]:hover{ - background: #414f6b; + background: var(--primary-button-color); } .btn{ border:none } .btn-secondary{ - background: #3767BE; + background: var(--secondary-button-color); color: #ffffff; } .btn-secondary:hover{ - background: #32559B; + background: var(--secondary-button-hover); color: #ffffff } .btn-danger{ - background: #C70500; + background: var(--danger-button-color); } .btn-text{ - color: #32559B; - background: #ffffff; + color: var(--primary-text-button-color); + background: transparent; } .btn-text:hover{ - background: #EBEEF5; + background: var(--primary-text-button-background-hover); + color: var(--primary-text-button-text-hover) +} + +.dropdown-menu{ + right: 0; + left:unset; } .text-bold {