From c97b3f1b6cb6c0dd907ad63adbe22820842e01ed Mon Sep 17 00:00:00 2001 From: dweinholz Date: Fri, 3 May 2024 11:51:46 +0200 Subject: [PATCH 01/16] started refagoring --- src/app/api-connector/applications.service.ts | 34 +- .../application-detail.component.ts | 137 +- .../application-pi-detail.component.html | 156 +- .../lifetime-extension-detail.component.html | 105 +- .../lifetime-extension-detail.component.ts | 11 +- .../modification-detail.component.html | 180 +- .../modification-detail.component.ts | 16 +- .../application.model/user.model.ts | 1 + .../application_extension.model.ts | 2 +- .../applications/applications.component.html | 2909 +++++++++-------- .../applications/applications.component.ts | 283 +- .../application-base-class.component.ts | 214 +- 12 files changed, 2125 insertions(+), 1923 deletions(-) diff --git a/src/app/api-connector/applications.service.ts b/src/app/api-connector/applications.service.ts index bd283a951f..8062826075 100644 --- a/src/app/api-connector/applications.service.ts +++ b/src/app/api-connector/applications.service.ts @@ -8,6 +8,7 @@ import { Application } from '../applications/application.model/application.model import { ApplicationLifetimeExtension } from '../applications/application_extension.model'; import { ApplicationModification } from '../applications/application_modification.model'; import { ApplicationCreditRequest } from '../applications/application_credit_request'; +import { User } from '../applications/application.model/user.model'; /** * Service which provides methods for creating application. @@ -44,6 +45,29 @@ export class ApplicationsService { ); } + getApplicationPI(application_id: string | number): Observable { + return this.http.get(`${ApiSettings.getApiBaseURL()}project_applications/${application_id}/pi/`, { + withCredentials: true, + }); + } + + getApplicationUser(application_id: string | number): Observable { + return this.http.get(`${ApiSettings.getApiBaseURL()}project_applications/${application_id}/user/`, { + withCredentials: true, + }); + } + + getLifetimeExtensionUser(lifetimeextension_id: string | number): Observable { + return this.http.get(`${ApiSettings.getApiBaseURL()}project_applications/lifetime/extensions/${lifetimeextension_id}/user/`, { + withCredentials: true, + }); + } + + getModificationUser(project_id: string | number): Observable { + return this.http.get(`${ApiSettings.getApiBaseURL()}project_applications/lifetime/modification/${project_id}/user/`, { + withCredentials: true, + }); + } getApplicationValidationByHash(hash: string): Observable { return this.http.get(`${ApiSettings.getApiBaseURL()}project_applications/validation/${hash}/`, { withCredentials: true, @@ -124,11 +148,11 @@ export class ApplicationsService { } /** - * Checks if some client has the resource available for an application. - * - * @param app_id - * @returns - */ + * Checks if some client has the resource available for an application. + * + * @param app_id + * @returns + */ getApplicationClientAvaiable(app_id: string): Observable { return this.http.get(`${ApiSettings.getApiBaseURL()}project_applications/${app_id}/clients/resource/`, { withCredentials: true, diff --git a/src/app/applications/application-detail/application-detail.component.ts b/src/app/applications/application-detail/application-detail.component.ts index e772a2931a..8705a3137c 100644 --- a/src/app/applications/application-detail/application-detail.component.ts +++ b/src/app/applications/application-detail/application-detail.component.ts @@ -35,81 +35,82 @@ export class ApplicationDetailComponent extends ApplicationBaseClassComponent im EXTENSION_TAB_ACTIVE: boolean = false; COMMENT_TAB_ACTIVE: boolean = false; - @Input() application: Application; - @Input() default_tab: number = this.PI_USER_TAB; + @Input() application: Application; + @Input() default_tab: number = this.PI_USER_TAB; - creditsService: CreditsService; - is_vo_admin: boolean = false; - current_credits: number = 0; - Application_States: typeof Application_States = Application_States; + creditsService: CreditsService; + is_vo_admin: boolean = false; + current_credits: number = 0; + Application_States: typeof Application_States = Application_States; - setAllTabsFalse(): void { - this.PI_USER_TAB_ACTIVE = false; - this.INFORMATION_TAB_ACTIVE = false; - this.RESOURCE_TAB_ACTIVE = false; - this.CREDITS_TAB_ACTIVE = false; - this.MODIFICATION_TAB_ACTIVE = false; - this.EXTENSION_TAB_ACTIVE = false; - this.COMMENT_TAB_ACTIVE = false; - } + setAllTabsFalse(): void { + this.PI_USER_TAB_ACTIVE = false; + this.INFORMATION_TAB_ACTIVE = false; + this.RESOURCE_TAB_ACTIVE = false; + this.CREDITS_TAB_ACTIVE = false; + this.MODIFICATION_TAB_ACTIVE = false; + this.EXTENSION_TAB_ACTIVE = false; + this.COMMENT_TAB_ACTIVE = false; + } - setTab(tab_num: number): void { - this.setAllTabsFalse(); - switch (tab_num) { - case this.PI_USER_TAB: - this.PI_USER_TAB_ACTIVE = true; - break; - case this.INFORMATION_TAB: - this.INFORMATION_TAB_ACTIVE = true; - break; - case this.RESOURCE_TAB: - this.RESOURCE_TAB_ACTIVE = true; - break; - case this.CREDITS_TAB: - this.CREDITS_TAB_ACTIVE = true; - break; - case this.MODIFICATION_TAB: - this.MODIFICATION_TAB_ACTIVE = true; - break; - case this.EXTENSION_TAB: - this.EXTENSION_TAB_ACTIVE = true; - break; - case this.COMMENT_TAB: - this.COMMENT_TAB_ACTIVE = true; - break; - default: - break; + setTab(tab_num: number): void { + this.setAllTabsFalse(); + switch (tab_num) { + case this.PI_USER_TAB: + this.PI_USER_TAB_ACTIVE = true; + break; + case this.INFORMATION_TAB: + this.INFORMATION_TAB_ACTIVE = true; + break; + case this.RESOURCE_TAB: + this.RESOURCE_TAB_ACTIVE = true; + break; + case this.CREDITS_TAB: + this.CREDITS_TAB_ACTIVE = true; + break; + case this.MODIFICATION_TAB: + this.MODIFICATION_TAB_ACTIVE = true; + break; + case this.EXTENSION_TAB: + this.EXTENSION_TAB_ACTIVE = true; + break; + case this.COMMENT_TAB: + this.COMMENT_TAB_ACTIVE = true; + break; + default: + break; + } } - } - constructor( - applicationsService: ApplicationsService, - userService: UserService, - facilityService: FacilityService, - creditsService: CreditsService, - cdrRef: ChangeDetectorRef, - ) { - super(userService, applicationsService, facilityService, cdrRef); - this.creditsService = creditsService; - } + constructor( + applicationsService: ApplicationsService, + userService: UserService, + facilityService: FacilityService, + creditsService: CreditsService, + cdrRef: ChangeDetectorRef, + ) { + super(userService, applicationsService, facilityService, cdrRef); + this.creditsService = creditsService; + } - ngOnInit(): void { - this.setTab(this.default_tab); + ngOnInit(): void { + this.setTab(this.default_tab); - this.getMemberDetailsByElixirId(this.application); - if (this.application.credits_allowed) { - this.getCurrentCredits(); + this.getApplicationPi(this.application); + this.getApplicationUser(this.application); + if (this.application.credits_allowed) { + this.getCurrentCredits(); + } + this.is_vo_admin = is_vo; } - this.is_vo_admin = is_vo; - } - getCurrentCredits(): void { - this.creditsService - .getCurrentCreditsOfProject(Number(this.application.project_application_perun_id.toString())) - .toPromise() - .then((credits: number): void => { - this.current_credits = credits; - }) - .catch((err: Error): void => console.log(err.message)); - } + getCurrentCredits(): void { + this.creditsService + .getCurrentCreditsOfProject(Number(this.application.project_application_perun_id.toString())) + .toPromise() + .then((credits: number): void => { + this.current_credits = credits; + }) + .catch((err: Error): void => console.log(err.message)); + } } diff --git a/src/app/applications/application-detail/application-pi-detail/application-pi-detail.component.html b/src/app/applications/application-detail/application-pi-detail/application-pi-detail.component.html index f1f6e57857..d3d5dc75cd 100644 --- a/src/app/applications/application-detail/application-pi-detail/application-pi-detail.component.html +++ b/src/app/applications/application-detail/application-pi-detail/application-pi-detail.component.html @@ -1,57 +1,103 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Approved by VO - {{ application?.approved_by_vo_manager }} -
Approved by FM - {{ application?.approved_by_facility_manager }} -
PI Name - {{ application?.project_application_pi?.username }} -
PI Email - {{ application?.project_application_pi?.email }} -
PI institue or workgroup link
PI Affiliations - {{ application?.project_application_pi?.user_affiliations }} -
User Affiliations{{ application?.project_application_user?.user_affiliations }}
User Name - {{ application?.project_application_user?.username }} -
User Email - {{ application?.project_application_user?.email }} -
Approved by VO + {{ application?.approved_by_vo_manager }} +
Approved by FM + {{ application?.approved_by_facility_manager }} +
PI Name + @if(application.project_application_pi?.username){ + {{ application?.project_application_pi?.username }} + @if (application?.project_application_pi?.pi_project_count_total > 0) { + + {{ application?.project_application_pi?.pi_project_count_total }} + + + }} + @else{ + + + } +
PI Email + @if (application?.project_application_pi?.email) { + {{ application?.project_application_pi?.email }} + } + @else{ + + + } +
PI institue or workgroup link
PI Affiliations + @if (application?.project_application_pi?.user_affiliations) { + {{ application?.project_application_pi?.user_affiliations }} + } + @else{ + + + } +
User Affiliations + @if (application?.project_application_pi?.user_affiliations) { + {{ application?.project_application_pi?.user_affiliations }} + } + @else{ + + + } +
User Name + @if (application?.project_application_pi?.username) { + {{ application?.project_application_pi?.username }} + } + @else{ + + + } +
User Email + @if (application?.project_application_pi?.email) { + {{ application?.project_application_pi?.email }} + } + @else{ + + + } +
diff --git a/src/app/applications/application-detail/lifetime-extension-detail/lifetime-extension-detail.component.html b/src/app/applications/application-detail/lifetime-extension-detail/lifetime-extension-detail.component.html index 66592b48dd..4464d8082b 100644 --- a/src/app/applications/application-detail/lifetime-extension-detail/lifetime-extension-detail.component.html +++ b/src/app/applications/application-detail/lifetime-extension-detail/lifetime-extension-detail.component.html @@ -1,51 +1,58 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Lifetime Extension Requested from: - {{ application?.project_lifetime_request?.user?.username }} - - ( {{ application?.project_lifetime_request?.user?.email }} | - {{ application?.project_lifetime_request?.user?.elixir_id }} - ) -
Comment{{ application?.project_lifetime_request?.comment }}
Comment by Cloud Governance{{ application?.project_lifetime_request?.manager_comment }}
Extra Lifetime - {{ application?.project_lifetime_request?.extra_lifetime }} - month(s) - ending - {{ application?.project_lifetime_request?.new_end_date.length > 11 ? '' : 'on' }} - {{ application?.project_lifetime_request?.new_end_date }} - instead of - {{ application?.project_lifetime_request?.old_end_date }} -
Extra Credits - {{ application?.project_lifetime_request?.extra_credits }} - (Total: - {{ application?.totalLifetimeExtensionCredits }}) -
Date Submitted - {{ application?.project_lifetime_request?.date_submitted }} -
Lifetime Extension Requested from: + + @if (application?.project_lifetime_request?.user) { + {{ application?.project_lifetime_request?.user?.username }} + + ( {{ application?.project_lifetime_request?.user?.email }} | + {{ application?.project_lifetime_request?.user?.elixir_id }} + ) } + @else{ + + + } + +
Comment{{ application?.project_lifetime_request?.comment }}
Comment by Cloud Governance{{ application?.project_lifetime_request?.manager_comment }}
Extra Lifetime + {{ application?.project_lifetime_request?.extra_lifetime }} + month(s) - ending + {{ application?.project_lifetime_request?.new_end_date.length > 11 ? '' : 'on' }} + {{ application?.project_lifetime_request?.new_end_date }} + instead of + {{ application?.project_lifetime_request?.old_end_date }} +
Extra Credits + {{ application?.project_lifetime_request?.extra_credits }} + (Total: + {{ application?.totalLifetimeExtensionCredits }}) +
Date Submitted + {{ application?.project_lifetime_request?.date_submitted }} +
diff --git a/src/app/applications/application-detail/lifetime-extension-detail/lifetime-extension-detail.component.ts b/src/app/applications/application-detail/lifetime-extension-detail/lifetime-extension-detail.component.ts index a153963c9d..561dc3952b 100644 --- a/src/app/applications/application-detail/lifetime-extension-detail/lifetime-extension-detail.component.ts +++ b/src/app/applications/application-detail/lifetime-extension-detail/lifetime-extension-detail.component.ts @@ -1,5 +1,6 @@ -import { Component, Input } from '@angular/core'; +import { Component, Input, OnInit } from '@angular/core'; import { Application } from '../../application.model/application.model'; +import { ApplicationBaseClassComponent } from '../../../shared/shared_modules/baseClass/application-base-class.component'; /** * Lifetime extension details. @@ -8,6 +9,10 @@ import { Application } from '../../application.model/application.model'; selector: 'app-lifetime-extension-detail', templateUrl: './lifetime-extension-detail.component.html', }) -export class LifetimeExtensionDetailComponent { - @Input() application: Application; +export class LifetimeExtensionDetailComponent extends ApplicationBaseClassComponent implements OnInit { + @Input() application: Application; + + ngOnInit() { + this.getExtensionUser(this.application); + } } diff --git a/src/app/applications/application-detail/modification-detail/modification-detail.component.html b/src/app/applications/application-detail/modification-detail/modification-detail.component.html index 6f484c7117..c655ba62e2 100644 --- a/src/app/applications/application-detail/modification-detail/modification-detail.component.html +++ b/src/app/applications/application-detail/modification-detail/modification-detail.component.html @@ -1,98 +1,104 @@ - - - + + + - - - - - - - - - + + + + + + + + - - - - - - - - - - - - - + >In development + Existing + + + + + + + + + + + + + + - - - - - - - - - - + + + + + + + + + + - - + + - - + + - - + + - - - - + + + + - - - - - - - - + + + + + + + + - - - + + +
Modification Requested from:
Modification Requested from: - {{ application?.project_modification_request?.user?.username }} - ( {{ application?.project_modification_request?.user?.email }} | - {{ application?.project_modification_request?.user?.elixir_id }}) -
Comment{{ application?.project_modification_request?.comment }}
Service status + @if (application?.project_modification_request?.user) { + + {{ application?.project_modification_request?.user?.username }} + ( {{ application?.project_modification_request?.user?.email }} | + {{ application?.project_modification_request?.user?.elixir_id }})} + + @else{ + + + } +
Comment{{ application?.project_modification_request?.comment }}
Service status In development - Existing -
VMs - Requested: - {{ application?.project_modification_request?.vms_requested }} -
Flavor: {{ flavor.name }}{{ application?.project_modification_request | flavorCounter: flavor }}
Total Cores
VMs + Requested: + {{ application?.project_modification_request?.vms_requested }} +
Flavor: {{ flavor.name }}{{ application?.project_modification_request | flavorCounter: flavor }}
Total Cores - {{ application?.project_modification_request?.total_cores }} -
Total RAM{{ application?.project_modification_request?.total_ram }} GB
Total GPUs - {{ application?.project_modification_request?.total_gpu }} -
+ {{ application?.project_modification_request?.total_cores }} +
Total RAM{{ application?.project_modification_request?.total_ram }} GB
Total GPUs + {{ application?.project_modification_request?.total_gpu }} +
Extra Credits
Extra Credits - {{ application?.project_modification_request?.extra_credits }} - (Total: {{ application?.totalModificationRequestCredits }}) -
+ {{ application?.project_modification_request?.extra_credits }} + (Total: {{ application?.totalModificationRequestCredits }}) +
Storage Limit
Storage Limit - {{ application?.project_modification_request?.volume_limit }} - GB -
Volume Counter + {{ application?.project_modification_request?.volume_limit }} + GB +
Volume Counter - {{ application?.project_modification_request?.volume_counter }} - volumes -
ObjectStorage - {{ application?.project_modification_request?.object_storage }} - GB -
Date Submitted + {{ application?.project_modification_request?.volume_counter }} + volumes +
ObjectStorage + {{ application?.project_modification_request?.object_storage }} + GB +
Date Submitted - {{ application?.project_modification_request?.date_submitted }} -
+ {{ application?.project_modification_request?.date_submitted }} +
diff --git a/src/app/applications/application-detail/modification-detail/modification-detail.component.ts b/src/app/applications/application-detail/modification-detail/modification-detail.component.ts index 25f3e3cae7..15ca644db0 100644 --- a/src/app/applications/application-detail/modification-detail/modification-detail.component.ts +++ b/src/app/applications/application-detail/modification-detail/modification-detail.component.ts @@ -1,6 +1,7 @@ -import { Component, Input } from '@angular/core'; +import { Component, Input, OnInit } from '@angular/core'; import { Application } from '../../application.model/application.model'; import { Application_States } from '../../../shared/shared_modules/baseClass/abstract-base-class'; +import { ApplicationBaseClassComponent } from '../../../shared/shared_modules/baseClass/application-base-class.component'; /** * Application modification details. @@ -9,8 +10,13 @@ import { Application_States } from '../../../shared/shared_modules/baseClass/abs selector: 'app-modification-detail', templateUrl: './modification-detail.component.html', }) -export class ModificationDetailComponent { - @Input() application: Application; - @Input() is_vo_admin: boolean; - Application_States: typeof Application_States = Application_States; +export class ModificationDetailComponent extends ApplicationBaseClassComponent implements OnInit { + @Input() application: Application; + @Input() is_vo_admin: boolean; + Application_States: typeof Application_States = Application_States; + + ngOnInit() { + this.getModificationUser(this.application); + } + } diff --git a/src/app/applications/application.model/user.model.ts b/src/app/applications/application.model/user.model.ts index ab36d49c33..42fde49920 100644 --- a/src/app/applications/application.model/user.model.ts +++ b/src/app/applications/application.model/user.model.ts @@ -6,4 +6,5 @@ export class User { user_affiliations: string [] = []; elixir_id: string; email: string; + pi_project_count_total: number = 0; } diff --git a/src/app/applications/application_extension.model.ts b/src/app/applications/application_extension.model.ts index 3ec92cb6bf..0613974c9d 100644 --- a/src/app/applications/application_extension.model.ts +++ b/src/app/applications/application_extension.model.ts @@ -5,7 +5,7 @@ import { Application } from './application.model/application.model'; * Application Extension class. */ export class ApplicationLifetimeExtension { - Id: number; + id: number; project_application_id: number | string; extra_lifetime: number; diff --git a/src/app/applications/applications.component.html b/src/app/applications/applications.component.html index d1d818b6c4..c64f71a57d 100644 --- a/src/app/applications/applications.component.html +++ b/src/app/applications/applications.component.html @@ -1,154 +1,164 @@
-
-
Applications for review
+
+
Applications for review
-
- - + - + - + -
-
-
- Loading applications/requests ... -
-
- - -
- -
+ id="tab_state_button_modification_request" + data-test-id="modification_requests_applications_tab" + > + Resource Modification Requests + {{ this.numberOfModificationRequests }} + +
+
+
+ Loading applications/requests ... +
+
+ + +
+ +
-
-
- - - - - - - - - - - - - - - - - - - - - - - +
+
+
Project NameShort NameDate submittedUserInstituteCompute CenterActions
- - {{ application?.project_application_name }}{{ application?.project_application_shortname }}{{ application?.project_application_date_submitted }} - {{ application?.project_application_user?.username }} - {{ application?.project_application_institute }}
+ + + + + + + + + + + + + + + + + + + + + + [(ngModel)]="selectedCenter[application.project_application_id]" + class="form-select" + [id]="'id_compute_center_option_' + application?.project_application_name" + [attr.data-test-id]="'id_compute_center_option_' + application?.project_application_name" + #selectedComputeCenter + > + + + + + + {{ + application?.project_application_compute_center?.Name + }} + - - + type="button" + class="btn btn-secondary" + style="margin: 2px" + > +   + Show + Hide + Information + + + + + + - - - -
Project NameShort NameDate submittedUserInstituteCompute CenterActions
+ + {{ application?.project_application_name }}{{ application?.project_application_shortname }}{{ application?.project_application_date_submitted }} + @if (application?.project_application_user?.username) { + {{ application?.project_application_user?.username }} + } + @else{ + - - - - - - - - - {{ - application?.project_application_compute_center?.Name - }} - -
- - + - - - + + + - + + style="margin: 2px" + [id]="'approve_' + application?.project_application_shortname" + [attr.data-test-id]="'approve_' + application?.project_application_shortname" + (click)="this.showConfirmationModal(application, ConfirmationActions.APPROVE_APPLICATION)" + [disabled]="!selectedCenter[application.project_application_id] || approveLocked" + type="button" + class="btn btn-success" + > +   + Approve & Create + - + [disabled]="!selectedCenter[application.project_application_id] || approveLocked" + type="button" + class="btn btn-success" + > +   + Approve & Create + - - + - - + + -
-
+ - - -
-
-
-
- -
- -
+ [application]="application" + > +
+ +
+ + + +
+
+ + +
+ +
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Project NameShort NameModification submittedUserInstituteCompute CenterActions
+ + {{ application?.project_application_name }}{{ application?.project_application_shortname }}{{ application?.project_modification_request?.date_submitted }} + @if (application?.project_application_user?.username) { + {{ application?.project_application_user?.username }} + } + @else{ + + + } -
-
- - - - - - - - - - - - - - - - - - - - - - - + + - + - - + type="button" + class="btn btn-secondary" + style="margin: 2px" + > +   + Show + Hide + Information + + + + - - - - -
Project NameShort NameModification submittedUserInstituteCompute CenterActions
- - {{ application?.project_application_name }}{{ application?.project_application_shortname }}{{ application?.project_modification_request?.date_submitted }} - {{ application?.project_application_user?.username }} - {{ application?.project_application_institute }}{{ application?.project_application_institute }} - {{ application?.project_application_compute_center?.Name }} - + {{ application?.project_application_compute_center?.Name }} + -
- - - + + + style="margin: 2px" + type="button" + (click)="setApplicationToAdjust(application); showModificationAdjustmentModal()" + class="btn btn-primary" + > +   + Adjust Modification + - + style="margin: 2px" + [attr.data-test-id]="'modification_approval_' + application?.project_application_shortname" + (click)="showConfirmationModal(application, ConfirmationActions.APPROVE_MODIFICATION)" + type="button" + class="btn btn-success" + > +   + Approve Modification + - - + + type="button" + class="btn btn-danger" + style="margin: 2px" + (click)="showConfirmationModal(application, ConfirmationActions.DECLINE_MODIFICATION)" + > +   + Decline Modification + - -
-
- -
-
-
- - -
- -
+
+ + + +
+
+
+
+ +
+ +
-
-
- - - - - - - - - - - - - - - - - - - - - - - +
+
+
Project NameShort NameLifetime extension submittedUserInstituteCompute CenterActions
- - {{ application?.project_application_name }}{{ application?.project_application_shortname }}{{ application?.project_lifetime_request?.date_submitted }} - {{ application?.project_application_user?.username }} - {{ application?.project_application_institute }}
+ + + + + + + + + + + + + + + + + + + + + + + + + application?.project_application_compute_center?.Name + }} + - - + type="button" + class="btn btn-secondary" + style="margin: 2px" + > +   + Show + Hide + Information + + + + + + - - - -
Project NameShort NameLifetime extension submittedUserInstituteCompute CenterActions
+ + {{ application?.project_application_name }}{{ application?.project_application_shortname }}{{ application?.project_lifetime_request?.date_submitted }} + @if (application?.project_application_user?.username) { + {{ application?.project_application_user?.username }} + } + @else{ + - + } {{ application?.project_application_institute }} {{ - application?.project_application_compute_center?.Name - }} - -
- - - + + - + - + + type="button" + class="btn btn-danger" + style="margin: 2px" + (click)="showConfirmationModal(application, ConfirmationActions.DECLINE_EXTENSION)" + > +   + Decline Extension + - -
-
+ - - -
-
-
-
- -
- -
+ [application]="application" + > +
+ + + + + + + + + +
+ +
-
-
- - - - - - - - - - - - - - - - - - + + + +
Project NameShort NameCredits request submittedUserInstituteCompute CenterActions
+
+
+ + + + + + + + + + + + + + + + + + - - - - - + + + + + + - + application?.project_application_compute_center?.Name + }} + - - + type="button" + class="btn btn-secondary" + style="margin: 2px" + > +   + Show + Hide + Information + + + + - - - - -
Project NameShort NameCredits request submittedUserInstituteCompute CenterActions
+ *ngIf="application?.project_application_openstack_project" + style="font-size: 20px" + class="project-openstack_plain_white_redbg" + data-toggle="tooltip" + title="This is an Openstack project" + > - {{ application?.processing_vo_initials }} + {{ application?.processing_vo_initials }} - + - + - + - + - + - {{ application?.processing_vo_initials }} + {{ application?.processing_vo_initials }} - {{ application?.project_application_name }}{{ application?.project_application_shortname }}{{ application?.project_credit_request?.date_submitted }} - {{ application?.project_application_user?.username }} - {{ application?.project_application_institute }}{{ application?.project_application_name }}{{ application?.project_application_shortname }}{{ application?.project_credit_request?.date_submitted }} + {{ application?.project_application_user?.username }} + {{ application?.project_application_institute }} + {{ - application?.project_application_compute_center?.Name - }} - -
- - - +
+
+ + + - + - -
-
- + + + -
-
-
- - - + [application]="application" + > + +
+
+
+
+ + -
-
Applications history
-
- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - + + + +
Project NameShort NameDate submitted (d/m/y)InstituteStatusActions
- - {{ application?.project_application_name }}{{ application?.project_application_shortname }}{{ application?.project_application_date_submitted }}{{ application?.project_application_institute }} - {{ getStatusById(st) }} - -
+
+
+
+ + + + + + diff --git a/src/app/applications/applications.component.ts b/src/app/applications/applications.component.ts index 8c1450565a..5ed6d337e5 100644 --- a/src/app/applications/applications.component.ts +++ b/src/app/applications/applications.component.ts @@ -25,15 +25,18 @@ import { CreditsService } from '../api-connector/credits.service'; import { ClientLimitsComponent } from '../vo_manager/clients/modals/client-limits..component'; import { NotificationModalComponent } from '../shared/modal/notification-modal'; import { ConfirmationModalComponent } from '../shared/modal/confirmation-modal.component'; -import { ModificationRequestComponent } from '../projectmanagement/modals/modification-request/modification-request.component'; +import { + ModificationRequestComponent, +} from '../projectmanagement/modals/modification-request/modification-request.component'; import { ConfirmationActions } from '../shared/modal/confirmation_actions'; +import { User } from './application.model/user.model'; // eslint-disable-next-line no-shadow enum TabStates { - 'SUBMITTED' = 0, - 'CREDITS_EXTENSION' = 3, - 'LIFETIME_EXTENSION' = 5, - 'MODIFICATION_EXTENSION' = 4, + 'SUBMITTED' = 0, + 'CREDITS_EXTENSION' = 3, + 'LIFETIME_EXTENSION' = 5, + 'MODIFICATION_EXTENSION' = 4, } /** @@ -66,29 +69,29 @@ export class ApplicationsComponent extends ApplicationBaseClassComponent impleme subscription: Subscription = new Subscription(); /** - * All Applications, just visible for a vo admin. - * - * @type {Array} - */ + * All Applications, just visible for a vo admin. + * + * @type {Array} + */ all_applications: Application[] = []; selectedApplication: Application; adjustedApplication: Application; applications_history: Application[] = []; /** - * Limits information for Client tested/used for Simple Vm Project creation. - */ + * Limits information for Client tested/used for Simple Vm Project creation. + */ notificationClientInfo: Client[] = []; /** - * id of the extension status. - * - * @type {number} - */ + * id of the extension status. + * + * @type {number} + */ extension_status: number = 0; /** - * id of Application set for deletion. - */ + * id of Application set for deletion. + */ numberOfExtensionRequests: number = 0; numberOfModificationRequests: number = 0; @@ -101,28 +104,28 @@ export class ApplicationsComponent extends ApplicationBaseClassComponent impleme approveLocked: boolean = false; /** - * Constructor. - * Loads all Applications if user is vo admin and all user_applications. - * - * @param applicationsService - * @param userService - * @param groupservice - * @param modalService - * @param voService - * @param facilityService - * @param flavorService - * @param creditsService - */ + * Constructor. + * Loads all Applications if user is vo admin and all user_applications. + * + * @param applicationsService + * @param userService + * @param groupservice + * @param modalService + * @param voService + * @param facilityService + * @param flavorService + * @param creditsService + */ constructor( applicationsService: ApplicationsService, userService: UserService, - private groupservice: GroupService, - private modalService: BsModalService, - private voService: VoService, - facilityService: FacilityService, - private flavorService: FlavorService, - private creditsService: CreditsService, - cdrRef: ChangeDetectorRef, + private groupservice: GroupService, + private modalService: BsModalService, + private voService: VoService, + facilityService: FacilityService, + private flavorService: FlavorService, + private creditsService: CreditsService, + cdrRef: ChangeDetectorRef, ) { super(userService, applicationsService, facilityService, cdrRef); } @@ -149,9 +152,37 @@ export class ApplicationsComponent extends ApplicationBaseClassComponent impleme } } + getAndSetPiAndUserApplication(application: Application) { + if (!application.project_application_user) { + this.getAndSetApplicationUser(application.project_application_id); + } + if (!application.project_application_pi) { + this.getAndSetApplicationPi(application.project_application_id); + } + + } + + getAndSetApplicationPi(project_application_id: number | string) { + this.applicationsService.getApplicationPI(project_application_id).subscribe((pi: User) => { + const index = this.all_applications.findIndex(app => app.project_application_id === project_application_id); + if (index !== -1) { // Check if the application exists in the array + this.all_applications[index].project_application_pi = pi; + } + }); + } + + getAndSetApplicationUser(project_application_id: number | string) { + this.applicationsService.getApplicationUser(project_application_id).subscribe((user: User) => { + const index = this.all_applications.findIndex(app => app.project_application_id === project_application_id); + if (index !== -1) { // Check if the application exists in the array + this.all_applications[index].project_application_user = user; + } + }); + } + /** - * Getting the current numbers of all Application-Request types from the API - */ + * Getting the current numbers of all Application-Request types from the API + */ getApplicationNumbers(): void { this.applicationsService.getExtensionRequestsCounter().subscribe((result: any): void => { this.numberOfCreditRequests = result['credits_extension_requests_all']; @@ -162,10 +193,10 @@ export class ApplicationsComponent extends ApplicationBaseClassComponent impleme } /** - * Sets both the selected application and the adjusted application which serves as a model for the - * application adjustment of the VO - * @param application - */ + * Sets both the selected application and the adjusted application which serves as a model for the + * application adjustment of the VO + * @param application + */ setApplicationToAdjust(application: Application): void { this.selectedApplication = application; this.adjustedApplication = new Application(application); @@ -251,9 +282,9 @@ export class ApplicationsComponent extends ApplicationBaseClassComponent impleme } /** - * Checks if the flavor is available for SimpleVM - * @param type - */ + * Checks if the flavor is available for SimpleVM + * @param type + */ checkIfTypeGotSimpleVmFlavor(type: FlavorType): boolean { for (const flav of this.flavorList) { if (flav.type.shortcut === type.shortcut && flav.simple_vm) { @@ -268,7 +299,7 @@ export class ApplicationsComponent extends ApplicationBaseClassComponent impleme for (const flav of this.flavorList) { if ( (flav.type.shortcut === type.shortcut && flav.simple_vm) - || type.shortcut === FlavorTypeShortcuts.CUSTOM_FLAVOR + || type.shortcut === FlavorTypeShortcuts.CUSTOM_FLAVOR ) { return true; } @@ -278,10 +309,10 @@ export class ApplicationsComponent extends ApplicationBaseClassComponent impleme } /** - * Checks if the key given represents a flavor and if so returns the respective Flavor - * - * @param key the key which is checked - */ + * Checks if the key given represents a flavor and if so returns the respective Flavor + * + * @param key the key which is checked + */ isKeyFlavor(key: string): Flavor { for (const fkey in this.flavorList) { if (fkey in this.flavorList) { @@ -302,10 +333,10 @@ export class ApplicationsComponent extends ApplicationBaseClassComponent impleme } /** - * Get the facility of an application. - * - * @param app - */ + * Get the facility of an application. + * + * @param app + */ getFacilityProject(app: Application): void { if (!app.project_application_compute_center && !app.hasSubmittedStatus() && !app.hasTerminatedStatus()) { this.groupservice @@ -439,22 +470,34 @@ export class ApplicationsComponent extends ApplicationBaseClassComponent impleme ); } + setApplicationByLoadedApplication(applications: Application[]) { + this.all_applications = []; + if (applications.length === 0) { + this.isLoaded_userApplication = true; + } + for (const application of applications) { + const newApplication = new Application(application); + + this.all_applications.push(newApplication); + this.getAndSetPiAndUserApplication(application); + + } + this.isLoaded = true; + for (const app of this.all_applications) { + this.getFacilityProject(app); + } + this.sortApplicationsByTabState(); + this.loading_applications = false; + + } + /** - * Get all Applications if user is admin. - */ + * Get all Applications if user is admin. + */ getSubmittedApplicationsAdmin(): void { if (this.is_vo_admin) { this.applicationsService.getSubmittedApplications().subscribe((applications: Application[]): void => { - if (applications.length === 0) { - this.isLoaded_userApplication = true; - } - for (const application of applications) { - this.all_applications.push(new Application(application)); - } - this.isLoaded = true; - for (const app of this.all_applications) { - this.getFacilityProject(app); - } + this.setApplicationByLoadedApplication(applications); }); } } @@ -470,42 +513,42 @@ export class ApplicationsComponent extends ApplicationBaseClassComponent impleme } /** - * Emptying all application lists, so applications don't get pushed to the lists multiple times. - */ + * Emptying all application lists, so applications don't get pushed to the lists multiple times. + */ clearApplicationLists(): void { this.all_applications = []; } /** - * Loading Applications dependent from the current tab selected (submitted, credits, lifetime, modification) - */ + * Loading Applications dependent from the current tab selected (submitted, credits, lifetime, modification) + */ sortApplicationsByTabState(): void { switch (this.tab_state) { case TabStates.SUBMITTED: this.all_applications.sort( (a, b) => new Date(a.project_application_date_submitted).getTime() - - new Date(b.project_application_date_submitted).getTime(), + - new Date(b.project_application_date_submitted).getTime(), ); break; case TabStates.LIFETIME_EXTENSION: this.all_applications.sort( (a, b) => new Date(a.project_lifetime_request.date_submitted).getTime() - - new Date(b.project_lifetime_request.date_submitted).getTime(), + - new Date(b.project_lifetime_request.date_submitted).getTime(), ); break; case TabStates.MODIFICATION_EXTENSION: this.all_applications.sort( (a, b) => new Date(a.project_modification_request.date_submitted).getTime() - - new Date(b.project_modification_request.date_submitted).getTime(), + - new Date(b.project_modification_request.date_submitted).getTime(), ); break; case TabStates.CREDITS_EXTENSION: this.all_applications.sort( (a, b) => new Date(a.project_credit_request.date_submitted).getTime() - - new Date(b.project_credit_request.date_submitted).getTime(), + - new Date(b.project_credit_request.date_submitted).getTime(), ); break; @@ -516,19 +559,8 @@ export class ApplicationsComponent extends ApplicationBaseClassComponent impleme getSubmittedApplications(): void { this.applicationsService.getSubmittedApplications().subscribe((applications: Application[]): void => { - if (applications.length === 0) { - this.isLoaded_userApplication = true; - } - for (const application of applications) { - this.all_applications.push(new Application(application)); - } - for (const app of this.all_applications) { - this.getFacilityProject(app); - } - this.sortApplicationsByTabState(); + this.setApplicationByLoadedApplication(applications); - this.isLoaded = true; - this.loading_applications = false; }); } @@ -554,19 +586,7 @@ export class ApplicationsComponent extends ApplicationBaseClassComponent impleme this.applicationsService .getLifetimeRequestedApplications() .subscribe((lifetime_applications: Application[]): void => { - if (lifetime_applications.length === 0) { - // bool here? - } - for (const lifetime_application of lifetime_applications) { - this.all_applications.push(new Application(lifetime_application)); - } - for (const app of this.all_applications) { - this.getFacilityProject(app); - } - this.sortApplicationsByTabState(); - - this.isLoaded = true; - this.loading_applications = false; + this.setApplicationByLoadedApplication(lifetime_applications); }); } @@ -574,19 +594,8 @@ export class ApplicationsComponent extends ApplicationBaseClassComponent impleme this.applicationsService .getModificationRequestedApplications() .subscribe((modification_applications: Application[]): void => { - if (modification_applications.length === 0) { - // bool here? - } - for (const modification_application of modification_applications) { - this.all_applications.push(new Application(modification_application)); - } - for (const app of this.all_applications) { - this.getFacilityProject(app); - } - this.sortApplicationsByTabState(); + this.setApplicationByLoadedApplication(modification_applications); - this.isLoaded = true; - this.loading_applications = false; }); } @@ -607,8 +616,8 @@ export class ApplicationsComponent extends ApplicationBaseClassComponent impleme } /** - * Get all Applications if user is admin. - */ + * Get all Applications if user is admin. + */ getAllApplications(): void { if (this.is_vo_admin) { this.applicationsService.getAllApplications().subscribe((applications: Application[]): void => { @@ -627,10 +636,10 @@ export class ApplicationsComponent extends ApplicationBaseClassComponent impleme } /** - * Updates an application with the actual values. - * - * @param application - */ + * Updates an application with the actual values. + * + * @param application + */ public getApplication(application: Application): void { const index: number = this.all_applications.indexOf(application); @@ -647,10 +656,10 @@ export class ApplicationsComponent extends ApplicationBaseClassComponent impleme } /** - * Remove Application from facility , where it is for confirmation - * - * @param application the application - */ + * Remove Application from facility , where it is for confirmation + * + * @param application the application + */ removeApplicationFromFacilityConfirmation(application: Application): void { this.groupservice.removeGroupFromResource(application.project_application_perun_id.toString()).subscribe( (): void => { @@ -665,11 +674,11 @@ export class ApplicationsComponent extends ApplicationBaseClassComponent impleme } /** - * Create a new Group in perun with the specific attributes. - * - * @param application - * @param compute_center - */ + * Create a new Group in perun with the specific attributes. + * + * @param application + * @param compute_center + */ public createOpenStackProjectGroup(application: Application, compute_center: string): void { this.groupservice.createGroupOpenStack(application.project_application_id, compute_center).subscribe( (result: { [key: string]: string }): void => { @@ -684,7 +693,7 @@ export class ApplicationsComponent extends ApplicationBaseClassComponent impleme } }, (error: any): void => { - const errorMessage = error && error.error === 'locked' + const errorMessage = error && error.error === 'locked' ? 'Project is locked and could not be created!' : 'Project could not be created!'; @@ -703,8 +712,8 @@ export class ApplicationsComponent extends ApplicationBaseClassComponent impleme } /** - * Bugfix not scrollable site after closing modal - */ + * Bugfix not scrollable site after closing modal + */ removeModalOpen(): void { document.body.classList.remove('modal-open'); } @@ -762,8 +771,8 @@ export class ApplicationsComponent extends ApplicationBaseClassComponent impleme } /** - * Function to listen to modal results. - */ + * Function to listen to modal results. + */ subscribeToBsModalRef(): void { this.subscription.add( this.bsModalRef.content.event.subscribe((result: any) => { @@ -840,7 +849,7 @@ export class ApplicationsComponent extends ApplicationBaseClassComponent impleme }, (error: any): void => { console.log(error); - const errorMessage = error && error.error === 'locked' + const errorMessage = error && error.error === 'locked' ? 'Project is locked and could not be created!' : 'Project could not be created!'; @@ -897,7 +906,7 @@ export class ApplicationsComponent extends ApplicationBaseClassComponent impleme } }, (error: any): void => { - const errorMessage = error && error.error === 'locked' + const errorMessage = error && error.error === 'locked' ? 'Project is locked and could not be created!' : 'Project could not be created!'; @@ -937,10 +946,10 @@ export class ApplicationsComponent extends ApplicationBaseClassComponent impleme } /** - * Decline an application. - * - * @param app - */ + * Decline an application. + * + * @param app + */ public declineApplication(app: Application): void { const idx: number = this.all_applications.indexOf(app); diff --git a/src/app/shared/shared_modules/baseClass/application-base-class.component.ts b/src/app/shared/shared_modules/baseClass/application-base-class.component.ts index 4d488be035..19c12c40bc 100644 --- a/src/app/shared/shared_modules/baseClass/application-base-class.component.ts +++ b/src/app/shared/shared_modules/baseClass/application-base-class.component.ts @@ -9,6 +9,8 @@ import { FlavorService } from '../../../api-connector/flavor.service'; import { FacilityService } from '../../../api-connector/facility.service'; import { UserService } from '../../../api-connector/user.service'; import { FlavorTypeShortcuts } from './flavor-type-shortcuts'; +import { User } from '../../../applications/application.model/user.model'; +import { ApplicationLifetimeExtension } from '../../../applications/application_extension.model'; /** * Application base component.. @@ -20,54 +22,54 @@ import { FlavorTypeShortcuts } from './flavor-type-shortcuts'; }) export class ApplicationBaseClassComponent extends AbstractBaseClass { /** - * If all Applications are loaded, important for the loader. - * - * @type {boolean} - */ + * If all Applications are loaded, important for the loader. + * + * @type {boolean} + */ isLoaded: boolean = false; FlavorTypeShortcuts: typeof FlavorTypeShortcuts = FlavorTypeShortcuts; /** - * Selected Application. - */ + * Selected Application. + */ selectedApplication: Application; /** - * All available compute centers. - * - * @type {Array} - */ + * All available compute centers. + * + * @type {Array} + */ computeCenters: ComputecenterComponent[] = []; /** - * List of flavor types. - */ + * List of flavor types. + */ typeList: FlavorType[]; /** - * Total number of cores. - * - * @type {number} - */ + * Total number of cores. + * + * @type {number} + */ totalNumberOfCores: number = 0; /** - * Total number of ram. - * - * @type {number} - */ + * Total number of ram. + * + * @type {number} + */ totalRAM: number = 0; /** - * Total number of GPUs - */ + * Total number of GPUs + */ totalGPU: number = 0; newFlavors: { - [id: string]: { - counter: number - flavor: Flavor - } - } = {}; + [id: string]: { + counter: number + flavor: Flavor + } + } = {}; GPU_SHORTCUT = 'GPU'; HMF_SHORTCUT = 'HMF'; @@ -75,57 +77,57 @@ export class ApplicationBaseClassComponent extends AbstractBaseClass { extension_request: boolean = false; /** - * If shortname is valid. - * - * @type {boolean} - */ + * If shortname is valid. + * + * @type {boolean} + */ public wronginput: boolean = false; /** - * - */ + * + */ constantStrings: Object; /** - * List of flavors. - */ + * List of flavors. + */ flavorList: Flavor[] = []; extraResourceCommentRequired: boolean = false; /** - * If all userApplications are loaded, important for the loader. - * - * @type {boolean} - */ + * If all userApplications are loaded, important for the loader. + * + * @type {boolean} + */ isLoaded_userApplication: boolean = false; public project_application_pi_approved: boolean = false; /** - * Name of the project. - */ + * Name of the project. + */ public projectName: string; public project_application_report_allowed: boolean = false; /** - * Applications of the user viewing the Application overview. - * - * @type {Array} - */ + * Applications of the user viewing the Application overview. + * + * @type {Array} + */ user_applications: Application[] = []; constructor( - protected userService: UserService, - protected applicationsService: ApplicationsService, - protected facilityService: FacilityService, - private cdRef: ChangeDetectorRef, + protected userService: UserService, + protected applicationsService: ApplicationsService, + protected facilityService: FacilityService, + private cdRef: ChangeDetectorRef, ) { super(); } /** - * Gets all available compute centers and saves them in the computeCenters attribute. - */ + * Gets all available compute centers and saves them in the computeCenters attribute. + */ getComputeCenters(): void { this.facilityService.getComputeCenters().subscribe((result: [{ [key: string]: string }]): void => { for (const cc of result) { @@ -161,8 +163,8 @@ export class ApplicationBaseClassComponent extends AbstractBaseClass { if ( (entry?.flavor?.type?.shortcut.toUpperCase() === this.GPU_SHORTCUT - || entry?.flavor?.type?.shortcut.toUpperCase() === this.HMF_SHORTCUT) - && entry.counter > 0 + || entry?.flavor?.type?.shortcut.toUpperCase() === this.HMF_SHORTCUT) + && entry.counter > 0 ) { this.extraResourceCommentRequired = true; this.cdRef.detectChanges(); @@ -172,6 +174,48 @@ export class ApplicationBaseClassComponent extends AbstractBaseClass { } this.extraResourceCommentRequired = false; this.cdRef.detectChanges(); + + } + + getApplicationPi(application: Application) { + if (!application.project_application_pi) { + this.applicationsService.getApplicationPI(application.project_application_id).subscribe((pi: User) => { + + application.project_application_pi = pi; + + }); + } + + } + + getApplicationUser(application: Application) { + if (!application.project_application_user) { + this.applicationsService.getApplicationUser(application.project_application_id).subscribe((user: User) => { + + application.project_application_user = user; + + }); + } + } + + getExtensionUser(application: Application) { + if (application.project_lifetime_request && !application.project_lifetime_request.user) { + this.applicationsService.getLifetimeExtensionUser(application.project_application_id).subscribe((user: User) => { + + application.project_lifetime_request.user = user; + + }); + } + } + + getModificationUser(application: Application) { + if (application.project_modification_request && !application.project_modification_request.user) { + this.applicationsService.getLifetimeExtensionUser(application.project_application_id).subscribe((user: User) => { + + application.project_modification_request.user = user; + + }); + } } calculateRamCores(): void { @@ -188,11 +232,11 @@ export class ApplicationBaseClassComponent extends AbstractBaseClass { } /** - * Get details of member like name and email by elixir. - * - * @param application - * @param collapse_id - */ + * Get details of member like name and email by elixir. + * + * @param application + * @param collapse_id + */ public getMemberDetailsByElixirIdIfCollapsed(application: Application, collapse_id: string): void { if (!this.getCollapseStatus(collapse_id)) { this.getMemberDetailsByElixirId(application); @@ -213,11 +257,11 @@ export class ApplicationBaseClassComponent extends AbstractBaseClass { } /** - * Get status name by status id. - * - * @param id - * @returns - */ + * Get status name by status id. + * + * @param id + * @returns + */ public getStatusById(id: number): string { const dummy: string = 'Unknown'; if (Application_States_Strings[Application_States[id]]) { @@ -228,20 +272,20 @@ export class ApplicationBaseClassComponent extends AbstractBaseClass { } /** - * Sets the selected application. - * - * @param application - */ + * Sets the selected application. + * + * @param application + */ setSelectedApplication(application: Application): void { this.selectedApplication = application; } /** - * Uses the param types to safe the available FlavorTypes to the array typeList. - * Also it fills the array collapseList with booleans of value 'false' so all flavor-categories are shown in the application form. - * - * @param types array of all available FlavorTypes - */ + * Uses the param types to safe the available FlavorTypes to the array typeList. + * Also it fills the array collapseList with booleans of value 'false' so all flavor-categories are shown in the application form. + * + * @param types array of all available FlavorTypes + */ setListOfTypes(types: FlavorType[]): void { let index: number = -1; for (let i: number = 0; i < types.length; i += 1) { @@ -260,17 +304,17 @@ export class ApplicationBaseClassComponent extends AbstractBaseClass { } /** - * Check if short name is valid. - * - * @param shortname - */ + * Check if short name is valid. + * + * @param shortname + */ public checkShortname(shortname: string): void { this.wronginput = !/^[a-zA-Z0-9\s]*$/.test(shortname); } /** - * Fills the array constantStrings with values dependent of keys which are used to indicate inputs from the application-form - */ + * Fills the array constantStrings with values dependent of keys which are used to indicate inputs from the application-form + */ generateConstants(): void { this.constantStrings = []; this.constantStrings['project_application_shortname'] = 'Shortname: '; @@ -307,13 +351,13 @@ export class ApplicationBaseClassComponent extends AbstractBaseClass { } /** - * This function concatenates a given key combined with a given value to a string - * which is used on the confirmation-modal. - * - * @param key the key to access a string in the array constantStrings - * @param val the value that is concatenated with the string from the array and an optional addition (depending on the key) - * @returns the concatenated string for the confirmation-modal - */ + * This function concatenates a given key combined with a given value to a string + * which is used on the confirmation-modal. + * + * @param key the key to access a string in the array constantStrings + * @param val the value that is concatenated with the string from the array and an optional addition (depending on the key) + * @returns the concatenated string for the confirmation-modal + */ matchString(key: string, val: string): string { if (key in this.constantStrings) { switch (key) { From 4a2e800431c2cfbe6d01681c609481b58f875f0b Mon Sep 17 00:00:00 2001 From: dweinholz Date: Tue, 7 May 2024 10:24:56 +0200 Subject: [PATCH 02/16] more refactoring --- src/app/api-connector/group.service.ts | 2 +- .../application-card.component.html | 234 ++++++++++ .../application-card.component.scss | 0 .../application-card.component.spec.ts | 23 + .../application-card.component.ts | 439 ++++++++++++++++++ .../application-header.component.html | 1 + .../application-header.component.scss | 0 .../application-header.component.spec.ts | 23 + .../application-header.component.ts | 12 + .../application-list.component.html | 30 ++ .../application-list.component.scss | 3 + .../application-list.component.spec.ts | 23 + .../application-list.component.ts | 40 ++ .../application.model/application.model.ts | 2 +- .../applications/applications.component.html | 338 ++------------ .../applications/applications.component.ts | 32 +- src/app/applications/applications.module.ts | 21 +- .../shared/enums/application-tab-states.ts | 7 + 18 files changed, 902 insertions(+), 328 deletions(-) create mode 100644 src/app/applications/application-card/application-card.component.html create mode 100644 src/app/applications/application-card/application-card.component.scss create mode 100644 src/app/applications/application-card/application-card.component.spec.ts create mode 100644 src/app/applications/application-card/application-card.component.ts create mode 100644 src/app/applications/application-header/application-header.component.html create mode 100644 src/app/applications/application-header/application-header.component.scss create mode 100644 src/app/applications/application-header/application-header.component.spec.ts create mode 100644 src/app/applications/application-header/application-header.component.ts create mode 100644 src/app/applications/application-list/application-list.component.html create mode 100644 src/app/applications/application-list/application-list.component.scss create mode 100644 src/app/applications/application-list/application-list.component.spec.ts create mode 100644 src/app/applications/application-list/application-list.component.ts create mode 100644 src/app/shared/enums/application-tab-states.ts diff --git a/src/app/api-connector/group.service.ts b/src/app/api-connector/group.service.ts index 81c34a6c35..0122236ca0 100644 --- a/src/app/api-connector/group.service.ts +++ b/src/app/api-connector/group.service.ts @@ -75,7 +75,7 @@ export class GroupService { } } - assignGroupToResource(groupid: string, computecenter: string): Observable { + assignGroupToResource(groupid: number, computecenter: string): Observable { const params: HttpParams = new HttpParams().set('compute_center', computecenter); return this.http.post(`${ApiSettings.getApiBaseURL()}projects/${groupid}/resource/`, params, { diff --git a/src/app/applications/application-card/application-card.component.html b/src/app/applications/application-card/application-card.component.html new file mode 100644 index 0000000000..b7bf6c3298 --- /dev/null +++ b/src/app/applications/application-card/application-card.component.html @@ -0,0 +1,234 @@ + + + + + {{ application?.project_application_name }} + {{ application?.project_application_shortname }} + {{ application?.project_application_date_submitted }} + + @if (application?.project_application_user?.username) { + {{ application?.project_application_user?.username }} + } @else { + + + } + + + {{ application?.project_application_institute }} + + + + {{ + application?.project_application_compute_center?.Name + }} + + + +
+ @if ((application | hasstatusinlist: Application_States.CONFIRMATION_DECLINED) || + ((application | hasstatusinlist: Application_States.WAIT_FOR_CONFIRMATION) && + !application?.project_application_compute_center?.FacilityId)) { + + } + + @if (application?.processing_vo_initials && is_vo_admin) { + + + } + @if (!application?.processing_vo_initials && is_vo_admin) { + + } + + + + + + + + + +
+ + + + @if (!isCollapsed) { + + + } + \ No newline at end of file diff --git a/src/app/applications/application-card/application-card.component.scss b/src/app/applications/application-card/application-card.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/applications/application-card/application-card.component.spec.ts b/src/app/applications/application-card/application-card.component.spec.ts new file mode 100644 index 0000000000..13477287c8 --- /dev/null +++ b/src/app/applications/application-card/application-card.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ApplicationCardComponent } from './application-card.component'; + +describe('ApplicationCardComponent', () => { + let component: ApplicationCardComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ApplicationCardComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(ApplicationCardComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/applications/application-card/application-card.component.ts b/src/app/applications/application-card/application-card.component.ts new file mode 100644 index 0000000000..9e21c59ef1 --- /dev/null +++ b/src/app/applications/application-card/application-card.component.ts @@ -0,0 +1,439 @@ +import { + Component, EventEmitter, Input, OnInit, Output, +} from '@angular/core'; +import { NgForOf, NgIf } from '@angular/common'; +import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal'; +import { Subscription } from 'rxjs'; +import { HttpStatusCode } from '@angular/common/http'; +import { AbstractBaseClass, Application_States } from '../../shared/shared_modules/baseClass/abstract-base-class'; +import { ConfirmationActions } from '../../shared/modal/confirmation_actions'; +import { ApplicationsModule } from '../applications.module'; +import { PipeModuleModule } from '../../pipe-module/pipe-module.module'; +import { PublicKeyModule } from '../../shared/shared_modules/public-key/public-key.module'; +import { SharedModuleModule } from '../../shared/shared_modules/shared-module.module'; +import { Application } from '../application.model/application.model'; +import { ApplicationTabStates } from '../../shared/enums/application-tab-states'; +import { ApplicationsService } from '../../api-connector/applications.service'; +import { NotificationModalComponent } from '../../shared/modal/notification-modal'; +import { is_vo } from '../../shared/globalvar'; +import { VoService } from '../../api-connector/vo.service'; +import { ComputecenterComponent } from '../../projectmanagement/computecenter.component'; +import { GroupService } from '../../api-connector/group.service'; +import { ConfirmationModalComponent } from '../../shared/modal/confirmation-modal.component'; +import { ClientLimitsComponent } from '../../vo_manager/clients/modals/client-limits..component'; + +@Component({ + selector: 'app-application-card', + templateUrl: './application-card.component.html', + styleUrl: './application-card.component.scss', +}) +export class ApplicationCardComponent extends AbstractBaseClass implements OnInit { + private subscription: Subscription = new Subscription(); + + @Input() application: Application; + @Input() tabState: ApplicationTabStates = ApplicationTabStates.SUBMITTED; + @Input() computeCenters: ComputecenterComponent[] = []; + @Output() reloadNumbersTrigger: EventEmitter = new EventEmitter(); + @Output() removeApplicationTrigger: EventEmitter = new EventEmitter(); + + bsModalRef: BsModalRef; + is_vo_admin: boolean = false; + selectedComputeCenter: ComputecenterComponent; + + ngOnInit() { + this.is_vo_admin = is_vo; + + } + + constructor( + private applicationsService: ApplicationsService, + private modalService: BsModalService, + private voService: VoService, + private groupService: GroupService, + ) { + super(); + } + + triggerRemoveApplication() { + console.log('trigger remove'); + + this.removeApplicationTrigger.emit(this.application.project_application_id); + } + + triggerReloadNumbers() { + console.log('trigger reload'); + this.reloadNumbersTrigger.emit(); + } + + setCurrentUserProcessingVoManager(application: Application): void { + if (this.is_vo_admin) { + this.voService.setCurrentUserProcessingVoManager(application.project_application_id).subscribe((res: any) => { + application.processing_vo_initials = res['processing_vo_initials']; + }); + } + } + + showConfirmationModal(application: Application, action: ConfirmationActions): void { + let initialState = {}; + if (action === ConfirmationActions.APPROVE_APPLICATION) { + const application_center = !this.selectedComputeCenter.FacilityId; + initialState = { application, action, application_center }; + } else { + initialState = { application, action }; + } + this.bsModalRef = this.modalService.show(ConfirmationModalComponent, { initialState, class: 'modal-lg' }); + this.subscribeToBsModalRef(); + } + + unsetProcessingVoManager(application: Application): void { + if (this.is_vo_admin) { + this.voService.unsetProcessingVoManager(application.project_application_id).subscribe(() => { + application.processing_vo_initials = null; + }); + } + } + + showClientsLimitsModal( + is_modification_request: boolean = false, + ): void { + const initialState = { + compute_center_id: this.selectedComputeCenter.FacilityId, + application: this.application, + is_modification_request, + }; + + this.bsModalRef = this.modalService.show(ClientLimitsComponent, { initialState }); + this.subscribeToBsModalRef(); + } + + removeApplicationFromFacilityConfirmation(): void { + this.groupService.removeGroupFromResource(this.application.project_application_perun_id.toString()).subscribe( + (): void => { + this.getApplication(); + this.showNotificationModal('Success', 'The application was removed from the compute center', 'success'); + }, + (): void => { + this.showNotificationModal('Failed', 'The application was removed from the compute center', 'danger'); + }, + ); + } + + getApplication(): void { + this.applicationsService.getApplication(this.application.project_application_id.toString()).subscribe( + (aj: Application): void => { + this.application = aj; + }, + (error: any): void => { + console.log(error); + }, + ); + } + + assignGroupToFacility(): void { + if (this.selectedComputeCenter) { + this.groupService.assignGroupToResource(this.application.project_application_perun_id, this.selectedComputeCenter.Name).subscribe( + (): void => { + this.getApplication(); + + this.showNotificationModal('Success', 'The project was assigned to the facility.', 'success'); + }, + (error: object): void => { + console.log(error); + this.showNotificationModal('Failed', 'Project could not be created!', 'danger'); + }, + ); + } else { + this.showNotificationModal('Failed', 'You need to select an compute center!', 'danger'); + } + } + + switchCollaps() { + this.isCollapsed = !this.isCollapsed; + } + + showNotificationModal( + notificationModalTitle: string, + notificationModalMessage: string, + notificationModalType: string, + ) { + const initialState = { notificationModalTitle, notificationModalType, notificationModalMessage }; + if (this.bsModalRef) { + this.bsModalRef.hide(); + } + + this.bsModalRef = this.modalService.show(NotificationModalComponent, { initialState }); + this.bsModalRef.setClass('modal-lg'); + } + + adjustApplication(): void { + this.applicationsService.adjustApplication(this.application).subscribe( + (adjustmentResult: Application): void => { + this.application = adjustmentResult; + this.showNotificationModal('Success', 'The resources of the application were adjusted successfully!', 'success'); + }, + (): void => { + this.showNotificationModal('Failed', 'The adjustment of the resources has failed!', 'danger'); + }, + ); + } + + createSimpleVmProjectGroup(): void { + this.showNotificationModal('Info', 'Creating Project...', 'info'); + + if (this.selectedComputeCenter && this.selectedComputeCenter.FacilityId) { + this.groupService.createGroupByApplication(this.application.project_application_id, this.selectedComputeCenter.FacilityId).subscribe( + (res: any): void => { + if (!res['client_available'] && !res['created']) { + this.showNotificationModal( + 'Failed', + `The client ${res['client_name']} has not the necessary resources left!`, + 'danger', + ); + } else { + this.showNotificationModal('Success', 'The project was created!', 'success'); + this.triggerRemoveApplication(); + this.triggerReloadNumbers(); + } + }, + (error: any): void => { + console.log(error); + const errorMessage = error && error.error === 'locked' + ? 'Project is locked and could not be created!' + : 'Project could not be created!'; + + this.showNotificationModal('Failed', errorMessage, 'danger'); + console.log(error); + }, + ); + } + } + + approveModificationRequest(): void { + this.applicationsService.approveModificationRequest(this.application.project_application_id).subscribe( + (res: Response): void => { + this.showNotificationModal('Success', 'The resource modification request was approved!', 'success'); + if (!this.application.project_application_openstack_project) { + if (res.status === HttpStatusCode.Accepted) { + this.triggerRemoveApplication(); + this.triggerReloadNumbers(); + + // this.all_applications.splice(this.all_applications.indexOf(application), 1); + } + } else { + this.getApplication(); + } + }, + (err: any): void => { + console.log('error', err.status); + this.showNotificationModal('Failed', 'Approval of resource modification failed!', 'danger'); + }, + ); + } + + declineModificationRequest(): void { + this.applicationsService.deleteModificationRequest(this.application.project_application_id).subscribe( + (): void => { + this.showNotificationModal('Declined', 'The resource modification request was declined!', 'success'); + this.triggerReloadNumbers(); + this.triggerRemoveApplication(); + + this.getApplication(); + }, + (err: any): void => { + console.log('error', err.status); + this.showNotificationModal('Failed', 'Decline of resource modification failed!', 'danger'); + }, + ); + } + + deleteApplication(): void { + // const idx: number = this.all_applications.indexOf(application); + + this.subscription.add( + this.applicationsService.deleteApplication(this.application.project_application_id).subscribe( + (): void => { + this.showNotificationModal('Success', 'The application has been successfully removed', 'success'); + this.triggerRemoveApplication(); + this.triggerReloadNumbers(); + + }, + (): void => { + this.updateNotificationModal('Failed', 'Application could not be removed!', true, 'danger'); + }, + ), + ); + } + + declineLifetimeExtension(): void { + this.applicationsService.deleteAdditionalLifetimeRequests(this.application.project_application_id).subscribe( + (): void => { + this.showNotificationModal('Declined', 'The project extension was declined!', 'success'); + this.triggerRemoveApplication(); + this.triggerReloadNumbers(); + + }, + (err: any): void => { + console.log('error', err.status); + this.showNotificationModal('Failed', 'Decline of project extension failed!', 'danger'); + }, + ); + } + + declineCreditExtension(): void { + this.applicationsService.deleteAdditionalCreditsRequests(this.application.project_application_id).subscribe( + (): void => { + this.showNotificationModal('Declined', 'The credit extension request was declined!', 'success'); + this.triggerRemoveApplication(); + this.triggerReloadNumbers(); + + }, + (err: any): void => { + console.log('error', err.status); + this.showNotificationModal('Failed', 'Decline of credit extension failed!', 'danger'); + }, + ); + } + + declineApplication(): void { + // const idx: number = this.all_applications.indexOf(app); + this.applicationsService.declineApplication(this.application.project_application_id).subscribe( + (): void => { + const message: string = 'The Application was declined.'; + + this.showNotificationModal('Success', message, 'success'); + this.triggerRemoveApplication(); + this.triggerReloadNumbers(); + + }, + (): void => { + this.showNotificationModal('Failed', 'Application could not be declined!', 'danger'); + }, + ); + } + + approveLifetimeExtension(): void { + this.applicationsService.approveAdditionalLifetime(this.application.project_application_id).subscribe( + (res: Response): void => { + if (this.application.project_application_openstack_project) { + this.getApplication(); + this.showNotificationModal('Success', 'The request has been sent to the facility manager.', 'success'); + } else { + this.showNotificationModal('Success', 'The project has been extended!', 'success'); + } + if (!this.application.project_application_openstack_project) { + if (res.status === HttpStatusCode.Accepted) { + this.triggerReloadNumbers(); + this.triggerRemoveApplication(); + + } + } + }, + (err: any): void => { + console.log('error', err.status); + this.showNotificationModal('Failed', 'Project lifetime could not be extendend!', 'danger'); + }, + ); + } + + approveCreditExtension(): void { + this.applicationsService.approveAdditionalCreditsRequest(this.application.project_application_id).subscribe( + (res: Response): void => { + this.showNotificationModal('Success', 'The credit extension request was approved!', 'success'); + if (!this.application.project_application_openstack_project) { + if (res.status === HttpStatusCode.Accepted) { + this.triggerReloadNumbers(); + this.triggerRemoveApplication(); + + } + } else { + + this.getApplication(); + } + }, + (err: any): void => { + console.log('error', err.status); + this.showNotificationModal('Failed', 'Approval of credit extension failed!', 'danger'); + }, + ); + } + + createOpenStackProjectGroup(): void { + this.groupService.createGroupOpenStack(this.application.project_application_id, this.selectedComputeCenter.Name).subscribe( + (result: { [key: string]: string }): void => { + if (result['Error']) { + this.showNotificationModal('Failed', result['Error'], 'danger'); + } else { + this.showNotificationModal('Success', 'The project was assigned to the facility.', 'success'); + this.getApplication(); + // this.switchApproveLocked(false); + } + }, + (error: any): void => { + const errorMessage = error && error.error === 'locked' + ? 'Project is locked and could not be created!' + : 'Project could not be created!'; + + this.showNotificationModal('Failed', errorMessage, 'danger'); + console.log(error); + }, + ); + } + + /** + * Function to listen to modal results. + */ + subscribeToBsModalRef(): void { + this.subscription.add( + this.bsModalRef.content.event.subscribe((result: any) => { + let action = null; + if ('action' in result) { + action = result['action']; + } + if ('createSimpleVM' in result) { + this.createSimpleVmProjectGroup(); + } + if (action === ConfirmationActions.APPROVE_MODIFICATION) { + this.approveModificationRequest(); + } + if ('closed' in result) { + // this.switchApproveLocked(false); + } + if (action === ConfirmationActions.DECLINE_MODIFICATION) { + this.declineModificationRequest(); + } + if (action === ConfirmationActions.DELETE_APPLICATION) { + this.deleteApplication(); + } + if (action === ConfirmationActions.DECLINE_EXTENSION) { + this.declineLifetimeExtension(); + } + if (action === ConfirmationActions.DECLINE_CREDITS) { + this.declineCreditExtension(); + } + if (action === ConfirmationActions.DECLINE_APPLICATION) { + this.declineApplication(); + } + + if (action === ConfirmationActions.APPROVE_EXTENSION) { + this.approveLifetimeExtension(); + } + if (action === ConfirmationActions.APPROVE_CREDITS) { + this.approveCreditExtension(); + } + if (action === ConfirmationActions.APPROVE_APPLICATION) { + if (this.application.project_application_openstack_project) { + this.createOpenStackProjectGroup(); + } + } + if (action === 'adjustedModificationRequest') { + // this.isLoaded = false; + // this.changeTabState(ApplicationTabStates.MODIFICATION_EXTENSION); + } + }), + ); + } + + isCollapsed: boolean = true; + protected readonly Application_States = Application_States; + protected readonly ConfirmationActions = ConfirmationActions; +} diff --git a/src/app/applications/application-header/application-header.component.html b/src/app/applications/application-header/application-header.component.html new file mode 100644 index 0000000000..adc37ad21f --- /dev/null +++ b/src/app/applications/application-header/application-header.component.html @@ -0,0 +1 @@ +

application-header works!

diff --git a/src/app/applications/application-header/application-header.component.scss b/src/app/applications/application-header/application-header.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/applications/application-header/application-header.component.spec.ts b/src/app/applications/application-header/application-header.component.spec.ts new file mode 100644 index 0000000000..711c3a0d2a --- /dev/null +++ b/src/app/applications/application-header/application-header.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ApplicationHeaderComponent } from './application-header.component'; + +describe('ApplicationHeaderComponent', () => { + let component: ApplicationHeaderComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ApplicationHeaderComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(ApplicationHeaderComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/applications/application-header/application-header.component.ts b/src/app/applications/application-header/application-header.component.ts new file mode 100644 index 0000000000..e1d9284392 --- /dev/null +++ b/src/app/applications/application-header/application-header.component.ts @@ -0,0 +1,12 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-application-header', + standalone: true, + imports: [], + templateUrl: './application-header.component.html', + styleUrl: './application-header.component.scss' +}) +export class ApplicationHeaderComponent { + +} diff --git a/src/app/applications/application-list/application-list.component.html b/src/app/applications/application-list/application-list.component.html new file mode 100644 index 0000000000..f37d77827e --- /dev/null +++ b/src/app/applications/application-list/application-list.component.html @@ -0,0 +1,30 @@ +
+
+ + + + + + + + + + + + + + + + @for (application of applications; track application.project_application_id) { + + + } + +
Project NameShort NameDate submittedUserInstituteCompute CenterActions
+
+
diff --git a/src/app/applications/application-list/application-list.component.scss b/src/app/applications/application-list/application-list.component.scss new file mode 100644 index 0000000000..d505897736 --- /dev/null +++ b/src/app/applications/application-list/application-list.component.scss @@ -0,0 +1,3 @@ +.table-line { + display: contents; +} \ No newline at end of file diff --git a/src/app/applications/application-list/application-list.component.spec.ts b/src/app/applications/application-list/application-list.component.spec.ts new file mode 100644 index 0000000000..b7637d941d --- /dev/null +++ b/src/app/applications/application-list/application-list.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ApplicationListComponent } from './application-list.component'; + +describe('ApplicationListComponent', () => { + let component: ApplicationListComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ApplicationListComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(ApplicationListComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/applications/application-list/application-list.component.ts b/src/app/applications/application-list/application-list.component.ts new file mode 100644 index 0000000000..1219ed1a99 --- /dev/null +++ b/src/app/applications/application-list/application-list.component.ts @@ -0,0 +1,40 @@ +import { + Component, EventEmitter, Input, OnInit, Output, +} from '@angular/core'; +import { NgForOf, NgIf } from '@angular/common'; +import { AbstractBaseClass, Application_States } from '../../shared/shared_modules/baseClass/abstract-base-class'; +import { ConfirmationActions } from '../../shared/modal/confirmation_actions'; + +import { Application } from '../application.model/application.model'; +import { ApplicationTabStates } from '../../shared/enums/application-tab-states'; +import { ComputecenterComponent } from '../../projectmanagement/computecenter.component'; + +@Component({ + selector: 'app-application-list', + + templateUrl: './application-list.component.html', + styleUrl: './application-list.component.scss', +}) +export class ApplicationListComponent { + @Output() reloadNumbersTrigger: EventEmitter = new EventEmitter(); + + @Input() applications: Application[] = []; + @Input() tabState: ApplicationTabStates = ApplicationTabStates.SUBMITTED; + @Input() computeCenters: ComputecenterComponent[] = []; + + triggerReloadNumbers() { + console.log('trigger reload 2'); + this.reloadNumbersTrigger.emit(); + } + + removeApplicationFromList(application_id: string | number) { + const idx: number = this.applications.findIndex((application: Application) => application.project_application_id === application_id); + + if (idx !== -1) { + console.log('remove index'); + this.applications.splice(idx, 1); + } + } + + protected readonly Application_States = Application_States; +} diff --git a/src/app/applications/application.model/application.model.ts b/src/app/applications/application.model/application.model.ts index c07ce90a99..b527e6b100 100644 --- a/src/app/applications/application.model/application.model.ts +++ b/src/app/applications/application.model/application.model.ts @@ -55,7 +55,7 @@ export class Application { project_lifetime_request: ApplicationLifetimeExtension; project_modification_request: ApplicationModification; project_credit_request: ApplicationCreditRequest = null; - project_application_perun_id: number | string; + project_application_perun_id: number; project_application_total_cores: number = 0; project_application_total_ram: number = 0; project_application_initial_credits: number = 0; diff --git a/src/app/applications/applications.component.html b/src/app/applications/applications.component.html index c64f71a57d..5d9aee4ca9 100644 --- a/src/app/applications/applications.component.html +++ b/src/app/applications/applications.component.html @@ -6,55 +6,55 @@
- + + @if(all_applications.length <= 0 && !loading_applications){
-
- -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Project NameShort NameDate submittedUserInstituteCompute CenterActions
- - {{ application?.project_application_name }}{{ application?.project_application_shortname }}{{ application?.project_application_date_submitted }} - @if (application?.project_application_user?.username) { - {{ application?.project_application_user?.username }} - } - @else{ - - - } - - {{ application?.project_application_institute }} - - - {{ - application?.project_application_compute_center?.Name - }} - -
- - - - - - - - +
} + @else{ + } - - - - - -
- - - - -
-
-
- +
- +
-