mirror of
https://github.com/GNS3/gns3-web-ui.git
synced 2025-06-02 07:20:42 +00:00
Merge pull request #320 from GNS3/Ability-to-manage-snapshots
Managing snapshots
This commit is contained in:
commit
23819b48c0
@ -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 { 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 { 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 { 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 = [
|
const routes: Routes = [
|
||||||
{
|
{
|
||||||
@ -62,6 +63,7 @@ const routes: Routes = [
|
|||||||
{ path: 'server/:server_id/projects', component: ProjectsComponent },
|
{ path: 'server/:server_id/projects', component: ProjectsComponent },
|
||||||
{ path: 'settings', component: SettingsComponent },
|
{ path: 'settings', component: SettingsComponent },
|
||||||
{ path: 'installed-software', component: InstalledSoftwareComponent },
|
{ 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', component: PreferencesComponent },
|
||||||
// { path: 'server/:server_id/preferences/general', component: GeneralPreferencesComponent },
|
// { path: 'server/:server_id/preferences/general', component: GeneralPreferencesComponent },
|
||||||
{ path: 'server/:server_id/preferences/builtin', component: BuiltInPreferencesComponent},
|
{ 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/preferences/iou/addtemplate', component: AddIouTemplateComponent }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{ path: 'server/:server_id/project/:project_id', component: ProjectMapComponent }
|
{
|
||||||
|
path: 'server/:server_id/project/:project_id', component: ProjectMapComponent,
|
||||||
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
@ -68,7 +68,6 @@ import { RavenState } from './common/error-handlers/raven-state-communicator';
|
|||||||
import { ServerDiscoveryComponent } from './components/servers/server-discovery/server-discovery.component';
|
import { ServerDiscoveryComponent } from './components/servers/server-discovery/server-discovery.component';
|
||||||
import { ServerDatabase } from './services/server.database';
|
import { ServerDatabase } from './services/server.database';
|
||||||
import { CreateSnapshotDialogComponent } from './components/snapshots/create-snapshot-dialog/create-snapshot-dialog.component';
|
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 { SnapshotMenuItemComponent } from './components/snapshots/snapshot-menu-item/snapshot-menu-item.component';
|
||||||
import { MATERIAL_IMPORTS } from './material.imports';
|
import { MATERIAL_IMPORTS } from './material.imports';
|
||||||
import { DrawingService } from './services/drawing.service';
|
import { DrawingService } from './services/drawing.service';
|
||||||
@ -165,6 +164,9 @@ import { SearchFilter } from './filters/searchFilter.pipe';
|
|||||||
import { RecentlyOpenedProjectService } from './services/recentlyOpenedProject.service';
|
import { RecentlyOpenedProjectService } from './services/recentlyOpenedProject.service';
|
||||||
import { ServerManagementService } from './services/server-management.service';
|
import { ServerManagementService } from './services/server-management.service';
|
||||||
import { DeleteActionComponent } from './components/project-map/context-menu/actions/delete-action/delete-action.component';
|
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';
|
||||||
|
import { NameFilter } from './filters/nameFilter.pipe';
|
||||||
import { CustomAdaptersComponent } from './components/preferences/common/custom-adapters/custom-adapters.component';
|
import { CustomAdaptersComponent } from './components/preferences/common/custom-adapters/custom-adapters.component';
|
||||||
|
|
||||||
if (environment.production) {
|
if (environment.production) {
|
||||||
@ -184,7 +186,6 @@ if (environment.production) {
|
|||||||
AddServerDialogComponent,
|
AddServerDialogComponent,
|
||||||
CreateSnapshotDialogComponent,
|
CreateSnapshotDialogComponent,
|
||||||
SnapshotMenuItemComponent,
|
SnapshotMenuItemComponent,
|
||||||
SnapshotsComponent,
|
|
||||||
ProjectsComponent,
|
ProjectsComponent,
|
||||||
AddBlankProjectDialogComponent,
|
AddBlankProjectDialogComponent,
|
||||||
ImportProjectDialogComponent,
|
ImportProjectDialogComponent,
|
||||||
@ -269,6 +270,9 @@ if (environment.production) {
|
|||||||
EmptyTemplatesListComponent,
|
EmptyTemplatesListComponent,
|
||||||
SymbolsMenuComponent,
|
SymbolsMenuComponent,
|
||||||
SearchFilter,
|
SearchFilter,
|
||||||
|
DateFilter,
|
||||||
|
NameFilter,
|
||||||
|
ListOfSnapshotsComponent,
|
||||||
CustomAdaptersComponent
|
CustomAdaptersComponent
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
|
@ -8,7 +8,8 @@ export class ProgressDialogService {
|
|||||||
|
|
||||||
public open() {
|
public open() {
|
||||||
const ref = this.dialog.open(ProgressDialogComponent, {
|
const ref = this.dialog.open(ProgressDialogComponent, {
|
||||||
width: '250px'
|
width: '250px',
|
||||||
|
autoFocus: false
|
||||||
});
|
});
|
||||||
return ref;
|
return ref;
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,8 @@ export class DeleteTemplateComponent {
|
|||||||
height: '250px',
|
height: '250px',
|
||||||
data: {
|
data: {
|
||||||
templateName: templateName
|
templateName: templateName
|
||||||
}
|
},
|
||||||
|
autoFocus: false
|
||||||
});
|
});
|
||||||
|
|
||||||
dialogRef.afterClosed().subscribe((answer: boolean) => {
|
dialogRef.afterClosed().subscribe((answer: boolean) => {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { ComponentFixture, async, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, async, TestBed } from '@angular/core/testing';
|
||||||
import { MatInputModule, MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, MatSelectModule, MatFormFieldModule, MatAutocompleteModule, MatTableModule } from '@angular/material';
|
import { MatInputModule, MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, MatSelectModule, MatFormFieldModule, MatAutocompleteModule, MatTableModule, MatStepperModule } from '@angular/material';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
import { RouterTestingModule } from '@angular/router/testing';
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
@ -36,7 +36,7 @@ describe('AddDockerTemplateComponent', () => {
|
|||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [ FormsModule, MatTableModule, MatAutocompleteModule, MatFormFieldModule, MatInputModule, ReactiveFormsModule, MatSelectModule, MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, CommonModule, NoopAnimationsModule, RouterTestingModule.withRoutes([])],
|
imports: [MatStepperModule, FormsModule, MatTableModule, MatAutocompleteModule, MatFormFieldModule, MatInputModule, ReactiveFormsModule, MatSelectModule, MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, CommonModule, NoopAnimationsModule, RouterTestingModule.withRoutes([])],
|
||||||
providers: [
|
providers: [
|
||||||
{
|
{
|
||||||
provide: ActivatedRoute, useValue: activatedRoute
|
provide: ActivatedRoute, useValue: activatedRoute
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { ComponentFixture, async, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, async, TestBed } from '@angular/core/testing';
|
||||||
import { MatInputModule, MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, MatSelectModule, MatFormFieldModule, MatAutocompleteModule, MatTableModule } from '@angular/material';
|
import { MatInputModule, MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, MatSelectModule, MatFormFieldModule, MatAutocompleteModule, MatTableModule, MatStepperModule } from '@angular/material';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
import { RouterTestingModule } from '@angular/router/testing';
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
@ -36,7 +36,7 @@ describe('AddIosTemplateComponent', () => {
|
|||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [ FormsModule, MatTableModule, MatAutocompleteModule, MatFormFieldModule, MatInputModule, ReactiveFormsModule, MatSelectModule, MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, CommonModule, NoopAnimationsModule, RouterTestingModule.withRoutes([])],
|
imports: [MatStepperModule, FormsModule, MatTableModule, MatAutocompleteModule, MatFormFieldModule, MatInputModule, ReactiveFormsModule, MatSelectModule, MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, CommonModule, NoopAnimationsModule, RouterTestingModule.withRoutes([])],
|
||||||
providers: [
|
providers: [
|
||||||
{
|
{
|
||||||
provide: ActivatedRoute, useValue: activatedRoute
|
provide: ActivatedRoute, useValue: activatedRoute
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { ComponentFixture, async, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, async, TestBed } from '@angular/core/testing';
|
||||||
import { MatInputModule, MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, MatSelectModule, MatFormFieldModule, MatAutocompleteModule, MatTableModule } from '@angular/material';
|
import { MatInputModule, MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, MatSelectModule, MatFormFieldModule, MatAutocompleteModule, MatTableModule, MatStepperModule } from '@angular/material';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
import { RouterTestingModule } from '@angular/router/testing';
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
@ -36,7 +36,7 @@ describe('AddIouTemplateComponent', () => {
|
|||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [ FormsModule, MatTableModule, MatAutocompleteModule, MatFormFieldModule, MatInputModule, ReactiveFormsModule, MatSelectModule, MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, CommonModule, NoopAnimationsModule, RouterTestingModule.withRoutes([])],
|
imports: [MatStepperModule, FormsModule, MatTableModule, MatAutocompleteModule, MatFormFieldModule, MatInputModule, ReactiveFormsModule, MatSelectModule, MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, CommonModule, NoopAnimationsModule, RouterTestingModule.withRoutes([])],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: ActivatedRoute, useValue: activatedRoute },
|
{ provide: ActivatedRoute, useValue: activatedRoute },
|
||||||
{ provide: ServerService, useValue: mockedServerService },
|
{ provide: ServerService, useValue: mockedServerService },
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { ComponentFixture, async, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, async, TestBed } from '@angular/core/testing';
|
||||||
import { MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, MatSelectModule, MatAutocompleteModule, MatFormFieldModule, MatInputModule } from '@angular/material';
|
import { MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, MatSelectModule, MatAutocompleteModule, MatFormFieldModule, MatInputModule, MatStepperModule } from '@angular/material';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
import { RouterTestingModule } from '@angular/router/testing';
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
@ -47,7 +47,7 @@ describe('AddQemuVmTemplateComponent', () => {
|
|||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [FormsModule, ReactiveFormsModule, MatSelectModule, MatAutocompleteModule, MatIconModule, MatFormFieldModule, MatInputModule,
|
imports: [MatStepperModule, FormsModule, ReactiveFormsModule, MatSelectModule, MatAutocompleteModule, MatIconModule, MatFormFieldModule, MatInputModule,
|
||||||
MatToolbarModule, MatMenuModule, MatCheckboxModule, CommonModule, NoopAnimationsModule, RouterTestingModule.withRoutes([])],
|
MatToolbarModule, MatMenuModule, MatCheckboxModule, CommonModule, NoopAnimationsModule, RouterTestingModule.withRoutes([])],
|
||||||
providers: [
|
providers: [
|
||||||
{
|
{
|
||||||
|
@ -6,7 +6,6 @@ import { VpcsService } from '../../../../services/vpcs.service';
|
|||||||
import { VpcsTemplate } from '../../../../models/templates/vpcs-template';
|
import { VpcsTemplate } from '../../../../models/templates/vpcs-template';
|
||||||
import { DeleteTemplateComponent } from '../../common/delete-template-component/delete-template.component';
|
import { DeleteTemplateComponent } from '../../common/delete-template-component/delete-template.component';
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-vpcs-templates',
|
selector: 'app-vpcs-templates',
|
||||||
templateUrl: './vpcs-templates.component.html',
|
templateUrl: './vpcs-templates.component.html',
|
||||||
|
@ -20,7 +20,8 @@ export class EditStyleActionComponent implements OnInit {
|
|||||||
|
|
||||||
editStyle() {
|
editStyle() {
|
||||||
const dialogRef = this.dialog.open(StyleEditorDialogComponent, {
|
const dialogRef = this.dialog.open(StyleEditorDialogComponent, {
|
||||||
width: '300px'
|
width: '300px',
|
||||||
|
autoFocus: false
|
||||||
});
|
});
|
||||||
let instance = dialogRef.componentInstance;
|
let instance = dialogRef.componentInstance;
|
||||||
instance.server = this.server;
|
instance.server = this.server;
|
||||||
|
@ -20,7 +20,8 @@ export class EditTextActionComponent implements OnInit {
|
|||||||
|
|
||||||
editText() {
|
editText() {
|
||||||
const dialogRef = this.dialog.open(TextEditorDialogComponent, {
|
const dialogRef = this.dialog.open(TextEditorDialogComponent, {
|
||||||
width: '300px'
|
width: '300px',
|
||||||
|
autoFocus: false
|
||||||
});
|
});
|
||||||
let instance = dialogRef.componentInstance;
|
let instance = dialogRef.componentInstance;
|
||||||
instance.server = this.server;
|
instance.server = this.server;
|
||||||
|
@ -83,7 +83,8 @@ export class AddBlankProjectDialogComponent implements OnInit {
|
|||||||
height: '150px',
|
height: '150px',
|
||||||
data: {
|
data: {
|
||||||
existingProject: existingProject
|
existingProject: existingProject
|
||||||
}
|
},
|
||||||
|
autoFocus: false
|
||||||
});
|
});
|
||||||
|
|
||||||
dialogRef.afterClosed().subscribe((answer: boolean) => {
|
dialogRef.afterClosed().subscribe((answer: boolean) => {
|
||||||
|
@ -106,7 +106,8 @@ export class ImportProjectDialogComponent implements OnInit {
|
|||||||
height: '150px',
|
height: '150px',
|
||||||
data: {
|
data: {
|
||||||
existingProject: existingProject
|
existingProject: existingProject
|
||||||
}
|
},
|
||||||
|
autoFocus: false
|
||||||
});
|
});
|
||||||
|
|
||||||
dialogRef.afterClosed().subscribe((answer: boolean) => {
|
dialogRef.afterClosed().subscribe((answer: boolean) => {
|
||||||
|
@ -109,7 +109,8 @@ export class ProjectsComponent implements OnInit {
|
|||||||
|
|
||||||
addBlankProject() {
|
addBlankProject() {
|
||||||
const dialogRef = this.dialog.open(AddBlankProjectDialogComponent, {
|
const dialogRef = this.dialog.open(AddBlankProjectDialogComponent, {
|
||||||
width: '400px'
|
width: '400px',
|
||||||
|
autoFocus: false
|
||||||
});
|
});
|
||||||
let instance = dialogRef.componentInstance;
|
let instance = dialogRef.componentInstance;
|
||||||
instance.server = this.server;
|
instance.server = this.server;
|
||||||
@ -117,7 +118,8 @@ export class ProjectsComponent implements OnInit {
|
|||||||
|
|
||||||
importProject() {
|
importProject() {
|
||||||
const dialogRef = this.dialog.open(ImportProjectDialogComponent, {
|
const dialogRef = this.dialog.open(ImportProjectDialogComponent, {
|
||||||
width: '400px'
|
width: '400px',
|
||||||
|
autoFocus: false
|
||||||
});
|
});
|
||||||
let instance = dialogRef.componentInstance;
|
let instance = dialogRef.componentInstance;
|
||||||
instance.server = this.server;
|
instance.server = this.server;
|
||||||
|
@ -69,7 +69,8 @@ export class ServersComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
createModal() {
|
createModal() {
|
||||||
const dialogRef = this.dialog.open(AddServerDialogComponent, {
|
const dialogRef = this.dialog.open(AddServerDialogComponent, {
|
||||||
width: '350px'
|
width: '350px',
|
||||||
|
autoFocus: false
|
||||||
});
|
});
|
||||||
|
|
||||||
dialogRef.afterClosed().subscribe(server => {
|
dialogRef.afterClosed().subscribe(server => {
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
<h1 mat-dialog-title>Create snapshot</h1>
|
<div class="title-container">
|
||||||
|
<h1 mat-dialog-title>Create snapshot</h1>
|
||||||
|
<button mat-button class="top-button" color="accent" (click)="onNoClick()" routerLink="/server/{{server.id}}/project/{{project.project_id}}/snapshots">Go to snapshots</button>
|
||||||
|
</div>
|
||||||
<div mat-dialog-content>
|
<div mat-dialog-content>
|
||||||
<mat-form-field> <input matInput tabindex="1" [(ngModel)]="snapshot.name" placeholder="Name" /> </mat-form-field>
|
<mat-form-field class="name-input">
|
||||||
|
<input matInput tabindex="1" [(ngModel)]="snapshot.name" placeholder="Name" />
|
||||||
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
<div mat-dialog-actions>
|
<div mat-dialog-actions>
|
||||||
<button mat-button (click)="onNoClick()" tabindex="-1" color="accent">No Thanks</button>
|
<button mat-button (click)="onNoClick()" tabindex="-1" color="accent">No Thanks</button>
|
||||||
|
@ -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;
|
||||||
|
}
|
@ -1,6 +1,9 @@
|
|||||||
import { Component, Inject } from '@angular/core';
|
import { Component, Inject } from '@angular/core';
|
||||||
import { Snapshot } from '../../../models/snapshot';
|
import { Snapshot } from '../../../models/snapshot';
|
||||||
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
|
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
|
||||||
|
import { Server } from '../../../models/server';
|
||||||
|
import { Project } from '../../../models/project';
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-create-snapshot-dialog',
|
selector: 'app-create-snapshot-dialog',
|
||||||
@ -8,12 +11,17 @@ import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
|
|||||||
styleUrls: ['./create-snapshot-dialog.component.scss']
|
styleUrls: ['./create-snapshot-dialog.component.scss']
|
||||||
})
|
})
|
||||||
export class CreateSnapshotDialogComponent {
|
export class CreateSnapshotDialogComponent {
|
||||||
|
server: Server;
|
||||||
|
project: Project;
|
||||||
snapshot: Snapshot = new Snapshot();
|
snapshot: Snapshot = new Snapshot();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public dialogRef: MatDialogRef<CreateSnapshotDialogComponent>,
|
public dialogRef: MatDialogRef<CreateSnapshotDialogComponent>,
|
||||||
@Inject(MAT_DIALOG_DATA) public data: any
|
@Inject(MAT_DIALOG_DATA) public data: any
|
||||||
) {}
|
) {
|
||||||
|
this.server = data['server'];
|
||||||
|
this.project = data['project'];
|
||||||
|
}
|
||||||
|
|
||||||
onAddClick(): void {
|
onAddClick(): void {
|
||||||
this.dialogRef.close(this.snapshot);
|
this.dialogRef.close(this.snapshot);
|
||||||
|
@ -0,0 +1,143 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { ListOfSnapshotsComponent } from './list-of-snapshots.component';
|
||||||
|
import { MatTableModule, MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, MatDialogModule, Sort } from '@angular/material';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
|
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
|
import { SnapshotService } from '../../../services/snapshot.service';
|
||||||
|
import { DateFilter } from '../../../filters/dateFilter.pipe';
|
||||||
|
import { of } from 'rxjs';
|
||||||
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
import { ServerService } from '../../../services/server.service';
|
||||||
|
import { MockedServerService } from '../../../services/server.service.spec';
|
||||||
|
import { ProgressDialogService } from '../../../common/progress-dialog/progress-dialog.service';
|
||||||
|
import { ToasterService } from '../../../services/toaster.service';
|
||||||
|
import { Server } from '../../../models/server';
|
||||||
|
import { Snapshot } from '../../../models/snapshot';
|
||||||
|
import { MockedToasterService } from '../../../services/toaster.service.spec';
|
||||||
|
import { NameFilter } from '../../../filters/nameFilter.pipe';
|
||||||
|
|
||||||
|
export class MockedActivatedRoute {
|
||||||
|
get() {
|
||||||
|
return {
|
||||||
|
params: of({ id: 3 }),
|
||||||
|
snapshot: {
|
||||||
|
parent: {
|
||||||
|
params: {
|
||||||
|
id: 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
paramMap: {
|
||||||
|
get(name: string): string {
|
||||||
|
return '1';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MockedSnapshotService {
|
||||||
|
public list(server: Server, project_id: string) {
|
||||||
|
return of([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public delete(server: Server, project_id: string, snapshot_id: string) {
|
||||||
|
return of({});
|
||||||
|
}
|
||||||
|
|
||||||
|
public restore(server: Server, project_id: string, snapshot_id: string) {
|
||||||
|
return of({});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('ListOfSnapshotsComponent', () => {
|
||||||
|
let component: ListOfSnapshotsComponent;
|
||||||
|
let fixture: ComponentFixture<ListOfSnapshotsComponent>;
|
||||||
|
let activatedRoute = new MockedActivatedRoute().get();
|
||||||
|
let mockedServerService = new MockedServerService();
|
||||||
|
let mockedSnapshotService = new MockedSnapshotService();
|
||||||
|
let mockedToasterService = new MockedToasterService();
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [MatDialogModule, MatTableModule, MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, CommonModule, NoopAnimationsModule, RouterTestingModule.withRoutes([])],
|
||||||
|
providers: [
|
||||||
|
{ provide: SnapshotService, useValue: mockedSnapshotService },
|
||||||
|
{ provide: ActivatedRoute, useValue: activatedRoute },
|
||||||
|
{ provide: ServerService, useValue: mockedServerService },
|
||||||
|
{ provide: ProgressDialogService, useClass: ProgressDialogService },
|
||||||
|
{ provide: ToasterService, useValue: mockedToasterService }
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
ListOfSnapshotsComponent,
|
||||||
|
DateFilter,
|
||||||
|
NameFilter
|
||||||
|
],
|
||||||
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(ListOfSnapshotsComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call snapshot service to get items', () => {
|
||||||
|
spyOn(mockedSnapshotService, 'list').and.returnValues(of([]));
|
||||||
|
|
||||||
|
component.getSnapshots();
|
||||||
|
|
||||||
|
expect(mockedSnapshotService.list).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call snapshot service to delete snapshot', () => {
|
||||||
|
let snapshot: Snapshot = {
|
||||||
|
snapshot_id: 1,
|
||||||
|
name: 'snapshot1',
|
||||||
|
created_at: '111111',
|
||||||
|
project_id: 1
|
||||||
|
};
|
||||||
|
spyOn(mockedSnapshotService, 'delete').and.returnValues(of([]));
|
||||||
|
|
||||||
|
component.deleteSnapshot(snapshot);
|
||||||
|
|
||||||
|
expect(mockedSnapshotService.delete).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should sort snapshots in correct order', () => {
|
||||||
|
component.snapshots = [
|
||||||
|
{
|
||||||
|
snapshot_id: 2,
|
||||||
|
name: 'second snapshot',
|
||||||
|
created_at: '222222',
|
||||||
|
project_id: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
snapshot_id: 1,
|
||||||
|
name: 'first snapshot',
|
||||||
|
created_at: '111111',
|
||||||
|
project_id: 1
|
||||||
|
}
|
||||||
|
];
|
||||||
|
let sort: Sort = {
|
||||||
|
active: 'name',
|
||||||
|
direction: 'asc'
|
||||||
|
};
|
||||||
|
|
||||||
|
component.sortData(sort);
|
||||||
|
|
||||||
|
expect(component.snapshots[0].snapshot_id).toBe(1);
|
||||||
|
|
||||||
|
sort.direction = 'desc';
|
||||||
|
component.sortData(sort);
|
||||||
|
|
||||||
|
expect(component.snapshots[0].snapshot_id).toBe(2);
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,46 @@
|
|||||||
|
<div class="content">
|
||||||
|
<div class="default-header">
|
||||||
|
<div class="row">
|
||||||
|
<h1 class="col">Snapshots</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="default-content">
|
||||||
|
<mat-card>
|
||||||
|
<mat-form-field class="filter-field">
|
||||||
|
<input matInput [(ngModel)]="searchText" placeholder="Filter">
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-table
|
||||||
|
class="mat-table"
|
||||||
|
#table matSort
|
||||||
|
(matSortChange)= "sortData($event)"
|
||||||
|
[dataSource]="snapshots | namefilter: searchText">
|
||||||
|
<ng-container matColumnDef="name">
|
||||||
|
<mat-header-cell *matHeaderCellDef mat-sort-header> Name </mat-header-cell>
|
||||||
|
<mat-cell *matCellDef="let row"> {{row.name}} </mat-cell>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="creationDate">
|
||||||
|
<mat-header-cell *matHeaderCellDef mat-sort-header> Date </mat-header-cell>
|
||||||
|
<mat-cell *matCellDef="let row"> {{row.created_at | datefilter}} </mat-cell>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="actions">
|
||||||
|
<mat-header-cell *matHeaderCellDef> Actions </mat-header-cell>
|
||||||
|
<mat-cell *matCellDef="let row" style="text-align: right">
|
||||||
|
<button mat-icon-button matTooltip="Restore snapshot" (click)="restoreSnapshot(row)">
|
||||||
|
<mat-icon aria-label="Restore snapshot">restore</mat-icon>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button mat-icon-button matTooltip="Delete snapshot" (click)="deleteSnapshot(row)">
|
||||||
|
<mat-icon aria-label="Delete snapshot">delete</mat-icon>
|
||||||
|
</button>
|
||||||
|
</mat-cell>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
|
||||||
|
<mat-row *matRowDef="let row; columns: displayedColumns"></mat-row>
|
||||||
|
</mat-table>
|
||||||
|
</mat-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -0,0 +1,7 @@
|
|||||||
|
.filter-field {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-table {
|
||||||
|
margin: -16px!important;
|
||||||
|
}
|
@ -0,0 +1,95 @@
|
|||||||
|
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'];
|
||||||
|
searchText: string;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
@ -30,7 +30,12 @@ export class SnapshotMenuItemComponent implements OnInit {
|
|||||||
|
|
||||||
public createSnapshotModal() {
|
public createSnapshotModal() {
|
||||||
const dialogRef = this.dialog.open(CreateSnapshotDialogComponent, {
|
const dialogRef = this.dialog.open(CreateSnapshotDialogComponent, {
|
||||||
width: '250px'
|
width: '450px',
|
||||||
|
data: {
|
||||||
|
server: this.server,
|
||||||
|
project: this.project
|
||||||
|
},
|
||||||
|
autoFocus: false
|
||||||
});
|
});
|
||||||
|
|
||||||
dialogRef.afterClosed().subscribe(snapshot => {
|
dialogRef.afterClosed().subscribe(snapshot => {
|
||||||
|
@ -1 +0,0 @@
|
|||||||
<p>snapshots works!</p>
|
|
@ -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<SnapshotsComponent>;
|
|
||||||
|
|
||||||
beforeEach(async(() => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
declarations: [SnapshotsComponent]
|
|
||||||
}).compileComponents();
|
|
||||||
}));
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
fixture = TestBed.createComponent(SnapshotsComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
@ -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() {}
|
|
||||||
}
|
|
@ -24,7 +24,8 @@ export class TemplateComponent implements OnInit {
|
|||||||
height: '560px',
|
height: '560px',
|
||||||
data: {
|
data: {
|
||||||
server: this.server
|
server: this.server
|
||||||
}
|
},
|
||||||
|
autoFocus: false
|
||||||
});
|
});
|
||||||
|
|
||||||
dialogRef.afterClosed().subscribe((template: Template) => {
|
dialogRef.afterClosed().subscribe((template: Template) => {
|
||||||
|
21
src/app/filters/dateFilter.pipe.ts
Normal file
21
src/app/filters/dateFilter.pipe.ts
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
0
src/app/filters/nameFilter.pipe.spec.ts
Normal file
0
src/app/filters/nameFilter.pipe.spec.ts
Normal file
17
src/app/filters/nameFilter.pipe.ts
Normal file
17
src/app/filters/nameFilter.pipe.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { Pipe, PipeTransform } from '@angular/core';
|
||||||
|
|
||||||
|
|
||||||
|
@Pipe({
|
||||||
|
name: 'namefilter'
|
||||||
|
})
|
||||||
|
export class NameFilter implements PipeTransform {
|
||||||
|
transform(items: any[], searchText: string): any[] {
|
||||||
|
if(!items) return [];
|
||||||
|
if(!searchText) return items;
|
||||||
|
|
||||||
|
searchText = searchText.toLowerCase();
|
||||||
|
return items.filter( item => {
|
||||||
|
return item.name.toLowerCase().includes(searchText);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,5 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { Server } from '../models/server';
|
import { Server } from '../models/server';
|
||||||
import { Observable } from 'rxjs';
|
|
||||||
import { HttpServer } from './http-server.service';
|
import { HttpServer } from './http-server.service';
|
||||||
import { Snapshot } from '../models/snapshot';
|
import { Snapshot } from '../models/snapshot';
|
||||||
|
|
||||||
@ -12,7 +11,15 @@ export class SnapshotService {
|
|||||||
return this.httpServer.post<Snapshot>(server, `/projects/${project_id}/snapshots`, snapshot);
|
return this.httpServer.post<Snapshot>(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) {
|
list(server: Server, project_id: string) {
|
||||||
return this.httpServer.get<Snapshot[]>(server, `/projects/${project_id}/snapshots`);
|
return this.httpServer.get<Snapshot[]>(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`, {});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user