From 46ef26931d207155e17de4da7d8782818c439c88 Mon Sep 17 00:00:00 2001 From: Piotr Pekala Date: Fri, 9 Aug 2019 04:14:55 -0700 Subject: [PATCH 1/7] Import/export buttons added --- .../project-map/project-map.component.html | 12 +++++-- .../project-map/project-map.component.ts | 32 +++++++++++++++++-- .../import-project-dialog.component.ts | 8 +++-- src/app/services/project.service.ts | 4 +++ 4 files changed, 50 insertions(+), 6 deletions(-) diff --git a/src/app/components/project-map/project-map.component.html b/src/app/components/project-map/project-map.component.html index 42cbcd30..5188ac6f 100644 --- a/src/app/components/project-map/project-map.component.html +++ b/src/app/components/project-map/project-map.component.html @@ -40,11 +40,19 @@ + + diff --git a/src/app/components/project-map/project-map.component.ts b/src/app/components/project-map/project-map.component.ts index 002d167b..d723fd47 100644 --- a/src/app/components/project-map/project-map.component.ts +++ b/src/app/components/project-map/project-map.component.ts @@ -1,5 +1,5 @@ import { Component, OnDestroy, OnInit, ViewChild, ViewEncapsulation } from '@angular/core'; -import { ActivatedRoute, ParamMap } from '@angular/router'; +import { ActivatedRoute, ParamMap, Router } from '@angular/router'; import { Observable, Subject, Subscription, from } from 'rxjs'; import { webSocket } from 'rxjs/webSocket'; @@ -51,6 +51,8 @@ import { LabelWidget } from '../../cartography/widgets/label'; import { MapLinkNodeToLinkNodeConverter } from '../../cartography/converters/map/map-link-node-to-link-node-converter'; import { ProjectMapMenuComponent } from './project-map-menu/project-map-menu.component'; import { ToasterService } from '../../services/toaster.service'; +import { ImportProjectDialogComponent } from '../projects/import-project-dialog/import-project-dialog.component'; +import { MatDialog } from '@angular/material'; @Component({ @@ -115,7 +117,9 @@ export class ProjectMapComponent implements OnInit, OnDestroy { private movingEventSource: MovingEventSource, private mapScaleService: MapScaleService, private nodeCreatedLabelStylesFixer: NodeCreatedLabelStylesFixer, - private toasterService: ToasterService + private toasterService: ToasterService, + private dialog: MatDialog, + private router: Router ) {} ngOnInit() { @@ -386,6 +390,30 @@ export class ProjectMapComponent implements OnInit, OnDestroy { resetZoom() { this.mapScaleService.resetToDefault(); } + + importProject() { + let uuid: string = ''; + const dialogRef = this.dialog.open(ImportProjectDialogComponent, { + width: '400px', + autoFocus: false + }); + let instance = dialogRef.componentInstance; + instance.server = this.server; + const subscription = dialogRef.componentInstance.onImportProject.subscribe((projectId: string) => { + uuid = projectId; + }); + + dialogRef.afterClosed().subscribe(() => { + subscription.unsubscribe(); + this.projectService.open(this.server, uuid).subscribe(() => { + this.router.navigate(['/server', this.server.id, 'project', uuid]); + }); + }); + } + + exportProject() { + window.location.href = `http://${this.server.host}:${this.server.port}/v2/projects/${this.project.project_id}/export`; + } public uploadImageFile(event) { this.readImageFile(event.target); diff --git a/src/app/components/projects/import-project-dialog/import-project-dialog.component.ts b/src/app/components/projects/import-project-dialog/import-project-dialog.component.ts index 07a76027..bcb54385 100644 --- a/src/app/components/projects/import-project-dialog/import-project-dialog.component.ts +++ b/src/app/components/projects/import-project-dialog/import-project-dialog.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit, Inject } from '@angular/core'; +import { Component, OnInit, Inject, EventEmitter } from '@angular/core'; import { MatDialogRef, MAT_DIALOG_DATA, MatDialog } from '@angular/material'; import { FileUploader, ParsedResponseHeaders, FileItem } from 'ng2-file-upload'; import { Server } from '../../../models/server'; @@ -26,6 +26,8 @@ export class ImportProjectDialogComponent implements OnInit { projectNameForm: FormGroup; submitted: boolean = false; isFirstStepCompleted: boolean = false; + uuid: string; + onImportProject = new EventEmitter(); constructor( private dialog: MatDialog, @@ -58,6 +60,7 @@ export class ImportProjectDialogComponent implements OnInit { status: number, headers: ParsedResponseHeaders ) => { + this.onImportProject.emit(this.uuid); this.resultMessage = 'Project was imported succesfully!'; this.isFinishEnabled = true; }; @@ -138,7 +141,8 @@ export class ImportProjectDialogComponent implements OnInit { } prepareUploadPath(): string { + this.uuid = uuid(); const projectName = this.projectNameForm.controls['projectName'].value; - return `http://${this.server.host}:${this.server.port}/v2/projects/${uuid()}/import?name=${projectName}`; + return `http://${this.server.host}:${this.server.port}/v2/projects/${this.uuid}/import?name=${projectName}`; } } diff --git a/src/app/services/project.service.ts b/src/app/services/project.service.ts index 4ca87f98..d5a7af51 100644 --- a/src/app/services/project.service.ts +++ b/src/app/services/project.service.ts @@ -49,6 +49,10 @@ export class ProjectService { return this.httpServer.delete(server, `/projects/${project_id}`); } + export(server: Server, project_id: string): Observable { + return this.httpServer.get(server, `/projects/${project_id}/export`) + } + notificationsPath(server: Server, project_id: string): string { return `ws://${server.host}:${server.port}/v2/projects/${project_id}/notifications/ws`; } From 2f1f6b5a35361d284d80b4b4085578ce19dd66ae Mon Sep 17 00:00:00 2001 From: Piotr Pekala Date: Fri, 9 Aug 2019 04:33:54 -0700 Subject: [PATCH 2/7] Update project-map.component.spec.ts --- .../project-map/project-map.component.spec.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/app/components/project-map/project-map.component.spec.ts b/src/app/components/project-map/project-map.component.spec.ts index 8daeda92..a38c4d2d 100644 --- a/src/app/components/project-map/project-map.component.spec.ts +++ b/src/app/components/project-map/project-map.component.spec.ts @@ -1,7 +1,7 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { ProjectMapComponent } from './project-map.component'; -import { MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule } from '@angular/material'; +import { MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, MatDialogModule } from '@angular/material'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { ServerService } from '../../services/server.service'; import { ProjectService } from '../../services/project.service'; @@ -20,7 +20,7 @@ import { DrawingsDataSource } from '../../cartography/datasources/drawings-datas import { CommonModule } from '@angular/common'; import { ANGULAR_MAP_DECLARATIONS } from '../../cartography/angular-map.imports'; import { NO_ERRORS_SCHEMA } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; +import { ActivatedRoute, Router } from '@angular/router'; import { MockedSettingsService } from '../../services/settings.service.spec'; import { MockedServerService } from '../../services/server.service.spec'; import { MockedProjectService } from '../../services/project.service.spec'; @@ -52,6 +52,7 @@ import { MapSettingService } from '../../services/mapsettings.service'; import { ProjectMapMenuComponent } from './project-map-menu/project-map-menu.component'; import { MockedToasterService } from '../../services/toaster.service.spec'; import { ToasterService } from '../../services/toaster.service'; +import { MockedActivatedRoute } from '../snapshots/list-of-snapshots/list-of-snaphshots.component.spec'; export class MockedProgressService { public activate() {} @@ -202,6 +203,7 @@ describe('ProjectMapComponent', () => { let linksDataSource = new MockedLinksDataSource(); let mockedToasterService = new MockedToasterService(); let nodeCreatedLabelStylesFixer; + let mockedRouter = new MockedActivatedRoute; beforeEach(async(() => { nodeCreatedLabelStylesFixer = { @@ -209,7 +211,7 @@ describe('ProjectMapComponent', () => { }; TestBed.configureTestingModule({ - imports: [MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, CommonModule, NoopAnimationsModule], + imports: [MatIconModule, MatDialogModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, CommonModule, NoopAnimationsModule], providers: [ { provide: ActivatedRoute }, { provide: ServerService, useClass: MockedServerService }, @@ -245,7 +247,8 @@ describe('ProjectMapComponent', () => { { provide: NodeCreatedLabelStylesFixer, useValue: nodeCreatedLabelStylesFixer}, { provide: MapScaleService }, { provide: NodeCreatedLabelStylesFixer, useValue: nodeCreatedLabelStylesFixer}, - { provide: ToasterService, useValue: mockedToasterService } + { provide: ToasterService, useValue: mockedToasterService }, + { provide: Router, useValue: mockedRouter } ], declarations: [ProjectMapComponent, ProjectMapMenuComponent, D3MapComponent, ...ANGULAR_MAP_DECLARATIONS], schemas: [NO_ERRORS_SCHEMA] From cd5890ca2bf9954cfcbafd86ff761dbf876bb3f7 Mon Sep 17 00:00:00 2001 From: Piotr Pekala Date: Fri, 9 Aug 2019 07:43:22 -0700 Subject: [PATCH 3/7] Save as button added --- src/app/app.module.ts | 7 +- .../project-map/project-map.component.ts | 9 ++- .../save-project-dialog.component.css | 7 ++ .../save-project-dialog.component.html | 23 ++++++ .../save-project-dialog.component.ts | 77 +++++++++++++++++++ 5 files changed, 120 insertions(+), 3 deletions(-) create mode 100644 src/app/components/projects/save-project-dialog/save-project-dialog.component.css diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 4242bce2..c2ea1386 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -191,6 +191,7 @@ import { DuplicateActionComponent } from './components/project-map/context-menu/ import { MapSettingService } from './services/mapsettings.service'; import { ProjectMapMenuComponent } from './components/project-map/project-map-menu/project-map-menu.component'; import { HelpComponent } from './components/help/help.component'; +import { SaveProjectDialogComponent } from './components/projects/save-project-dialog/save-project-dialog.component'; if (environment.production) { Raven.config('https://b2b1cfd9b043491eb6b566fd8acee358@sentry.io/842726', { @@ -312,7 +313,8 @@ if (environment.production) { ConsoleComponent, NodesMenuComponent, ProjectMapMenuComponent, - HelpComponent + HelpComponent, + SaveProjectDialogComponent ], imports: [ BrowserModule, @@ -404,7 +406,8 @@ if (environment.production) { SymbolsComponent, DeleteConfirmationDialogComponent, HelpDialogComponent, - StartCaptureDialogComponent + StartCaptureDialogComponent, + SaveProjectDialogComponent ], bootstrap: [AppComponent] }) diff --git a/src/app/components/project-map/project-map.component.ts b/src/app/components/project-map/project-map.component.ts index 424bb105..b4bc7371 100644 --- a/src/app/components/project-map/project-map.component.ts +++ b/src/app/components/project-map/project-map.component.ts @@ -54,6 +54,7 @@ import { ToasterService } from '../../services/toaster.service'; import { ImportProjectDialogComponent } from '../projects/import-project-dialog/import-project-dialog.component'; import { MatDialog } from '@angular/material'; import { AddBlankProjectDialogComponent } from '../projects/add-blank-project-dialog/add-blank-project-dialog.component'; +import { SaveProjectDialogComponent } from '../projects/save-project-dialog/save-project-dialog.component'; @Component({ @@ -402,7 +403,13 @@ export class ProjectMapComponent implements OnInit, OnDestroy { } saveProject() { - + const dialogRef = this.dialog.open(SaveProjectDialogComponent, { + width: '400px', + autoFocus: false + }); + let instance = dialogRef.componentInstance; + instance.server = this.server; + instance.project = this.project; } importProject() { diff --git a/src/app/components/projects/save-project-dialog/save-project-dialog.component.css b/src/app/components/projects/save-project-dialog/save-project-dialog.component.css new file mode 100644 index 00000000..acf081db --- /dev/null +++ b/src/app/components/projects/save-project-dialog/save-project-dialog.component.css @@ -0,0 +1,7 @@ +.file-name-form-field { + width: 100%; +} + +.project-snackbar { + background: #2196F3; +} diff --git a/src/app/components/projects/save-project-dialog/save-project-dialog.component.html b/src/app/components/projects/save-project-dialog/save-project-dialog.component.html index e69de29b..c772cb96 100644 --- a/src/app/components/projects/save-project-dialog/save-project-dialog.component.html +++ b/src/app/components/projects/save-project-dialog/save-project-dialog.component.html @@ -0,0 +1,23 @@ +

Save project as

+
+ + + Project name is required + Project name is incorrect + +
+ + +
+
diff --git a/src/app/components/projects/save-project-dialog/save-project-dialog.component.ts b/src/app/components/projects/save-project-dialog/save-project-dialog.component.ts index e69de29b..afb76ec0 100644 --- a/src/app/components/projects/save-project-dialog/save-project-dialog.component.ts +++ b/src/app/components/projects/save-project-dialog/save-project-dialog.component.ts @@ -0,0 +1,77 @@ +import { Component, OnInit, EventEmitter } from '@angular/core'; +import { Router } from '@angular/router'; +import { MatDialog, MatDialogRef } from '@angular/material'; +import { FormBuilder, FormGroup, Validators, FormControl } from '@angular/forms'; +import { Project } from '../../../models/project'; +import { Server } from '../../../models/server'; +import { ProjectService } from '../../../services/project.service'; +import { v4 as uuid } from 'uuid'; +import { ProjectNameValidator } from '../models/projectNameValidator'; +import { ToasterService } from '../../../services/toaster.service'; + + +@Component({ + selector: 'app-save-project-dialog', + templateUrl: './save-project-dialog.component.html', + styleUrls: ['./save-project-dialog.component.css'], + providers: [ProjectNameValidator] +}) +export class SaveProjectDialogComponent implements OnInit { + server: Server; + project: Project; + projectNameForm: FormGroup; + onAddProject = new EventEmitter(); + + constructor( + public dialogRef: MatDialogRef, + private router: Router, + private dialog: MatDialog, + private projectService: ProjectService, + private toasterService: ToasterService, + private formBuilder: FormBuilder, + private projectNameValidator: ProjectNameValidator + ) { + this.projectNameForm = this.formBuilder.group({ + projectName: new FormControl(null, [Validators.required, projectNameValidator.get]) + }); + } + + ngOnInit() {} + + get form() { + return this.projectNameForm.controls; + } + + onAddClick(): void { + if (this.projectNameForm.invalid) { + return; + } + this.projectService.list(this.server).subscribe((projects: Project[]) => { + const projectName = this.projectNameForm.controls['projectName'].value; + let existingProject = projects.find(project => project.name === projectName); + + if (existingProject) { + this.toasterService.success(`Project with this name already exists.`); + } else { + this.addProject(); + } + }); + } + + onNoClick(): void { + this.dialogRef.close(); + } + + addProject(): void { + this.projectService.duplicate(this.server, this.project.project_id, this.projectNameForm.controls['projectName'].value).subscribe((project: Project) => { + this.dialogRef.close(); + this.toasterService.success(`Project ${project.name} added`); + }); + } + + onKeyDown(event) { + if (event.key === "Enter") { + this.onAddClick(); + } + } +} From 1abcad72d2cce8ff4c919aab471e69fa2d757a69 Mon Sep 17 00:00:00 2001 From: Piotr Pekala Date: Mon, 12 Aug 2019 01:58:10 -0700 Subject: [PATCH 4/7] Update project-map.component.ts --- src/app/components/project-map/project-map.component.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/app/components/project-map/project-map.component.ts b/src/app/components/project-map/project-map.component.ts index b4bc7371..bedbfde5 100644 --- a/src/app/components/project-map/project-map.component.ts +++ b/src/app/components/project-map/project-map.component.ts @@ -433,7 +433,11 @@ export class ProjectMapComponent implements OnInit, OnDestroy { } exportProject() { - window.location.href = `http://${this.server.host}:${this.server.port}/v2/projects/${this.project.project_id}/export`; + if (this.nodes.filter(node => node.node_type = 'virtualbox').length > 0) { + this.toasterService.error('Map with VirtualBox machines cannot be exported.') + } else { + window.location.href = `http://${this.server.host}:${this.server.port}/v2/projects/${this.project.project_id}/export`; + } } public uploadImageFile(event) { From 6f62f4171d3c653ae447a40aaba966077affe9c5 Mon Sep 17 00:00:00 2001 From: Piotr Pekala Date: Tue, 20 Aug 2019 06:20:03 -0700 Subject: [PATCH 5/7] Update project-map.component.ts --- src/app/components/project-map/project-map.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/components/project-map/project-map.component.ts b/src/app/components/project-map/project-map.component.ts index 44128aaf..ca1b28f5 100644 --- a/src/app/components/project-map/project-map.component.ts +++ b/src/app/components/project-map/project-map.component.ts @@ -464,7 +464,7 @@ export class ProjectMapComponent implements OnInit, OnDestroy { } exportProject() { - if (this.nodes.filter(node => node.node_type = 'virtualbox').length > 0) { + if (this.nodes.filter(node => node.node_type === 'virtualbox').length > 0) { this.toasterService.error('Map with VirtualBox machines cannot be exported.') } else { window.location.href = `http://${this.server.host}:${this.server.port}/v2/projects/${this.project.project_id}/export`; From 43e87307ba74e7e728c7376b3c01a314b3c5da48 Mon Sep 17 00:00:00 2001 From: Piotr Pekala Date: Tue, 20 Aug 2019 06:52:13 -0700 Subject: [PATCH 6/7] Update project-map-component --- src/app/components/project-map/project-map.component.html | 2 +- src/app/components/project-map/project-map.component.ts | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/app/components/project-map/project-map.component.html b/src/app/components/project-map/project-map.component.html index 06c2ef3d..c537ac97 100644 --- a/src/app/components/project-map/project-map.component.html +++ b/src/app/components/project-map/project-map.component.html @@ -48,7 +48,7 @@