diff --git a/src/app/app.component.html b/src/app/app.component.html index b11d739..7fa9eac 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -2,7 +2,7 @@ - + - + diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 5e45992..fc03396 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -31,8 +31,7 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { ProjectDashboardComponent } from './project-dashboard/project-dashboard.component'; import { PageNotFoundComponent } from './page-not-found/page-not-found.component'; import { AppRoutingModule } from './app-routing.module'; -import { CreateProjectDialogComponent } from './project-dashboard/create-project-dialog/create-project-dialog.component'; -import { EditProjectDialogComponent } from './project-dashboard/edit-project-dialog/edit-project-dialog.component'; +import { ProjectMetaDataDialogComponent } from './project-dashboard/project-meta-data-dialog/project-meta-data-dialog.component'; import { ProjectBoardComponent } from './project-board/project-board.component'; import { DiagramComponent } from './project-board/diagram/diagram.component'; import { CatalogComponent } from './project-board/catalog/catalog.component'; @@ -65,8 +64,7 @@ import { InputDialogComponent } from './project-board/evaluation/input-dialog/in AppComponent, ProjectDashboardComponent, PageNotFoundComponent, - CreateProjectDialogComponent, - EditProjectDialogComponent, + ProjectMetaDataDialogComponent, ProjectBoardComponent, DiagramComponent, CatalogComponent, @@ -127,8 +125,7 @@ import { InputDialogComponent } from './project-board/evaluation/input-dialog/in providers: [], bootstrap: [AppComponent], entryComponents: [ - CreateProjectDialogComponent, - EditProjectDialogComponent, + ProjectMetaDataDialogComponent, CreateGraphDialogComponent, EditGraphDialogComponent, ReplacementDialogComponent, diff --git a/src/app/project-dashboard/create-project-dialog/create-project-dialog.component.html b/src/app/project-dashboard/create-project-dialog/create-project-dialog.component.html deleted file mode 100644 index 846bcaf..0000000 --- a/src/app/project-dashboard/create-project-dialog/create-project-dialog.component.html +++ /dev/null @@ -1,76 +0,0 @@ -

Create a new Project

-
-
- - - - Please enter a name - - - Name too long - - - - - - - Owners too long - - - - - - - Description too long - - -
-
- - Select all regions, which are neccessary for your cloud application. - -
- - - - - - {{item.provider.title}} - - - - - -
    -
  • - - {{option.region.name}} - -
  • -
-
-
-
-
- -
-
- - -
\ No newline at end of file diff --git a/src/app/project-dashboard/create-project-dialog/create-project-dialog.component.spec.ts b/src/app/project-dashboard/create-project-dialog/create-project-dialog.component.spec.ts deleted file mode 100644 index b3c4978..0000000 --- a/src/app/project-dashboard/create-project-dialog/create-project-dialog.component.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - -import { CreateProjectDialogComponent } from './create-project-dialog.component'; - -describe('CreateProjectDialogComponent', () => { - let component: CreateProjectDialogComponent; - let fixture: ComponentFixture; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ CreateProjectDialogComponent ] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(CreateProjectDialogComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/src/app/project-dashboard/create-project-dialog/create-project-dialog.component.ts b/src/app/project-dashboard/create-project-dialog/create-project-dialog.component.ts deleted file mode 100644 index 0a4cea2..0000000 --- a/src/app/project-dashboard/create-project-dialog/create-project-dialog.component.ts +++ /dev/null @@ -1,131 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { MatDialogRef } from '@angular/material'; -import { FormGroup, FormBuilder, Validators } from '@angular/forms'; -import CloudProviderItem from './cloud-provider-item'; -import { HttpClient } from '@angular/common/http'; -import {JsonCloudProvider} from '@openclams/clams-ml'; -import { environment } from '../../../environments/environment'; -import Project from '../../model/project'; -import {ClamsProject} from '@openclams/clams-ml'; -import {CloudProviderFactory} from '@openclams/clams-ml'; - -@Component({ - selector: 'app-create-project-dialog', - templateUrl: './create-project-dialog.component.html', - styleUrls: ['./create-project-dialog.component.css'] -}) -export class CreateProjectDialogComponent implements OnInit { - - public projectForm: FormGroup; - public providerList: CloudProviderItem[]; - - constructor(private http: HttpClient, - private formBuilder: FormBuilder, - public dialogRef: MatDialogRef) { } - - ngOnInit() { - this.providerList = []; - this.projectForm = this.formBuilder.group({ - name: ['', [ - Validators.required, - Validators.maxLength(256) - ]], - description: ['', [ - Validators.maxLength(2048) - ]], - owner: ['', [ - Validators.maxLength(128) - ]], - }); - - this.http.get(environment.serviceServer).subscribe( cloudProviders => - this.providerList = cloudProviders.map(provider => { - const item = new CloudProviderItem(); - item.provider = provider; - item.options = provider.regions.map(r => { - return { - completed: true, - region: r - }; - }); - return item; - }) - ); - } - - updateAllComplete(provider: CloudProviderItem) { - provider.allCompleted = provider.options != null - && provider.options.every(t => t.completed); - } - - someComplete(provider: CloudProviderItem): boolean { - if (provider.options == null || provider.options.length === 0) { - return false; - } - return provider.options.filter(t => t.completed).length > 0 - && !provider.allCompleted; - } - - setAll(provider: CloudProviderItem, completed: boolean) { - provider.allCompleted = completed; - if (provider.options == null || provider.options.length === 0) { - return; - } - provider.options.forEach(t => t.completed = completed); - } - - onCancelClick() { - this.dialogRef.close(null); - } - - onCreate() { - this.projectForm.markAllAsTouched(); - if (this.projectForm.invalid) { - return; - } - const rawData = this.projectForm.getRawValue(); - const id = rawData.name + '_' + Date.now().toString(16); - const project = new Project(); - project.metaData.id = id; - project.metaData.name = rawData.name; - project.metaData.description = rawData.description; - project.metaData.owner = rawData.owner; - - project.metaData.cloudProviders = this.getCloudProviders(); - // Create empty model - project.model = new ClamsProject(); - // Include Cloud Providers to model - project.model.cloudProviders = project.metaData.cloudProviders.map(jsonCloudProvider => - CloudProviderFactory.fromJSON(jsonCloudProvider)); - - this.dialogRef.close(project); - } - - public getCloudProviders(): JsonCloudProvider[] { - return this.providerList.map(item => { - if (this.someComplete(item) || item.allCompleted) { - const provider = item.provider; - provider.regions = item.options.map(option => { - if (option.completed) { - return option.region; - } - return null; - }).filter(p => p); - return provider; - } - return null; - }).filter(p => p); - } - - get name() { - return this.projectForm.get('name'); - } - - get description() { - return this.projectForm.get('description'); - } - - get owner() { - return this.projectForm.get('owner'); - } -} diff --git a/src/app/project-dashboard/edit-project-dialog/edit-project-dialog.component.css b/src/app/project-dashboard/edit-project-dialog/edit-project-dialog.component.css deleted file mode 100644 index 888c369..0000000 --- a/src/app/project-dashboard/edit-project-dialog/edit-project-dialog.component.css +++ /dev/null @@ -1,17 +0,0 @@ -.provider-section { - margin: 12px 0; - } - - .provider-margin { - margin: 0 12px; - } - - ul { - list-style-type: none; - margin-top: 4px; - } - - .provider-headers-align{ - margin-top: 20px; - margin-bottom: 20px; - } \ No newline at end of file diff --git a/src/app/project-dashboard/edit-project-dialog/edit-project-dialog.component.html b/src/app/project-dashboard/edit-project-dialog/edit-project-dialog.component.html deleted file mode 100644 index c036b0d..0000000 --- a/src/app/project-dashboard/edit-project-dialog/edit-project-dialog.component.html +++ /dev/null @@ -1,68 +0,0 @@ -

Edit Project

-
-
- - - - Please enter a name - - - Name too long - - - - - - - Owners too long - - - - - - - Description too long - - -
-
- - Your Project is using the following cloud providers. - -
- - - - - {{provider.title}} - - - - -
    -
  • - {{region.name}} -
  • -
-
-
-
-
- -
-
- - -
\ No newline at end of file diff --git a/src/app/project-dashboard/edit-project-dialog/edit-project-dialog.component.spec.ts b/src/app/project-dashboard/edit-project-dialog/edit-project-dialog.component.spec.ts deleted file mode 100644 index ecbb8f7..0000000 --- a/src/app/project-dashboard/edit-project-dialog/edit-project-dialog.component.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - -import { EditProjectDialogComponent } from './edit-project-dialog.component'; - -describe('EditProjectDialogComponent', () => { - let component: EditProjectDialogComponent; - let fixture: ComponentFixture; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ EditProjectDialogComponent ] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(EditProjectDialogComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/src/app/project-dashboard/edit-project-dialog/edit-project-dialog.component.ts b/src/app/project-dashboard/edit-project-dialog/edit-project-dialog.component.ts deleted file mode 100644 index 0241f54..0000000 --- a/src/app/project-dashboard/edit-project-dialog/edit-project-dialog.component.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { Component, OnInit, Inject } from '@angular/core'; -import { FormGroup, FormBuilder, Validators } from '@angular/forms'; -import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; -import { ProjectManager } from '../../data-management/project-manager'; -import JsonProjectMeta from '../../model/json-project-meta'; - -@Component({ - selector: 'app-edit-project-dialog', - templateUrl: './edit-project-dialog.component.html', - styleUrls: ['./edit-project-dialog.component.css'] -}) -export class EditProjectDialogComponent implements OnInit { - - public projectForm: FormGroup; - - constructor(private formBuilder: FormBuilder, - public dialogRef: MatDialogRef, - @Inject(MAT_DIALOG_DATA) public data: {projectMeta: JsonProjectMeta} ) { } - - ngOnInit() { - this.projectForm = this.formBuilder.group({ - name: [this.data.projectMeta.name, [ - Validators.required, - Validators.maxLength(32) - ]], - description: [this.data.projectMeta.description, [ - Validators.maxLength(2048) - ]], - owner: [this.data.projectMeta.owner, [ - Validators.maxLength(128) - ]], - }); - } - - onCancelClick() { - this.dialogRef.close(null); - } - - onCreate() { - this.projectForm.markAllAsTouched(); - if (this.projectForm.invalid) { - return; - } - const rawData = this.projectForm.getRawValue(); - const newMeta: JsonProjectMeta = { - id: this.data.projectMeta.id, - name: rawData.name, - description: rawData.description, - owner: rawData.owner, - cloudProviders: this.data.projectMeta.cloudProviders - }; - - this.dialogRef.close(newMeta); - } - - get name() { - return this.projectForm.get('name'); - } - - get description() { - return this.projectForm.get('description'); - } - - get owner() { - return this.projectForm.get('owner'); - } - -} diff --git a/src/app/project-dashboard/project-dashboard.component.html b/src/app/project-dashboard/project-dashboard.component.html index f307234..b7fc218 100644 --- a/src/app/project-dashboard/project-dashboard.component.html +++ b/src/app/project-dashboard/project-dashboard.component.html @@ -1,70 +1,70 @@ - - + + - - + + -
- -
+
+ +
-
- -

Projects

- -
+
- - -
cloud_queue
- - {{project.name | truncate : 40 : '...'}} - - {{project.owner}} -
- -

- {{project.description}} -

-
- - -
- - - -
-
-
+

Projects

+
+ + +
+ cloud_queue
+ + {{project.name | truncate : 40 : '...'}} + + {{project.owner}} +
+ +

+ {{project.description}} +

+
+ + +
+ + +
+
+
+
diff --git a/src/app/project-dashboard/project-dashboard.component.ts b/src/app/project-dashboard/project-dashboard.component.ts index f9763da..6b09967 100644 --- a/src/app/project-dashboard/project-dashboard.component.ts +++ b/src/app/project-dashboard/project-dashboard.component.ts @@ -2,9 +2,8 @@ import { Component, OnInit, HostListener } from '@angular/core'; import { ProjectManager } from '../data-management/project-manager'; import JsonProjectMeta from '../model/json-project-meta'; import { MatDialog } from '@angular/material/dialog'; -import { CreateProjectDialogComponent } from './create-project-dialog/create-project-dialog.component'; +import { ProjectMetaDataDialogComponent } from './project-meta-data-dialog/project-meta-data-dialog.component'; import Project from '../model/project'; -import { EditProjectDialogComponent } from './edit-project-dialog/edit-project-dialog.component'; import { Router } from '@angular/router'; @Component({ @@ -20,14 +19,6 @@ export class ProjectDashboardComponent implements OnInit { ngOnInit() { ProjectManager.refreshProjectMetas() - this.onResize(); - } - - @HostListener('window:resize', ['$event']) - onResize(event?) { - const screenHeight = window.innerHeight; - let box2h = 64//document.getElementById("command-bar").offsetHeight - document.getElementById("main-container").style.height = (screenHeight - box2h)+'px'; } onDelete(project: JsonProjectMeta, idx: number) { @@ -38,34 +29,25 @@ export class ProjectDashboardComponent implements OnInit { this.router.navigate(['project', project.id]); } - openCreateProjectDialog() { - console.log('Create Project Event'); - const dialogRef = this.dialog.open(CreateProjectDialogComponent); + openProjectDialog(metaData?: JsonProjectMeta) { + console.log(metaData); + const dialogRef = this.dialog.open(ProjectMetaDataDialogComponent, + {data: {projectMeta: metaData}}); - dialogRef.afterClosed().subscribe(result => { - if (!result) { + dialogRef.afterClosed().subscribe((result: { isUpdate: boolean, data: Project }) => { + if (!result || !result.data) { return; } - const project = result as Project; - console.log(`Dialog result:`, project); - // Save project - ProjectManager.save(project); - }); - } - - openEditDialog(metaData: JsonProjectMeta) { - console.log('Create Project Event'); - const dialogRef = this.dialog.open(EditProjectDialogComponent, - {data: {projectMeta: metaData}}); - - dialogRef.afterClosed().subscribe(result => { - if (!result) { - return; - } + console.log(`Dialog result:`, result); + // Update/Save project + if (result.isUpdate) { ProjectManager.load(metaData.id).then(project => { - project.metaData = result; + project.metaData = result.data.metaData; ProjectManager.save(project); }); + } else { + ProjectManager.save(result.data); + } }); } diff --git a/src/app/project-dashboard/create-project-dialog/cloud-provider-item.ts b/src/app/project-dashboard/project-meta-data-dialog/cloud-provider-item.ts similarity index 100% rename from src/app/project-dashboard/create-project-dialog/cloud-provider-item.ts rename to src/app/project-dashboard/project-meta-data-dialog/cloud-provider-item.ts diff --git a/src/app/project-dashboard/create-project-dialog/create-project-dialog.component.css b/src/app/project-dashboard/project-meta-data-dialog/project-meta-data-dialog.component.css similarity index 100% rename from src/app/project-dashboard/create-project-dialog/create-project-dialog.component.css rename to src/app/project-dashboard/project-meta-data-dialog/project-meta-data-dialog.component.css diff --git a/src/app/project-dashboard/project-meta-data-dialog/project-meta-data-dialog.component.html b/src/app/project-dashboard/project-meta-data-dialog/project-meta-data-dialog.component.html new file mode 100644 index 0000000..d2f98e4 --- /dev/null +++ b/src/app/project-dashboard/project-meta-data-dialog/project-meta-data-dialog.component.html @@ -0,0 +1,81 @@ +

Create a new Project

+
+
+ + Name + + + {{ getNameErrorMsg() }} + + + + + Owner + + + Owners too long + + + + + Description + + + Description too long + + +
+
+ + Select all regions, which are neccessary for your cloud application. + +
+ + + + + + {{item.provider.title}} + + + + + +
    +
  • + + {{option.region.name}} + +
  • +
+
+
+
+
+ +
+
+ + +
diff --git a/src/app/project-dashboard/project-meta-data-dialog/project-meta-data-dialog.component.spec.ts b/src/app/project-dashboard/project-meta-data-dialog/project-meta-data-dialog.component.spec.ts new file mode 100644 index 0000000..9e59be6 --- /dev/null +++ b/src/app/project-dashboard/project-meta-data-dialog/project-meta-data-dialog.component.spec.ts @@ -0,0 +1,110 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ProjectMetaDataDialogComponent } from './project-meta-data-dialog.component'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { InjectionToken } from '@angular/core'; +import JsonProjectMeta from '../../model/json-project-meta'; +import { ReactiveFormsModule } from '@angular/forms'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatExpansionModule } from '@angular/material/expansion'; +import { MatInputModule } from '@angular/material/input'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { ProjectManager } from '../../data-management/project-manager'; +import { By } from '@angular/platform-browser'; + +const testStrings: string[] = [ + 'string1', + 'string1 ', + ' string1', + 'string2', + 'string3' +]; + +describe('ProjectMetaDataDialogComponent', () => { + let component: ProjectMetaDataDialogComponent; + let fixture: ComponentFixture; + const noData = new InjectionToken<{ projectMeta: JsonProjectMeta }>(undefined); + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ProjectMetaDataDialogComponent], + imports: [ + HttpClientTestingModule, + BrowserAnimationsModule, + ReactiveFormsModule, + MatFormFieldModule, + MatExpansionModule, + MatInputModule + ], + providers: [ + {provide: MatDialogRef, useValue: {}}, + {provide: MAT_DIALOG_DATA, useValue: noData} + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ProjectMetaDataDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + + ProjectManager.projectMetas = [ + { + id: undefined, + name: testStrings[0], + description: undefined, + owner: undefined, + cloudProviders: undefined + }, + { + id: undefined, + name: testStrings[3], + description: undefined, + owner: undefined, + cloudProviders: undefined + } + ]; + }); + + afterEach(() => { + ProjectManager.projectMetas = []; + component.projectForm.reset() + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('#duplicateNameValidator() should recognize duplicate', () => { + expect(component.duplicateNameValidator()({value: testStrings[0]}).duplicate).toBeTrue(); + expect(component.duplicateNameValidator()({value: testStrings[1]}).duplicate).toBeTrue(); + expect(component.duplicateNameValidator()({value: testStrings[2]}).duplicate).toBeTrue(); + expect(component.duplicateNameValidator()({value: testStrings[3]}).duplicate).toBeTrue(); + }); + + it('#duplicateNameValidator() should not recognize duplicate', () => { + expect(component.duplicateNameValidator()({value: testStrings[4]})).toBeNull(); + }); + + function addLetters(letters: string) { + component.projectForm.controls.name.setValue(component.name.value + letters); + } + + it('#getNameErrorMsg() should return correct error message', () => { + component.projectForm.controls.name.markAsTouched() + expect(component.getNameErrorMsg()).toMatch('Please enter a name'); + addLetters('s'); + expect(component.getNameErrorMsg()).toMatch(''); + addLetters('tring1'); + expect(component.getNameErrorMsg()).toMatch('Name already exists'); + addLetters('0'); + expect(component.getNameErrorMsg()).toMatch(''); + // Results in 257 letters + addLetters('x'.repeat(249)); + expect(component.getNameErrorMsg()).toMatch('Name too long'); + addLetters('x'); + expect(component.getNameErrorMsg()).toMatch('Name too long'); + }); +}); diff --git a/src/app/project-dashboard/project-meta-data-dialog/project-meta-data-dialog.component.ts b/src/app/project-dashboard/project-meta-data-dialog/project-meta-data-dialog.component.ts new file mode 100644 index 0000000..8a74a80 --- /dev/null +++ b/src/app/project-dashboard/project-meta-data-dialog/project-meta-data-dialog.component.ts @@ -0,0 +1,183 @@ +import { Component, Inject, OnInit } from '@angular/core'; +import { MatDialogRef } from '@angular/material'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import CloudProviderItem from './cloud-provider-item'; +import { HttpClient } from '@angular/common/http'; +import { ClamsProject, CloudProviderFactory, JsonCloudProvider } from '@openclams/clams-ml'; +import { environment } from '../../../environments/environment'; +import Project from '../../model/project'; +import { ProjectManager } from '../../data-management/project-manager'; +import JsonProjectMeta from '../../model/json-project-meta'; +import { MAT_DIALOG_DATA } from '@angular/material/dialog'; + +@Component({ + selector: 'app-project-meta-data-dialog', + templateUrl: './project-meta-data-dialog.component.html', + styleUrls: ['./project-meta-data-dialog.component.css'] +}) +export class ProjectMetaDataDialogComponent implements OnInit { + + public projectForm: FormGroup; + public providerList: CloudProviderItem[]; + readonly isUpdate; + + constructor(private http: HttpClient, + private formBuilder: FormBuilder, + public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: { projectMeta: JsonProjectMeta }) { + this.isUpdate = !!data.projectMeta; + } + + ngOnInit() { + this.providerList = []; + this.projectForm = this.formBuilder.group({ + name: [this.isUpdate ? this.data.projectMeta.name : '', [ + Validators.required, + Validators.maxLength(256), + this.duplicateNameValidator(this.isUpdate ? this.data.projectMeta.name : undefined) + ]], + description: [this.isUpdate ? this.data.projectMeta.description : '', [ + Validators.maxLength(2048) + ]], + owner: [this.isUpdate ? this.data.projectMeta.owner : '', [ + Validators.maxLength(128) + ]], + }); + + if (!this.isUpdate) { + this.http.get(environment.serviceServer).subscribe(cloudProviders => + this.providerList = cloudProviders.map(provider => { + const item = new CloudProviderItem(); + item.provider = provider; + item.options = provider.regions.map(r => { + return { + completed: true, + region: r + }; + }); + return item; + }) + ); + } + } + + updateAllComplete(provider: CloudProviderItem) { + provider.allCompleted = provider.options != null + && provider.options.every(t => t.completed); + } + + someComplete(provider: CloudProviderItem): boolean { + if (provider.options == null || provider.options.length === 0) { + return false; + } + return provider.options.filter(t => t.completed).length > 0 + && !provider.allCompleted; + } + + setAll(provider: CloudProviderItem, completed: boolean) { + provider.allCompleted = completed; + if (provider.options == null || provider.options.length === 0) { + return; + } + provider.options.forEach(t => t.completed = completed); + } + + onCancelClick() { + this.dialogRef.close(undefined); + } + + onSubmit() { + this.projectForm.markAllAsTouched(); + if (this.projectForm.invalid) { + return; + } + const project = new Project(); + + project.metaData.name = this.name.value; + project.metaData.description = this.description.value; + project.metaData.owner = this.owner.value; + + if (this.isUpdate) { + project.metaData.id = this.data.projectMeta.id; + project.metaData.cloudProviders = this.data.projectMeta.cloudProviders; + } else { + project.metaData.id = this.name.value + '_' + Date.now().toString(16); + project.metaData.cloudProviders = this.getCloudProviders(); + // Create empty project model + project.model = new ClamsProject(); + // Include Cloud Providers to model + project.model.cloudProviders = project.metaData.cloudProviders.map(jsonCloudProvider => + CloudProviderFactory.fromJSON(jsonCloudProvider)); + } + this.dialogRef.close({ + isUpdate: this.isUpdate, + data: project + }); + } + + /** + * Check if the current input name already exists in the project manager and return error if it is the case. + * In case of update, ignore the name of this project. + * + * @param oldName: Name that is excluded from the duplicate check. + * + * @return Null if the strings do not match + * An object { duplicate: true } if the strings do match + */ + duplicateNameValidator(oldName?: string) { + return (control) => { + for (const project of ProjectManager.projectMetas) { + if (oldName && !oldName.localeCompare(control.value.trim())) { + continue; + } + const name = project.name.toLowerCase(); + if (!name.localeCompare(control.value.trim())) { + return {duplicate: true}; + } + } + return null; + }; + } + + /** + * Return the appropriate error message for the name input form field. + * + * @return One of three strings: + * If no input is given: 'Please enter a name' + * If input is too long: 'Name too long' + * If name exists in metas: 'Name already exists' + */ + getNameErrorMsg() { + return this.name.hasError('required') ? 'Please enter a name' : + this.name.hasError('maxlength') ? 'Name too long' : + this.name.invalid ? 'Name already exists' : ''; + } + + public getCloudProviders(): JsonCloudProvider[] { + return this.providerList.map(item => { + if (this.someComplete(item) || item.allCompleted) { + const provider = item.provider; + provider.regions = item.options.map(option => { + if (option.completed) { + return option.region; + } + return null; + }).filter(p => p); + return provider; + } + return null; + }).filter(p => p); + } + + get description() { + return this.projectForm.get('description'); + } + + get name() { + return this.projectForm.get('name'); + } + + get owner() { + return this.projectForm.get('owner'); + } +} diff --git a/src/styles.css b/src/styles.css index a0a07e3..9c5939d 100644 --- a/src/styles.css +++ b/src/styles.css @@ -1,4 +1,4 @@ /* You can add global styles to this file, and also import other style files */ @import '@angular/material/prebuilt-themes/indigo-pink.css'; -html, body { height: 100%; } +html, body { flex: 1; display: flex; flex-direction: column; } body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }