diff --git a/package-lock.json b/package-lock.json
index 468482af1f..2f55688e60 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "@denbi/cloud-portal-webapp",
- "version": "4.847.0",
+ "version": "4.848.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@denbi/cloud-portal-webapp",
- "version": "4.847.0",
+ "version": "4.848.0",
"dependencies": {
"@angular-eslint/eslint-plugin": "^17.3.0",
"@angular/animations": "17.3.10",
@@ -88,7 +88,7 @@
"eslint-config-airbnb-base": "15.0.0",
"eslint-plugin-import": "^2.29.0",
"eslint-plugin-jsdoc": "48.2.7",
- "eslint-plugin-no-null": "*",
+ "eslint-plugin-no-null": "latest",
"eslint-plugin-prefer-arrow": "1.2.3",
"exports-loader": "5.0.0",
"file-loader": "6.2.0",
@@ -17449,10 +17449,11 @@
}
},
"node_modules/undici": {
- "version": "6.15.0",
- "resolved": "https://registry.npmjs.org/undici/-/undici-6.15.0.tgz",
- "integrity": "sha512-VviMt2tlMg1BvQ0FKXxrz1eJuyrcISrL2sPfBf7ZskX/FCEc/7LeThQaoygsMJpNqrATWQIsRVx+1Dpe4jaYuQ==",
+ "version": "6.19.2",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-6.19.2.tgz",
+ "integrity": "sha512-JfjKqIauur3Q6biAtHJ564e3bWa8VvT+7cSiOJHFbX4Erv6CLGDpg8z+Fmg/1OI/47RA+GI2QZaF48SSaLvyBA==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=18.17"
}
diff --git a/package.json b/package.json
index 5afada3c1a..5dddf1e030 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@denbi/cloud-portal-webapp",
- "version": "4.847.0",
+ "version": "4.848.0",
"description": "de.NBI Cloud Portal",
"scripts": {
"ng": "ng serve",
@@ -124,7 +124,7 @@
"webpack-cli": "5.1.4"
},
"overrides": {
- "undici": "6.15.0",
+ "undici": "6.19.2",
"vite": "5.2.12"
},
"lint-staged": {
diff --git a/src/app/applications/application-card/application-card.component.html b/src/app/applications/application-card/application-card.component.html
index 05d0a2ab73..b3c4b50121 100644
--- a/src/app/applications/application-card/application-card.component.html
+++ b/src/app/applications/application-card/application-card.component.html
@@ -23,6 +23,7 @@
[computeCenters]="computeCenters"
(reloadNumbersTrigger)="triggerReloadNumbers()"
(removeApplicationTrigger)="triggerRemoveApplication()"
+ (reloadApplicationTrigger)="getApplication()"
>
} @else if (facilityView) {
}
@if (!isCollapsed) {
= new EventEmitter();
@Input() facilityView: boolean = false;
@Input() voView: boolean = false;
+ @ViewChild('applicationdetail') applicationDetailComponent: ApplicationDetailComponent;
bsModalRef: BsModalRef;
is_vo_admin: boolean = false;
- selectedComputeCenter: ComputecenterComponent;
ngOnInit() {
this.is_vo_admin = is_vo;
@@ -61,11 +56,7 @@ export class ApplicationCardComponent extends AbstractBaseClass implements OnIni
});
}
- constructor(
- private applicationsService: ApplicationsService,
- private modalService: BsModalService,
- private groupService: GroupService,
- ) {
+ constructor(private applicationsService: ApplicationsService) {
super();
}
@@ -88,288 +79,11 @@ export class ApplicationCardComponent extends AbstractBaseClass implements OnIni
);
}
- resetApplicationPI(): void {
- this.applicationsService.resetPIValidation(this.application).subscribe(() => {
- this.getApplication();
- });
- }
-
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');
- }
-
- 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.FacilityId)
- .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.RESET_PI) {
- this.resetApplicationPI();
- }
- 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;
- protected readonly ApplicationTabStates = ApplicationTabStates;
}
diff --git a/src/app/applications/application-detail/resource-detail/resource-detail.component.html b/src/app/applications/application-detail/resource-detail/resource-detail.component.html
index fba0bb77b4..2f1d6a30fc 100644
--- a/src/app/applications/application-detail/resource-detail/resource-detail.component.html
+++ b/src/app/applications/application-detail/resource-detail/resource-detail.component.html
@@ -101,11 +101,11 @@
0 ? 'green' : 'red'
+ color: flavorDiff.diff >= 0 ? 'green' : 'red'
}"
>
{{ flavorDiff.diff === 0 ? '' : flavorDiff.diff > 0 ? '+' : '-' }}
- {{ flavorDiff.diff }}
+ {{ Math.abs(flavorDiff.diff) }}
diff --git a/src/app/applications/application-facility-actions/application-facility-actions.component.ts b/src/app/applications/application-facility-actions/application-facility-actions.component.ts
index f0435db1b8..a3b3ea5f4e 100644
--- a/src/app/applications/application-facility-actions/application-facility-actions.component.ts
+++ b/src/app/applications/application-facility-actions/application-facility-actions.component.ts
@@ -33,6 +33,7 @@ export class ApplicationFacilityActionsComponent extends AbstractBaseClass {
isCollapsed: boolean = true;
bsModalRef: BsModalRef;
@Output() switchCollapseEvent: EventEmitter = new EventEmitter();
+ @Output() reloadApplicationTrigger: EventEmitter = new EventEmitter();
constructor(
private facilityService: FacilityService,
@@ -54,6 +55,10 @@ export class ApplicationFacilityActionsComponent extends AbstractBaseClass {
this.reloadNumbersTrigger.emit();
}
+ triggerReloadApplication(): void {
+ this.reloadApplicationTrigger.emit();
+ }
+
declineApplication(): void {
this.showNotificationModal('Decline Application', 'Waiting..', 'info');
diff --git a/src/app/applications/application-list/application-list.component.ts b/src/app/applications/application-list/application-list.component.ts
index f761a7873e..c1c4a11e4d 100644
--- a/src/app/applications/application-list/application-list.component.ts
+++ b/src/app/applications/application-list/application-list.component.ts
@@ -15,72 +15,73 @@ import { is_vo } from '../../shared/globalvar';
styleUrl: './application-list.component.scss',
})
export class ApplicationListComponent implements OnInit, OnChanges {
- @Output() reloadNumbersTrigger: EventEmitter = new EventEmitter();
+ @Output() reloadNumbersTrigger: EventEmitter = new EventEmitter();
- @Input() applications: Application[] = [];
- @Input() tabState: ApplicationTabStates = ApplicationTabStates.SUBMITTED;
- @Input() computeCenters: ComputecenterComponent[] = [];
- @Input() facilityView: boolean = false;
- @Input() voView: boolean = false;
- dataTestId: string = '';
+ @Input() applications: Application[] = [];
+ @Input() tabState: ApplicationTabStates = ApplicationTabStates.SUBMITTED;
+ @Input() computeCenters: ComputecenterComponent[] = [];
+ @Input() facilityView: boolean = false;
+ @Input() voView: boolean = false;
- is_vo_admin: boolean = false;
+ dataTestId: string = '';
- ngOnInit() {
- this.is_vo_admin = is_vo;
- this.setDataTestId();
- }
+ is_vo_admin: boolean = false;
- ngOnChanges(changes: SimpleChanges) {
- this.setDataTestId();
+ ngOnInit() {
+ this.is_vo_admin = is_vo;
+ this.setDataTestId();
+ }
- }
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ ngOnChanges(changes: SimpleChanges) {
+ this.setDataTestId();
+ }
- setDataTestId(): void {
- console.log('set data test id');
- switch (this.tabState) {
- case ApplicationTabStates.SUBMITTED: {
- this.dataTestId = 'submitted_applications_container';
- break;
- }
- case ApplicationTabStates.CREDITS_EXTENSION: {
- this.dataTestId = 'credits_requests_applications_container';
- break;
- }
- case ApplicationTabStates.LIFETIME_EXTENSION: {
- this.dataTestId = 'lifetime_requests_applications_container';
- break;
- }
- case ApplicationTabStates.MODIFICATION_EXTENSION: {
- this.dataTestId = 'modification_requests_applications_container';
- break;
- }
- case ApplicationTabStates.TERMINATION_REQUEST: {
- this.dataTestId = 'termination_requests_applications_container';
- break;
- }
- default: {
- break;
- }
+ setDataTestId(): void {
+ console.log('set data test id');
+ switch (this.tabState) {
+ case ApplicationTabStates.SUBMITTED: {
+ this.dataTestId = 'submitted_applications_container';
+ break;
+ }
+ case ApplicationTabStates.CREDITS_EXTENSION: {
+ this.dataTestId = 'credits_requests_applications_container';
+ break;
+ }
+ case ApplicationTabStates.LIFETIME_EXTENSION: {
+ this.dataTestId = 'lifetime_requests_applications_container';
+ break;
+ }
+ case ApplicationTabStates.MODIFICATION_EXTENSION: {
+ this.dataTestId = 'modification_requests_applications_container';
+ break;
+ }
+ case ApplicationTabStates.TERMINATION_REQUEST: {
+ this.dataTestId = 'termination_requests_applications_container';
+ break;
+ }
+ default: {
+ break;
}
- console.log(this.dataTestId);
}
+ console.log(this.dataTestId);
+ }
- triggerReloadNumbers() {
- console.log('trigger reload 2');
- this.reloadNumbersTrigger.emit();
- }
+ 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,
- );
+ 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);
- }
+ if (idx !== -1) {
+ console.log('remove index');
+ this.applications.splice(idx, 1);
}
+ }
- protected readonly Application_States = Application_States;
+ protected readonly Application_States = Application_States;
}
diff --git a/src/app/applications/application-vo-actions/application-vo-actions.component.ts b/src/app/applications/application-vo-actions/application-vo-actions.component.ts
index 68191fff20..958abb6c2f 100644
--- a/src/app/applications/application-vo-actions/application-vo-actions.component.ts
+++ b/src/app/applications/application-vo-actions/application-vo-actions.component.ts
@@ -33,6 +33,7 @@ export class ApplicationVoActionsComponent extends AbstractBaseClass implements
@Input() tabState: ApplicationTabStates = ApplicationTabStates.SUBMITTED;
@Input() computeCenters: ComputecenterComponent[] = [];
@Output() reloadNumbersTrigger: EventEmitter = new EventEmitter();
+ @Output() reloadApplicationTrigger: EventEmitter = new EventEmitter();
@Output() removeApplicationTrigger: EventEmitter = new EventEmitter();
@Output() switchCollapseEvent: EventEmitter = new EventEmitter();
@@ -64,7 +65,7 @@ export class ApplicationVoActionsComponent extends AbstractBaseClass implements
.showAdjustLifetimeExtensionModal(this.application)
.subscribe((eventSuccess: boolean) => {
if (eventSuccess) {
- this.getApplication();
+ this.triggerReloadApplication();
this.showNotificationModal(
'Success',
'The lifetime of the extension request were adjusted successfully!',
@@ -76,10 +77,14 @@ export class ApplicationVoActionsComponent extends AbstractBaseClass implements
});
}
+ triggerReloadApplication(): void {
+ this.reloadApplicationTrigger.emit();
+ }
+
showAdjustApplicationModal() {
this.adjustApplicationModal.showAdjustApplicationModal(this.application).subscribe((changed: boolean) => {
if (changed) {
- this.getApplication();
+ this.triggerReloadApplication();
this.showNotificationModal('Success', 'The resources of the application were adjusted successfully!', 'success');
} else {
@@ -160,7 +165,7 @@ export class ApplicationVoActionsComponent extends AbstractBaseClass implements
removeApplicationFromFacilityConfirmation(): void {
this.groupService.removeGroupFromResource(this.application.project_application_perun_id.toString()).subscribe(
(): void => {
- this.getApplication();
+ this.triggerReloadApplication();
this.showNotificationModal('Success', 'The application was removed from the compute center', 'success');
},
(): void => {
@@ -169,24 +174,13 @@ export class ApplicationVoActionsComponent extends AbstractBaseClass implements
);
}
- 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.FacilityId)
.subscribe(
(): void => {
- this.getApplication();
+ this.triggerReloadApplication();
this.showNotificationModal('Success', 'The project was assigned to the facility.', 'success');
},
@@ -202,7 +196,7 @@ export class ApplicationVoActionsComponent extends AbstractBaseClass implements
resetApplicationPI(): void {
this.applicationsService.resetPIValidation(this.application).subscribe(() => {
- this.getApplication();
+ this.triggerReloadApplication();
});
}
@@ -269,7 +263,7 @@ export class ApplicationVoActionsComponent extends AbstractBaseClass implements
// this.all_applications.splice(this.all_applications.indexOf(application), 1);
}
} else {
- this.getApplication();
+ this.triggerReloadApplication();
}
},
(err: any): void => {
@@ -286,7 +280,7 @@ export class ApplicationVoActionsComponent extends AbstractBaseClass implements
this.triggerReloadNumbers();
this.triggerRemoveApplication();
- this.getApplication();
+ this.triggerReloadApplication();
},
(err: any): void => {
console.log('error', err.status);
@@ -360,7 +354,7 @@ export class ApplicationVoActionsComponent extends AbstractBaseClass implements
this.applicationsService.approveAdditionalLifetime(this.application.project_application_id).subscribe(
(res: Response): void => {
if (this.application.project_application_openstack_project) {
- this.getApplication();
+ this.triggerReloadApplication();
this.showNotificationModal('Success', 'The request has been sent to the facility manager.', 'success');
} else {
this.showNotificationModal('Success', 'The project has been extended!', 'success');
@@ -389,7 +383,7 @@ export class ApplicationVoActionsComponent extends AbstractBaseClass implements
this.triggerRemoveApplication();
}
} else {
- this.getApplication();
+ this.triggerReloadApplication();
}
},
(err: any): void => {
@@ -408,7 +402,7 @@ export class ApplicationVoActionsComponent extends AbstractBaseClass implements
this.showNotificationModal('Failed', result['Error'], 'danger');
} else {
this.showNotificationModal('Success', 'The project was assigned to the facility.', 'success');
- this.getApplication();
+ this.triggerReloadApplication();
// this.switchApproveLocked(false);
}
},
diff --git a/src/app/applications/type-overview.component.html b/src/app/applications/type-overview.component.html
index 174f0f468a..e0561c5a7d 100644
--- a/src/app/applications/type-overview.component.html
+++ b/src/app/applications/type-overview.component.html
@@ -1,762 +1,722 @@
@if (is_vo_admin) {
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Ease of Use – SimpleVM lets you start and stop VMs with a few
- clicks
- which means that
- there is no additional configuration necessary.
-
-
-
-
-
-
-
-
-
- Flat Learning Curve – If you want to use SimpleVM you don’t have to
- learn about
- resource management, like network configurations or storage management. No
- background
- knowledge in Cloud
- Computing is necessary.
-
-
-
-
-
-
-
-
-
- Remote Desktop – If you don’t want to use SimpleVM via terminal you
- can
- easily connect
- to your VM using a Remote Desktop.
-
-
-
-
-
-
-
-
-
Workshop support – Create a workshop and start multiple configured
- VMs
- for your
- participants with just a few clicks. To learn more about our workshop support click
-
here .
-
-
-
-
-
-
-
Typical use cases:
-
-
- Tools and Pipelines: SimpleVM is a quick way to get up and
- running with your tools
- and pipelines in the cloud.
-
-
- Remote Desktop: Launch your own Desktop or a browser-based
- IDE
- in the cloud (based
- on Apache Guacamole, RStudio, Theia IDE, and more) and be able to run
- programs
- with graphical user
- interfaces in addition to the command-line.
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Ease of Use – SimpleVM lets you start and stop VMs with a few clicks which means that
+ there is no additional configuration necessary.
+
+
+
+
+
+
+
+
+
+ Flat Learning Curve – If you want to use SimpleVM you don’t have to learn about
+ resource management, like network configurations or storage management. No background knowledge in
+ Cloud Computing is necessary.
+
+
+
+
+
+
+
+
+
+ Remote Desktop – If you don’t want to use SimpleVM via terminal you can easily
+ connect to your VM using a Remote Desktop.
+
+
+
+
+
+
+
+
+
Workshop support – Create a workshop and start multiple configured VMs for your
+ participants with just a few clicks. To learn more about our workshop support click
+
here .
+
+
+
+
+
+
+
Typical use cases:
+
+
+ Tools and Pipelines: SimpleVM is a quick way to get up and running with your
+ tools and pipelines in the cloud.
+
+
+ Remote Desktop: Launch your own Desktop or a browser-based IDE in the cloud
+ (based on Apache Guacamole, RStudio, Theia IDE, and more) and be able to run programs with
+ graphical user interfaces in addition to the command-line.
+
+
+
+
-
-
-
-
-
-
- Want to learn more about SimpleVM?
-
-
-
-
-
-
-
+
+
+
+
+
+
+ Want to learn more about SimpleVM?
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Scalability – Easy scaling of virtual machines and distribution of
- data
- for efficient
- computations.
-
-
-
-
-
-
-
-
-
- Configurability – OpenStack allows the configuration of any
- resource
- type available,
- such as virtual machines, network, block and object storage.
-
-
-
-
-
-
-
-
-
- API Access – Any interaction with OpenStack can be automated via
- its
- API enabling users
- to script actions, e.g. starting and stopping of virtual machines.
-
-
-
-
-
-
-
Typical use cases:
-
-
- Distributed Workflows: Distribution of workflows on
- multiple
- virtual machines.
-
- Services: The ability to offer services for other
- researchers.
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Scalability – Easy scaling of virtual machines and distribution of data for efficient
+ computations.
+
+
+
+
+
+
+
+
+
+ Configurability – OpenStack allows the configuration of any resource type available,
+ such as virtual machines, network, block and object storage.
+
+
+
+
+
+
+
+
+
+ API Access – Any interaction with OpenStack can be automated via its API enabling
+ users to script actions, e.g. starting and stopping of virtual machines.
+
+
+
+
+
+
+
Typical use cases:
+
+
+ Distributed Workflows: Distribution of workflows on multiple virtual machines.
+
+ Services: The ability to offer services for other researchers.
+
+
+
-
-
-
-
-
-
- Want to learn more about Openstack?
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Scalability – Easy scaling of virtual machines and distribution of
- data for efficient
- computations.
-
-
-
-
-
-
-
-
-
- Configurability – Kubernetes allows the configuration of any
- resource type available,
- such as virtual machines, network.
-
-
-
-
-
-
-
-
-
- API Access – Any interaction with Kubernetes can be automated via
- its API enabling
- users to script actions, e.g. starting and stopping of containerized services.
-
-
-
-
-
-
-
Typical use cases:
-
-
- Workflow Engines: Workflow engines like Nextflow allow you
- to run your analysis on
- top of Kubernetes.
-
-
- Services: The ability to offer stateless and stateful
- (databases) services for
- other researchers.
-
-
-
-
+
+
+
+
+
+
+ Want to learn more about Openstack?
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Scalability – Easy scaling of containers and distribution of data for efficient
+ computations.
+
+
+
+
+
+
+
+
+
+ Configurability – Kubernetes allows the configuration of any resource type available,
+ such as virtual machines, network.
+
+
+
+
+
+
+
+
+
+ API Access – Any interaction with Kubernetes can be automated via its API enabling
+ users to script actions, e.g. starting and stopping of containerized services.
+
+
+
+
+
+
+
Typical use cases:
+
+
+ Workflow Engines: Workflow engines like Nextflow allow you to run your analysis
+ on top of Kubernetes.
+
+
+ Services: The ability to offer stateless and stateful (databases) services for
+ other researchers.
+
+
+
+
-
-
-
-
-
- Want to learn more about Kubernetes?
-
-
-
-
-
-
-
+
+
+
+
+
+ Want to learn more about Kubernetes?
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
- Want to learn more about SimpleVM?
-
-
-
-
-
-
-
- Want to learn more about Openstack?
-
-
-
-
-
- Want to learn more about Kubernetes?
-
-
-
-
-
-
-
-
-
-
- Want to learn the Difference?
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+ Want to learn more about SimpleVM?
+
+
+
+
+
+
+
+ Want to learn more about Openstack?
+
+
+
+
+
+ Want to learn more about Kubernetes?
+
+
+
+
+
+
+
+
+
+ Want to learn the Difference?
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
} @else {
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Ease of Use – SimpleVM lets you start and stop VMs with a few
- clicks which means that
- there is no additional configuration necessary.
-
-
-
-
-
-
-
-
-
- Flat Learning Curve – If you want to use SimpleVM you don’t have to
- learn about
- resource management, like network configurations or storage management. No
- background knowledge in Cloud
- Computing is necessary.
-
-
-
-
-
-
-
-
-
- Remote Desktop – If you don’t want to use SimpleVM via terminal you
- can easily connect
- to your VM using a Remote Desktop.
-
-
-
-
-
-
-
-
-
Workshop support – Create a workshop and start multiple configured
- VMs for your
- participants with just a few clicks. To learn more about our workshop support click
-
here .
-
-
-
-
-
-
-
-
- Typical use cases:
-
-
-
-
-
-
- • Tools and Pipelines
- – SimpleVM is a quick way to get up and running with your tools and pipelines in
- the cloud.
-
-
-
-
-
-
- • Remote Desktop
- – Launch your own Desktop or a browser based IDE in the cloud (based on Apache
- Guacamole, RStudio,
- Theia IDE and more) and be able to run programs with graphical user interfaces
- in addition to the
- command-line.
-
-
-
-
-
-
-
-
-
- Want to learn more about SimpleVM?
-
-
-
-
-
-
-
-
-
-
-
-
- Want to learn the Difference between SimpleVM and Openstack?
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Ease of Use – SimpleVM lets you start and stop VMs with a few clicks which means that
+ there is no additional configuration necessary.
+
+
+
+
+
+
+
+
+
+ Flat Learning Curve – If you want to use SimpleVM you don’t have to learn about
+ resource management, like network configurations or storage management. No background knowledge in
+ Cloud Computing is necessary.
+
+
+
+
+
+
+
+
+
+ Remote Desktop – If you don’t want to use SimpleVM via terminal you can easily
+ connect to your VM using a Remote Desktop.
+
+
+
+
+
+
+
+
+
Workshop support – Create a workshop and start multiple configured VMs for your
+ participants with just a few clicks. To learn more about our workshop support click
+
here .
+
+
+
+
+
+
+
+
+ Typical use cases:
+
+
+
+
+
+
+ • Tools and Pipelines
+ – SimpleVM is a quick way to get up and running with your tools and pipelines in the cloud.
+
+
+
+
+
+
+ • Remote Desktop
+ – Launch your own Desktop or a browser based IDE in the cloud (based on Apache Guacamole, RStudio,
+ Theia IDE and more) and be able to run programs with graphical user interfaces in addition to the
+ command-line.
+
+
+
+
+
+
+
+
+
+ Want to learn more about SimpleVM?
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Scalability – Easy scaling of virtual machines and distribution of
- data for efficient
- computations.
-
-
-
-
-
-
-
-
-
- Configurability – OpenStack allows the configuration of any
- resource type available,
- such as virtual machines, network, block and object storage.
-
-
-
-
-
-
-
-
-
- API Access – Any interaction with OpenStack can be automated via
- its API enabling users
- to script actions, e.g. starting and stopping of virtual machines.
-
-
-
-
-
-
-
-
- Typical use cases:
-
-
-
-
-
-
- • Distributed Workflows
- – Distribution of workflows on multiple virtual machines.
-
-
-
-
-
-
- • Services
- – The ability to offer services for other researchers.
-
-
-
-
-
-
-
-
-
- Want to learn more about Openstack?
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Want to learn more about SimpleVM?
-
-
-
-
-
-
- Want to learn the Difference?
-
-
-
-
-
-
- Want to learn more about Openstack?
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+ Want to learn the Difference between SimpleVM and Openstack?
+
+
+
+
-}
\ No newline at end of file
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Scalability – Easy scaling of virtual machines and distribution of data for efficient
+ computations.
+
+
+
+
+
+
+
+
+
+ Configurability – OpenStack allows the configuration of any resource type available,
+ such as virtual machines, network, block and object storage.
+
+
+
+
+
+
+
+
+
+ API Access – Any interaction with OpenStack can be automated via its API enabling
+ users to script actions, e.g. starting and stopping of virtual machines.
+
+
+
+
+
+
+
+
+ Typical use cases:
+
+
+
+
+
+
+ • Distributed Workflows
+ – Distribution of workflows on multiple virtual machines.
+
+
+
+
+
+
+ • Services
+ – The ability to offer services for other researchers.
+
+
+
+
+
+
+
+
+
+ Want to learn more about Openstack?
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Want to learn more about SimpleVM?
+
+
+
+
+
+
+ Want to learn the Difference?
+
+
+
+
+
+
+ Want to learn more about Openstack?
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+}
diff --git a/src/app/facility_manager/facilityprojectsoverview.component.html b/src/app/facility_manager/facilityprojectsoverview.component.html
index cd9cc581dd..d7feed64bd 100644
--- a/src/app/facility_manager/facilityprojectsoverview.component.html
+++ b/src/app/facility_manager/facilityprojectsoverview.component.html
@@ -45,8 +45,7 @@
-
-
+
Send CSV Templated Email
diff --git a/src/app/facility_manager/facilityprojectsoverview.component.ts b/src/app/facility_manager/facilityprojectsoverview.component.ts
index 4bbf018218..b722a8d618 100644
--- a/src/app/facility_manager/facilityprojectsoverview.component.ts
+++ b/src/app/facility_manager/facilityprojectsoverview.component.ts
@@ -1,7 +1,7 @@
import {
Component, Input, OnInit, QueryList, ViewChildren, inject,
} from '@angular/core';
-import { Observable, Subject, take } from 'rxjs';
+import { Observable, take } from 'rxjs';
import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal';
import { MatomoTracker } from 'ngx-matomo-client';
import { ProjectMember } from '../projectmanagement/project_member.model';
@@ -19,10 +19,10 @@ import {
import { ProjectSortService } from '../shared/shared_modules/services/project-sort.service';
import { AbstractBaseClass } from '../shared/shared_modules/baseClass/abstract-base-class';
import { ProjectEmailModalComponent } from '../shared/modal/email/project-email-modal/project-email-modal.component';
-import { NotificationModalComponent } from '../shared/modal/notification-modal';
import { MembersListModalComponent } from '../shared/modal/members/members-list-modal.component';
import { EmailService } from '../api-connector/email.service';
import { CsvMailTemplateModel } from '../shared/classes/csvMailTemplate.model';
+import { ProjectCsvTemplatedEmailModalComponent } from '../shared/modal/email/project-csv-templated-email-modal/project-csv-templated-email-modal.component';
/**
* Facility Project overview component.
@@ -43,9 +43,7 @@ export class FacilityProjectsOverviewComponent extends AbstractBaseClass impleme
public memberFilter: string = '';
filteredMembers: object[] = [];
selectedMember: object[] = [];
- facility_members: object[] = [];
- filterChanged: Subject = new Subject();
isLoaded: boolean = false;
projects: Application[] = [];
projectsCopy: Application[] = [];
@@ -183,7 +181,11 @@ export class FacilityProjectsOverviewComponent extends AbstractBaseClass impleme
);
}
}
+ openProjectCSVMailModal(): void {
+ console.log('show');
+ this.bsModalRef = this.modalService.show(ProjectCsvTemplatedEmailModalComponent, { class: 'modal-lg' });
+ }
onSort({ column, direction }: SortEvent) {
// resetting other headers
this.headers.forEach(header => {
@@ -196,10 +198,6 @@ export class FacilityProjectsOverviewComponent extends AbstractBaseClass impleme
this.sortProjectService.sortDirection = direction;
}
- searchForUserInFacility(searchString: string): void {
- this.facilityService.getFilteredMembersOfFacility(searchString);
- }
-
filterMembers(bare_searchString: string): void {
this.filteredMembers = [];
const searchString: string = bare_searchString.toLowerCase();
@@ -516,24 +514,6 @@ export class FacilityProjectsOverviewComponent extends AbstractBaseClass impleme
initialState = { selectedProjects: this.selectedEmailProjects };
}
this.bsModalRef = this.modalService.show(ProjectEmailModalComponent, { initialState, class: 'modal-lg' });
- this.bsModalRef.content.event.subscribe((sent_successfully: boolean) => {
- if (sent_successfully) {
- const initialStateNotification = {
- notificationModalTitle: 'Success',
- notificationModalType: 'success',
- notificationModalMessage: 'Mails were successfully sent',
- };
-
- this.modalService.show(NotificationModalComponent, { initialState: initialStateNotification });
- } else {
- const initialStateNotification = {
- notificationModalTitle: 'Failed',
- notificationModalType: 'danger',
- notificationModalMessage: 'Failed to send mails!',
- };
- this.modalService.show(NotificationModalComponent, { initialState: initialStateNotification });
- }
- });
}
setFacilitySupportMails(supportMails: string): void {
diff --git a/src/app/projectmanagement/modals/withdraw/withdraw-modal.component.ts b/src/app/projectmanagement/modals/withdraw/withdraw-modal.component.ts
index ff6ca1edcf..bd8eef3a06 100644
--- a/src/app/projectmanagement/modals/withdraw/withdraw-modal.component.ts
+++ b/src/app/projectmanagement/modals/withdraw/withdraw-modal.component.ts
@@ -39,6 +39,8 @@ export class WithdrawModalComponent {
this.event.emit(true);
});
break;
+ default:
+ this.event.emit(false);
}
}
diff --git a/src/app/projectmanagement/projectmanagement.module.ts b/src/app/projectmanagement/projectmanagement.module.ts
index 2b53063b0a..96bd65b697 100644
--- a/src/app/projectmanagement/projectmanagement.module.ts
+++ b/src/app/projectmanagement/projectmanagement.module.ts
@@ -25,6 +25,7 @@ import { ResultComponent } from './modals/result/result.component';
import { SharedModuleModule } from '../shared/shared_modules/shared-module.module';
import { AdjustLifetimeRequestComponent } from './modals/adjust-lifetime/adjust-lifetime-request.component';
import { AdjustApplicationComponent } from './modals/adjust-application/adjust-application.component';
+import { WithdrawModalComponent } from './modals/withdraw/withdraw-modal.component';
/**
* Projectmanagment module.
@@ -59,6 +60,7 @@ import { AdjustApplicationComponent } from './modals/adjust-application/adjust-a
ExtensionEntryComponent,
AdjustLifetimeRequestComponent,
AdjustApplicationComponent,
+ WithdrawModalComponent,
],
exports: [ProjectOsDetailsComponent, ExtensionEntryComponent],
})
diff --git a/src/app/shared/modal/email/project-csv-templated-email-modal/project-csv-templated-email-modal.component.html b/src/app/shared/modal/email/project-csv-templated-email-modal/project-csv-templated-email-modal.component.html
new file mode 100644
index 0000000000..adb3016ba2
--- /dev/null
+++ b/src/app/shared/modal/email/project-csv-templated-email-modal/project-csv-templated-email-modal.component.html
@@ -0,0 +1,194 @@
+
+
+
+ @if (!csvFile || csvMailTemplate?.errors?.length > 0) {
+
+
+ Valid Example:
+
+
{{ validCSVExample }}
+
+
The following template keys would be provided by this CSV file:
+
+ {VM}
+ {LOCATION}
+
+
+
+ }
+
+
+
+ @if (csvMailTemplate?.errors?.length > 0) {
+
+
Error! Unable to use the CSV file "{{ csvFile.name }}". The following errors occurred:
+
+
+
+ }
+ @if (csvMailTemplate?.warnings?.length > 0) {
+
+
+ Warning! The following issues were found in the CSV file, but sending emails is still possible:
+
+
+
+
+ }
+
+ @if (csvMailTemplate?.valid_projects?.length > 0) {
+
+
Send Mail to:
+
+ {{ pr.project_application_shortname }}
+
+
+ }
+
+
+ @if (csvFile) {
+
+
+ The following keys were provided by the CSV file: {{ csvFile.name }}
+
+
+ {{ '{' + template + '}' }}
+
+
+
+
+
You can use the following keys as variables:
+
+ {{ '{' + template + '}' }}
+
+
+
+
+ Please consider: In case any dates are part of the sent E-Mails, they will be formatted in the german
+ TT.MM.YYYY -format.
+
+ }
+
+
+
diff --git a/src/app/shared/modal/email/project-csv-templated-email-modal/project-csv-templated-email-modal.component.ts b/src/app/shared/modal/email/project-csv-templated-email-modal/project-csv-templated-email-modal.component.ts
new file mode 100644
index 0000000000..aa5c04b137
--- /dev/null
+++ b/src/app/shared/modal/email/project-csv-templated-email-modal/project-csv-templated-email-modal.component.ts
@@ -0,0 +1,94 @@
+import {
+ Component, EventEmitter, OnDestroy, OnInit,
+} from '@angular/core';
+import { BsModalRef } from 'ngx-bootstrap/modal';
+import { Application } from '../../../../applications/application.model/application.model';
+import { EmailService } from '../../../../api-connector/email.service';
+import { STATUS_LINK } from '../../../../../links/links';
+import { CsvMailTemplateModel } from '../../../classes/csvMailTemplate.model';
+import { NotificationModalComponent } from '../../notification-modal';
+
+@Component({
+ selector: 'app-project-csv-templated-email-modal',
+ templateUrl: './project-csv-templated-email-modal.component.html',
+ styleUrls: ['./project-csv-templated-email.scss'],
+ providers: [EmailService],
+})
+export class ProjectCsvTemplatedEmailModalComponent implements OnInit, OnDestroy {
+ csvMailTemplate: CsvMailTemplateModel;
+ csvFile: File;
+
+ emailAdminsOnly: boolean;
+ emailSubject: string;
+ emailReply: string;
+ emailText: string;
+ templates: string[];
+ validCSVExample = `Project, VM, LOCATION
+Proj1, VM_1, Bielefeld
+Proj2, VM_2, Giessen`;
+
+ public event: EventEmitter = new EventEmitter();
+
+ constructor(
+ public bsModalRef: BsModalRef,
+ private emailService: EmailService,
+ private notificationModal: NotificationModalComponent,
+ ) {
+ // eslint-disable-next-line no-empty-function
+ }
+
+ ngOnInit() {
+ this.getMailTemplates();
+ }
+
+ onCsvFileSelected(event): void {
+ const inputElement = event.target as HTMLInputElement;
+ this.csvFile = inputElement.files[0];
+ if (this.csvFile) {
+ this.emailService.sendCsvTemplate(this.csvFile).subscribe(
+ (csvTemplate: CsvMailTemplateModel) => {
+ this.csvMailTemplate = csvTemplate;
+ },
+ (error: CsvMailTemplateModel) => {
+ this.csvMailTemplate = error;
+ console.log(error['error']);
+ },
+ );
+ }
+ }
+
+ getMailTemplates(): void {
+ this.emailService.getMailTemplates().subscribe((res: string[]) => {
+ this.templates = res;
+ });
+ }
+
+ sentProjectsTemplatedMail(): void {
+ const project_ids = this.csvMailTemplate.valid_projects.map((pr: Application) => pr.project_application_perun_id);
+ this.notificationModal.showInfoNotificationModal('Info', 'Sending Mails...');
+
+ this.emailService
+ .sendCsvTemplatedMail(
+ this.csvFile,
+ project_ids,
+ this.emailSubject,
+ this.emailText,
+ this.emailAdminsOnly,
+ this.emailReply,
+ )
+ .subscribe(
+ () => {
+ this.notificationModal.showSuccessFullNotificationModal('Success', 'Mails were successfully sent!');
+ },
+ () => {
+ this.notificationModal.showDangerNotificationModal('Failed', 'Failed to send mails!');
+ },
+ );
+ }
+
+ ngOnDestroy(): void {
+ this.bsModalRef.hide();
+ }
+
+ protected readonly STATUS_LINK = STATUS_LINK;
+}
diff --git a/src/app/shared/modal/email/project-csv-templated-email-modal/project-csv-templated-email.scss b/src/app/shared/modal/email/project-csv-templated-email-modal/project-csv-templated-email.scss
new file mode 100644
index 0000000000..a86e9ffc41
--- /dev/null
+++ b/src/app/shared/modal/email/project-csv-templated-email-modal/project-csv-templated-email.scss
@@ -0,0 +1,25 @@
+.templates-container {
+ border: 1px solid #ccc;
+ padding: 10px;
+ background-color: #f9f9f9;
+}
+
+.templates-list {
+ margin: 10px 0;
+ font-family: monospace;
+}
+
+.valid-example {
+ border: 1px solid #ccc;
+ padding: 10px;
+ background-color: #f9f9f9;
+ margin-top: 10px;
+}
+
+.valid-example-heading {
+ margin-bottom: 5px;
+}
+
+.valid-example-content {
+ font-family: monospace;
+}
\ No newline at end of file
diff --git a/src/app/shared/modal/email/project-email-modal/project-email-modal.component.html b/src/app/shared/modal/email/project-email-modal/project-email-modal.component.html
index 5584431736..1b9623c3e8 100644
--- a/src/app/shared/modal/email/project-email-modal/project-email-modal.component.html
+++ b/src/app/shared/modal/email/project-email-modal/project-email-modal.component.html
@@ -16,31 +16,6 @@ Sent Mail to spe
{{ pr.project_application_shortname }}
- 0">
-
-
Error! Unable to use the CSV file "{{ csvFile.name }}". The following errors occurred:
-
-
-
-
-
- Valid Example:
-
-
{{ validCSVExample }}
-
-
-
- 0">
-
- Warning! The following issues were found in the CSV file, but sending emails is still possible:
-
-
-
-
@@ -52,7 +27,6 @@
Sent Mail to spe
name="emailAdminsOnly"
[(ngModel)]="emailAdminsOnly"
id="adminOnlyCheckbox"
- [disabled]="csvMailTemplate?.errors?.length > 0"
/>
Group administrators only
@@ -75,7 +49,6 @@ Sent Mail to spe
class="form-control"
type="text"
[(ngModel)]="emailSubject"
- [disabled]="csvMailTemplate?.errors?.length > 0"
minlength="1"
#emailSub="ngModel"
[ngClass]="{
@@ -96,7 +69,6 @@ Sent Mail to spe
class="form-control"
type="text"
[(ngModel)]="emailReply"
- [disabled]="csvMailTemplate?.errors.length > 0"
#emailRep="ngModel"
pattern="([ ]*)(?!(^[.-].*|[^@]*[.-]@|.*\.{2,}.*)|^.{254}.)([a-zA-Z0-9!#$%&'*+\/=?^_`{|}~.-]+@)(?!-.*|.*-\.)([a-zA-Z0-9-]{1,63}\.)+[a-zA-Z]{2,15}([ ]*)"
[ngClass]="{
@@ -117,7 +89,6 @@ Sent Mail to spe
id="emailText"
name="emailText"
[(ngModel)]="emailText"
- [disabled]="csvMailTemplate?.errors?.length > 0"
type="text"
#emailT="ngModel"
[ngClass]="{
@@ -128,14 +99,6 @@ Sent Mail to spe
-
-
- The following keys were provided by the CSV file: {{ csvFile.name }}
-
-
- {{ '{' + template + '}' }}
-
-
You can use the following keys as variables:
@@ -159,21 +122,11 @@
Sent Mail to spe
Abort & Close
0"
+ [disabled]="f.invalid"
data-test-id="confirm_confirmation_modal_btn"
(click)="sentProjectsMail(); bsModalRef.hide()"
>
Send Mail
- 0"
- data-test-id="confirm_confirmation_modal_btn"
- (click)="sentProjectsTemplatedMail(); bsModalRef.hide()"
- >
- Send Mail
-
diff --git a/src/app/shared/modal/email/project-email-modal/project-email-modal.component.ts b/src/app/shared/modal/email/project-email-modal/project-email-modal.component.ts
index 40c9dce8ef..aa9e6ce5d6 100644
--- a/src/app/shared/modal/email/project-email-modal/project-email-modal.component.ts
+++ b/src/app/shared/modal/email/project-email-modal/project-email-modal.component.ts
@@ -3,11 +3,9 @@ import {
} from '@angular/core';
import { BsModalRef } from 'ngx-bootstrap/modal';
import { Application } from '../../../../applications/application.model/application.model';
-import { VoService } from '../../../../api-connector/vo.service';
-import { IResponseTemplate } from '../../../../api-connector/response-template';
import { EmailService } from '../../../../api-connector/email.service';
import { STATUS_LINK } from '../../../../../links/links';
-import { CsvMailTemplateModel } from '../../../classes/csvMailTemplate.model';
+import { NotificationModalComponent } from '../../notification-modal';
@Component({
selector: 'app-project-email-modal',
@@ -17,24 +15,19 @@ import { CsvMailTemplateModel } from '../../../classes/csvMailTemplate.model';
})
export class ProjectEmailModalComponent implements OnInit, OnDestroy {
@Input() selectedProjects: Application[];
- @Input() csvMailTemplate: CsvMailTemplateModel;
- @Input() csvFile: File;
emailAdminsOnly: boolean;
emailSubject: string;
emailReply: string;
emailText: string;
templates: string[];
- validCSVExample = `Project, Key1, Key2
-Proj1, ValK1, ValK2
-Proj2, ValK1, ValK2`;
public event: EventEmitter = new EventEmitter();
constructor(
public bsModalRef: BsModalRef,
- private voService: VoService,
private emailService: EmailService,
+ private notificationModal: NotificationModalComponent,
) {
// eslint-disable-next-line no-empty-function
}
@@ -49,39 +42,18 @@ Proj2, ValK1, ValK2`;
});
}
- sentProjectsTemplatedMail(): void {
- const project_ids = this.selectedProjects.map((pr: Application) => pr.project_application_perun_id);
-
- this.emailService
- .sendCsvTemplatedMail(
- this.csvFile,
- project_ids,
- this.emailSubject,
- this.emailText,
- this.emailAdminsOnly,
- this.emailReply,
- )
- .subscribe(
- (res: IResponseTemplate) => {
- this.event.emit(res.value as boolean);
- },
- () => {
- this.event.emit(false);
- },
- );
- }
-
sentProjectsMail(): void {
const project_ids = this.selectedProjects.map((pr: Application) => pr.project_application_perun_id);
+ this.notificationModal.showInfoNotificationModal('Info', 'Sending Mails...');
this.emailService
.sendMailToProjects(project_ids, this.emailSubject, this.emailText, this.emailAdminsOnly, this.emailReply)
.subscribe(
- (res: IResponseTemplate) => {
- this.event.emit(res.value as boolean);
+ () => {
+ this.notificationModal.showSuccessFullNotificationModal('Success', 'Mails were successfully sent');
},
() => {
- this.event.emit(false);
+ this.notificationModal.showSuccessFullNotificationModal('Failed', 'Failed to send mails!');
},
);
}
diff --git a/src/app/shared/modal/notification-modal.component.html b/src/app/shared/modal/notification-modal.component.html
index d1ed6e7157..bc5949a3dc 100644
--- a/src/app/shared/modal/notification-modal.component.html
+++ b/src/app/shared/modal/notification-modal.component.html
@@ -1,25 +1,29 @@
-
{{ notificationModalMessage }}
+
diff --git a/src/app/shared/modal/notification-modal.ts b/src/app/shared/modal/notification-modal.ts
index 36e1f79258..a9932f23fe 100644
--- a/src/app/shared/modal/notification-modal.ts
+++ b/src/app/shared/modal/notification-modal.ts
@@ -1,18 +1,82 @@
-import { Component } from '@angular/core';
-import { BsModalRef } from 'ngx-bootstrap/modal';
+import { Component, Injectable, OnDestroy } from '@angular/core';
+import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal';
+import { Router } from '@angular/router';
+@Injectable({ providedIn: 'root' })
@Component({
- selector: 'app-notification-modal',
- templateUrl: './notification-modal.component.html',
+ selector: 'app-notification-modal',
+ templateUrl: './notification-modal.component.html',
})
-export class NotificationModalComponent {
-
+export class NotificationModalComponent implements OnDestroy {
notificationModalTitle: string;
notificationModalType: string;
notificationModalMessage: string;
+ routerRedirectString: string;
+ modalId: number | string | undefined;
+
+ hide(): void {
+ this.modalService.hide(this.modalId);
+ }
- constructor(public bsModalRef: BsModalRef) {
+ constructor(
+ private router: Router,
+ private modalService: BsModalService,
+ ) {
// eslint-disable-next-line no-empty-function
}
+ showNotificationModal(
+ notificationModalTitle: string,
+ notificationModalMessage: string,
+ notificationModalType: string,
+ routerRedirectString?: string,
+ ): void {
+ const initialState = {
+ notificationModalTitle,
+ notificationModalType,
+ notificationModalMessage,
+ routerRedirectString,
+ };
+ const bsModalRef: BsModalRef = this.modalService.show(NotificationModalComponent, { initialState });
+ bsModalRef.setClass('modal-lg');
+ this.modalId = bsModalRef.id;
+ }
+
+ showSuccessFullNotificationModal(
+ notificationModalTitle: string,
+ notificationModalMessage: string,
+ routerRedirectString?: string,
+ ): void {
+ this.showNotificationModal(notificationModalTitle, notificationModalMessage, 'success', routerRedirectString);
+ }
+
+ showDangerNotificationModal(
+ notificationModalTitle: string,
+ notificationModalMessage: string,
+ routerRedirectString?: string,
+ ): void {
+ this.showNotificationModal(notificationModalTitle, notificationModalMessage, 'danger', routerRedirectString);
+ }
+
+ showWarningNotificationModal(
+ notificationModalTitle: string,
+ notificationModalMessage: string,
+ routerRedirectString?: string,
+ ): void {
+ this.showNotificationModal(notificationModalTitle, notificationModalMessage, 'warning', routerRedirectString);
+ }
+
+ showInfoNotificationModal(
+ notificationModalTitle: string,
+ notificationModalMessage: string,
+ routerRedirectString?: string,
+ ): void {
+ this.showNotificationModal(notificationModalTitle, notificationModalMessage, 'info', routerRedirectString);
+ }
+
+ ngOnDestroy(): void {
+ if (this.routerRedirectString) {
+ void this.router.navigateByUrl(this.routerRedirectString);
+ }
+ }
}
diff --git a/src/app/shared/shared_modules/shared-module.module.ts b/src/app/shared/shared_modules/shared-module.module.ts
index eab670b22f..f55f91f92f 100644
--- a/src/app/shared/shared_modules/shared-module.module.ts
+++ b/src/app/shared/shared_modules/shared-module.module.ts
@@ -4,6 +4,7 @@ import { ModalModule } from 'ngx-bootstrap/modal';
import { ProgressModule, ToastModule } from '@coreui/angular';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { NgSelectModule } from '@ng-select/ng-select';
+import { RouterLink } from '@angular/router';
import { ApplicationBaseClassComponent } from './baseClass/application-base-class.component';
import { NotificationModalComponent } from '../modal/notification-modal';
import { InformationToastComponent } from '../toaster/information-toast.component';
@@ -16,6 +17,7 @@ import { SharedDirectivesModule } from './shared_directives.module';
import { MaintenanceNotificationComponent } from './components/maintenance-notification/maintenance-notification.component';
import { PipeModuleModule } from '../../pipe-module/pipe-module.module';
import { MembersListModalComponent } from '../modal/members/members-list-modal.component';
+import { ProjectCsvTemplatedEmailModalComponent } from '../modal/email/project-csv-templated-email-modal/project-csv-templated-email-modal.component';
/**
* Shared module.
@@ -30,6 +32,7 @@ import { MembersListModalComponent } from '../modal/members/members-list-modal.c
MigrationInformationComponent,
ApplicationBadgesComponent,
ProjectEmailModalComponent,
+ ProjectCsvTemplatedEmailModalComponent,
TestimonialFormComponent,
MaintenanceNotificationComponent,
],
@@ -43,6 +46,7 @@ import { MembersListModalComponent } from '../modal/members/members-list-modal.c
NgSelectModule,
SharedDirectivesModule,
ReactiveFormsModule,
+ RouterLink,
],
declarations: [
ApplicationBaseClassComponent,
@@ -53,6 +57,7 @@ import { MembersListModalComponent } from '../modal/members/members-list-modal.c
MigrationInformationComponent,
ApplicationBadgesComponent,
ProjectEmailModalComponent,
+ ProjectCsvTemplatedEmailModalComponent,
TestimonialFormComponent,
MaintenanceNotificationComponent,
],
diff --git a/src/app/vo_manager/VoOverviewComponent.ts b/src/app/vo_manager/VoOverviewComponent.ts
index 9a207966b7..af1c573d5e 100644
--- a/src/app/vo_manager/VoOverviewComponent.ts
+++ b/src/app/vo_manager/VoOverviewComponent.ts
@@ -20,12 +20,11 @@ import {
SortEvent,
} from '../shared/shared_modules/directives/nbd-sortable-header.directive';
import { ProjectSortService } from '../shared/shared_modules/services/project-sort.service';
-import { ProjectEmailModalComponent } from '../shared/modal/email/project-email-modal/project-email-modal.component';
import { ConfirmationModalComponent } from '../shared/modal/confirmation-modal.component';
import { ConfirmationActions } from '../shared/modal/confirmation_actions';
import { MembersListModalComponent } from '../shared/modal/members/members-list-modal.component';
import { EmailService } from '../api-connector/email.service';
-import { CsvMailTemplateModel } from '../shared/classes/csvMailTemplate.model';
+import { ProjectCsvTemplatedEmailModalComponent } from '../shared/modal/email/project-csv-templated-email-modal/project-csv-templated-email-modal.component';
/**
* Vo Overview component.
@@ -122,21 +121,6 @@ export class VoOverviewComponent extends AbstractBaseClass implements OnInit, On
this.subscription.unsubscribe();
}
- onCsvFileSelected(event): void {
- const inputElement = event.target as HTMLInputElement;
- if (inputElement.files && inputElement.files.length > 0) {
- this.emailService.sendCsvTemplate(inputElement.files[0]).subscribe(
- (csvTemplate: CsvMailTemplateModel) => {
- this.openProjectMailsModal(inputElement.files[0], csvTemplate);
- },
- (error: CsvMailTemplateModel) => {
- console.log(error['error']);
- this.openProjectMailsModal(inputElement.files[0], error['error']);
- },
- );
- }
- }
-
getTSVInformation(timeout: number = this.checkTSVTimeout): void {
this.stopCheckTSVTimer();
this.subscription.add(
@@ -252,28 +236,8 @@ export class VoOverviewComponent extends AbstractBaseClass implements OnInit, On
}
}
- openProjectMailsModal(csvFile: File = null, csvTemplate: CsvMailTemplateModel = null): void {
- let initialState = {};
-
- if (csvFile) {
- initialState = {
- selectedProjects: csvTemplate.valid_projects,
- csvFile,
- csvMailTemplate: csvTemplate,
- };
- } else {
- initialState = { selectedProjects: this.selectedEmailProjects };
- }
-
- this.bsModalRef = this.modalService.show(ProjectEmailModalComponent, { initialState, class: 'modal-lg' });
- this.bsModalRef.content.event.subscribe((sent_successfully: boolean) => {
- if (sent_successfully) {
- this.updateNotificationModal('Success', 'Mails were successfully sent', true, 'success');
- } else {
- this.updateNotificationModal('Failed', 'Failed to send mails!', true, 'danger');
- }
- this.notificationModal.show();
- });
+ openProjectCSVMailModal(): void {
+ this.bsModalRef = this.modalService.show(ProjectCsvTemplatedEmailModalComponent, { class: 'modal-lg' });
}
disableProject(project: Application): void {
diff --git a/src/app/vo_manager/vo-guard.service.ts b/src/app/vo_manager/vo-guard.service.ts
index 51dac09551..cad5b4abc9 100644
--- a/src/app/vo_manager/vo-guard.service.ts
+++ b/src/app/vo_manager/vo-guard.service.ts
@@ -1,4 +1,6 @@
/* eslint-disable */
+/* tslint-disable */
+
import { Injectable } from '@angular/core'
import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router'
import { Observable } from 'rxjs'
@@ -9,9 +11,7 @@ import { is_vo } from '../shared/globalvar'
*/
@Injectable()
export class VoGuardService {
- constructor(private router: Router) {
- this.router = router
- }
+ constructor(private router: Router) {}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
canActivate(
diff --git a/src/app/vo_manager/voOverview.component.html b/src/app/vo_manager/voOverview.component.html
index 439ddfcc7d..d9c4f15667 100644
--- a/src/app/vo_manager/voOverview.component.html
+++ b/src/app/vo_manager/voOverview.component.html
@@ -22,7 +22,7 @@
-
-
+
Send CSV Templated Email