diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts
index dd9e3e44..c5ebac59 100644
--- a/src/app/app-routing.module.ts
+++ b/src/app/app-routing.module.ts
@@ -50,6 +50,7 @@ import { CopyQemuVmTemplateComponent } from './components/preferences/qemu/copy-
import { CopyIosTemplateComponent } from './components/preferences/dynamips/copy-ios-template/copy-ios-template.component';
import { CopyDockerTemplateComponent } from './components/preferences/docker/copy-docker-template/copy-docker-template.component';
import { CopyIouTemplateComponent } from './components/preferences/ios-on-unix/copy-iou-template/copy-iou-template.component';
+import { ListOfSnapshotsComponent } from './components/snapshots/list-of-snapshots/list-of-snapshots.component';
const routes: Routes = [
{
@@ -62,6 +63,7 @@ const routes: Routes = [
{ path: 'server/:server_id/projects', component: ProjectsComponent },
{ path: 'settings', component: SettingsComponent },
{ path: 'installed-software', component: InstalledSoftwareComponent },
+ { path: 'server/:server_id/project/:project_id/snapshots', component: ListOfSnapshotsComponent },
{ path: 'server/:server_id/preferences', component: PreferencesComponent },
// { path: 'server/:server_id/preferences/general', component: GeneralPreferencesComponent },
{ path: 'server/:server_id/preferences/builtin', component: BuiltInPreferencesComponent},
@@ -116,7 +118,9 @@ const routes: Routes = [
{ path: 'server/:server_id/preferences/iou/addtemplate', component: AddIouTemplateComponent }
]
},
- { path: 'server/:server_id/project/:project_id', component: ProjectMapComponent }
+ {
+ path: 'server/:server_id/project/:project_id', component: ProjectMapComponent,
+ }
];
@NgModule({
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 7cecaf5f..50690cbe 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -68,7 +68,6 @@ import { RavenState } from './common/error-handlers/raven-state-communicator';
import { ServerDiscoveryComponent } from './components/servers/server-discovery/server-discovery.component';
import { ServerDatabase } from './services/server.database';
import { CreateSnapshotDialogComponent } from './components/snapshots/create-snapshot-dialog/create-snapshot-dialog.component';
-import { SnapshotsComponent } from './components/snapshots/snapshots.component';
import { SnapshotMenuItemComponent } from './components/snapshots/snapshot-menu-item/snapshot-menu-item.component';
import { MATERIAL_IMPORTS } from './material.imports';
import { DrawingService } from './services/drawing.service';
@@ -165,6 +164,8 @@ import { SearchFilter } from './filters/searchFilter.pipe';
import { RecentlyOpenedProjectService } from './services/recentlyOpenedProject.service';
import { ServerManagementService } from './services/server-management.service';
import { DeleteActionComponent } from './components/project-map/context-menu/actions/delete-action/delete-action.component';
+import { ListOfSnapshotsComponent } from './components/snapshots/list-of-snapshots/list-of-snapshots.component';
+import { DateFilter } from './filters/dateFilter.pipe';
if (environment.production) {
Raven.config('https://b2b1cfd9b043491eb6b566fd8acee358@sentry.io/842726', {
@@ -183,7 +184,6 @@ if (environment.production) {
AddServerDialogComponent,
CreateSnapshotDialogComponent,
SnapshotMenuItemComponent,
- SnapshotsComponent,
ProjectsComponent,
AddBlankProjectDialogComponent,
ImportProjectDialogComponent,
@@ -267,7 +267,9 @@ if (environment.production) {
CopyDockerTemplateComponent,
EmptyTemplatesListComponent,
SymbolsMenuComponent,
- SearchFilter
+ SearchFilter,
+ DateFilter,
+ ListOfSnapshotsComponent
],
imports: [
BrowserModule,
diff --git a/src/app/components/preferences/vpcs/vpcs-templates/vpcs-templates.component.ts b/src/app/components/preferences/vpcs/vpcs-templates/vpcs-templates.component.ts
index ad88af64..a2e570d8 100644
--- a/src/app/components/preferences/vpcs/vpcs-templates/vpcs-templates.component.ts
+++ b/src/app/components/preferences/vpcs/vpcs-templates/vpcs-templates.component.ts
@@ -6,7 +6,6 @@ import { VpcsService } from '../../../../services/vpcs.service';
import { VpcsTemplate } from '../../../../models/templates/vpcs-template';
import { DeleteTemplateComponent } from '../../common/delete-template-component/delete-template.component';
-
@Component({
selector: 'app-vpcs-templates',
templateUrl: './vpcs-templates.component.html',
diff --git a/src/app/components/snapshots/create-snapshot-dialog/create-snapshot-dialog.component.html b/src/app/components/snapshots/create-snapshot-dialog/create-snapshot-dialog.component.html
index f81fdfac..ca13ce5e 100644
--- a/src/app/components/snapshots/create-snapshot-dialog/create-snapshot-dialog.component.html
+++ b/src/app/components/snapshots/create-snapshot-dialog/create-snapshot-dialog.component.html
@@ -1,6 +1,11 @@
-
Create snapshot
+
+
Create snapshot
+
+
-
+
+
+
diff --git a/src/app/components/snapshots/create-snapshot-dialog/create-snapshot-dialog.component.scss b/src/app/components/snapshots/create-snapshot-dialog/create-snapshot-dialog.component.scss
index e69de29b..7977448c 100644
--- a/src/app/components/snapshots/create-snapshot-dialog/create-snapshot-dialog.component.scss
+++ b/src/app/components/snapshots/create-snapshot-dialog/create-snapshot-dialog.component.scss
@@ -0,0 +1,21 @@
+.title-container {
+ display: flex;
+ align-items: baseline;
+ justify-content: space-between;
+}
+
+.name-input {
+ width: 100%;
+}
+
+.top-button {
+ outline: none;
+ box-shadow: none !important;
+ background: transparent !important;
+}
+
+button:focus {
+ outline: 0 !important;
+ border: 0 !important;
+ box-shadow: none !important;
+}
diff --git a/src/app/components/snapshots/create-snapshot-dialog/create-snapshot-dialog.component.ts b/src/app/components/snapshots/create-snapshot-dialog/create-snapshot-dialog.component.ts
index ed415f68..739c543a 100644
--- a/src/app/components/snapshots/create-snapshot-dialog/create-snapshot-dialog.component.ts
+++ b/src/app/components/snapshots/create-snapshot-dialog/create-snapshot-dialog.component.ts
@@ -1,6 +1,9 @@
import { Component, Inject } from '@angular/core';
import { Snapshot } from '../../../models/snapshot';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
+import { Server } from '../../../models/server';
+import { Project } from '../../../models/project';
+
@Component({
selector: 'app-create-snapshot-dialog',
@@ -8,12 +11,17 @@ import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
styleUrls: ['./create-snapshot-dialog.component.scss']
})
export class CreateSnapshotDialogComponent {
+ server: Server;
+ project: Project;
snapshot: Snapshot = new Snapshot();
constructor(
public dialogRef: MatDialogRef
,
@Inject(MAT_DIALOG_DATA) public data: any
- ) {}
+ ) {
+ this.server = data['server'];
+ this.project = data['project'];
+ }
onAddClick(): void {
this.dialogRef.close(this.snapshot);
diff --git a/src/app/components/snapshots/snapshots.component.scss b/src/app/components/snapshots/list-of-snapshots/list-of-snaphshots.component.spec.ts
similarity index 100%
rename from src/app/components/snapshots/snapshots.component.scss
rename to src/app/components/snapshots/list-of-snapshots/list-of-snaphshots.component.spec.ts
diff --git a/src/app/components/snapshots/list-of-snapshots/list-of-snapshots.component.html b/src/app/components/snapshots/list-of-snapshots/list-of-snapshots.component.html
new file mode 100644
index 00000000..8f9adb59
--- /dev/null
+++ b/src/app/components/snapshots/list-of-snapshots/list-of-snapshots.component.html
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+ Name
+ {{row.name}}
+
+
+
+ Date
+ {{row.created_at | datefilter}}
+
+
+
+ Actions
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/app/components/snapshots/list-of-snapshots/list-of-snapshots.component.scss b/src/app/components/snapshots/list-of-snapshots/list-of-snapshots.component.scss
new file mode 100644
index 00000000..e69de29b
diff --git a/src/app/components/snapshots/list-of-snapshots/list-of-snapshots.component.ts b/src/app/components/snapshots/list-of-snapshots/list-of-snapshots.component.ts
new file mode 100644
index 00000000..8b25083c
--- /dev/null
+++ b/src/app/components/snapshots/list-of-snapshots/list-of-snapshots.component.ts
@@ -0,0 +1,94 @@
+import { Component, OnInit } from '@angular/core';
+import { SnapshotService } from '../../../services/snapshot.service';
+import { ServerService } from '../../../services/server.service';
+import { ActivatedRoute } from '@angular/router';
+import { Server } from '../../..//models/server';
+import { Snapshot } from '../../../models/snapshot';
+import { Sort } from '@angular/material';
+import { ProgressDialogService } from '../../../common/progress-dialog/progress-dialog.service';
+import { ToasterService } from '../../../services/toaster.service';
+import { Project } from '../../../models/project';
+import { ProgressDialogComponent } from '../../../common/progress-dialog/progress-dialog.component';
+
+@Component({
+ selector: 'app-list-of-snapshots',
+ templateUrl: './list-of-snapshots.component.html',
+ styleUrls: ['./list-of-snapshots.component.scss']
+})
+export class ListOfSnapshotsComponent implements OnInit {
+ server: Server;
+ projectId: string;
+ snapshots: Snapshot[];
+ displayedColumns = ['name', 'creationDate', 'actions'];
+
+ constructor(
+ private snapshotService: SnapshotService,
+ private serverService: ServerService,
+ private route: ActivatedRoute,
+ private progressDialogService: ProgressDialogService,
+ private toaster: ToasterService
+ ) {}
+
+ ngOnInit() {
+ let serverId = this.route.snapshot.paramMap.get("server_id");
+ this.projectId = this.route.snapshot.paramMap.get("project_id");
+ this.serverService.get(parseInt(serverId, 10)).then((server: Server) => {
+ this.server = server;
+ this.getSnapshots();
+ });
+ }
+
+ getSnapshots() {
+ this.snapshotService.list(this.server, this.projectId).subscribe((snapshots: Snapshot[]) => {
+ this.snapshots = snapshots;
+ });
+ }
+
+ restoreSnapshot(snapshot: Snapshot) {
+ const restoring = this.snapshotService.restore(this.server, this.projectId, snapshot.snapshot_id.toString());
+ const progress = this.progressDialogService.open();
+ const subscription = restoring.subscribe(
+ (project: Project) => {
+ this.toaster.success(`Snapshot ${snapshot.name} has been restored.`);
+ progress.close();
+ }
+ );
+
+ progress.afterClosed().subscribe(result => {
+ if (result === ProgressDialogComponent.CANCELLED) {
+ subscription.unsubscribe();
+ }
+ });
+ }
+
+ deleteSnapshot(snapshot: Snapshot) {
+ this.snapshotService.delete(this.server, this.projectId, snapshot.snapshot_id.toString()).subscribe(() => {
+ this.getSnapshots();
+ this.toaster.success(`Snapshot ${snapshot.name} has been deleted.`);
+ });
+ }
+
+ sortData(sort: Sort) {
+ if (!sort.active || sort.direction === '') return;
+
+ let snapshots = this.snapshots.slice();
+ this.snapshots = snapshots.sort((a, b) => {
+ const isAsc = sort.direction === 'asc';
+ if (sort.active === 'name') {
+ return compareNames(a.name, b.name, isAsc);
+ } else if (sort.active === 'creationDate') {
+ return compareDates(+a.created_at, +b.created_at, !isAsc);
+ } else return 0;
+ });
+ }
+}
+
+function compareDates(a: number, b: number, isAsc: boolean) {
+ return (a < b ? -1 : 1) * (isAsc ? 1 : -1);
+}
+
+function compareNames(a: string, b: string, isAsc: boolean) {
+ a = a.toLowerCase();
+ b = b.toLowerCase();
+ return (a < b ? -1 : 1) * (isAsc ? 1 : -1);
+}
diff --git a/src/app/components/snapshots/snapshot-menu-item/snapshot-menu-item.component.ts b/src/app/components/snapshots/snapshot-menu-item/snapshot-menu-item.component.ts
index d2dc6a93..066a31e3 100644
--- a/src/app/components/snapshots/snapshot-menu-item/snapshot-menu-item.component.ts
+++ b/src/app/components/snapshots/snapshot-menu-item/snapshot-menu-item.component.ts
@@ -30,7 +30,11 @@ export class SnapshotMenuItemComponent implements OnInit {
public createSnapshotModal() {
const dialogRef = this.dialog.open(CreateSnapshotDialogComponent, {
- width: '250px'
+ width: '450px',
+ data: {
+ server: this.server,
+ project: this.project
+ }
});
dialogRef.afterClosed().subscribe(snapshot => {
diff --git a/src/app/components/snapshots/snapshots.component.html b/src/app/components/snapshots/snapshots.component.html
deleted file mode 100644
index fc5228ca..00000000
--- a/src/app/components/snapshots/snapshots.component.html
+++ /dev/null
@@ -1 +0,0 @@
-snapshots works!
diff --git a/src/app/components/snapshots/snapshots.component.spec.ts b/src/app/components/snapshots/snapshots.component.spec.ts
deleted file mode 100644
index 1e49c3d8..00000000
--- a/src/app/components/snapshots/snapshots.component.spec.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import { async, ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { SnapshotsComponent } from './snapshots.component';
-
-describe('SnapshotsComponent', () => {
- let component: SnapshotsComponent;
- let fixture: ComponentFixture;
-
- beforeEach(async(() => {
- TestBed.configureTestingModule({
- declarations: [SnapshotsComponent]
- }).compileComponents();
- }));
-
- beforeEach(() => {
- fixture = TestBed.createComponent(SnapshotsComponent);
- component = fixture.componentInstance;
- fixture.detectChanges();
- });
-
- it('should create', () => {
- expect(component).toBeTruthy();
- });
-});
diff --git a/src/app/components/snapshots/snapshots.component.ts b/src/app/components/snapshots/snapshots.component.ts
deleted file mode 100644
index bf0984e9..00000000
--- a/src/app/components/snapshots/snapshots.component.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-
-@Component({
- selector: 'app-snapshots',
- templateUrl: './snapshots.component.html',
- styleUrls: ['./snapshots.component.scss']
-})
-export class SnapshotsComponent implements OnInit {
- constructor() {}
-
- ngOnInit() {}
-}
diff --git a/src/app/filters/dateFilter.pipe.spec.ts b/src/app/filters/dateFilter.pipe.spec.ts
new file mode 100644
index 00000000..e69de29b
diff --git a/src/app/filters/dateFilter.pipe.ts b/src/app/filters/dateFilter.pipe.ts
new file mode 100644
index 00000000..930a8683
--- /dev/null
+++ b/src/app/filters/dateFilter.pipe.ts
@@ -0,0 +1,21 @@
+import { Pipe, PipeTransform } from '@angular/core';
+
+
+@Pipe({
+ name: 'datefilter'
+})
+export class DateFilter implements PipeTransform {
+ transform(timestamp: string): string {
+ let date = new Date(+timestamp*1000);
+
+ let hours = date.getHours();
+ let minutes = "0" + date.getMinutes();
+ let seconds = "0" + date.getSeconds();
+
+ let year = date.getFullYear();
+ let month = date.getMonth() + 1;
+ let day = date.getDate();
+
+ return hours + ':' + minutes.substr(-2) + ':' + seconds.substr(-2) + ' ' + day + '/' + month + '/' + year;
+ }
+}
diff --git a/src/app/services/snapshot.service.ts b/src/app/services/snapshot.service.ts
index 1613606b..ea36c657 100644
--- a/src/app/services/snapshot.service.ts
+++ b/src/app/services/snapshot.service.ts
@@ -1,6 +1,5 @@
import { Injectable } from '@angular/core';
import { Server } from '../models/server';
-import { Observable } from 'rxjs';
import { HttpServer } from './http-server.service';
import { Snapshot } from '../models/snapshot';
@@ -12,7 +11,15 @@ export class SnapshotService {
return this.httpServer.post(server, `/projects/${project_id}/snapshots`, snapshot);
}
+ delete(server: Server, project_id: string, snapshot_id: string) {
+ return this.httpServer.delete(server, `/projects/${project_id}/snapshots/${snapshot_id}`);
+ }
+
list(server: Server, project_id: string) {
return this.httpServer.get(server, `/projects/${project_id}/snapshots`);
}
+
+ restore(server: Server, project_id: string, snapshot_id: string) {
+ return this.httpServer.post(server, `/projects/${project_id}/snapshots/${snapshot_id}/restore`, {});
+ }
}