]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: Add shared Confirmation Modal 22601/head
authorTiago Melo <tmelo@suse.com>
Mon, 18 Jun 2018 15:15:07 +0000 (16:15 +0100)
committerTiago Melo <tmelo@suse.com>
Wed, 20 Jun 2018 10:40:43 +0000 (11:40 +0100)
Replaced 'FlattenConfirmationModalComponent' and
'RollbackConfirmationModalComponent' with the new generic modal.

Any new confirmation modals should use the new component.

Signed-off-by: Tiago Melo <tmelo@suse.com>
18 files changed:
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/block.module.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/flatten-confirmation-modal/flatten-confimation-modal.component.html [deleted file]
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/flatten-confirmation-modal/flatten-confimation-modal.component.scss [deleted file]
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/flatten-confirmation-modal/flatten-confimation-modal.component.spec.ts [deleted file]
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/flatten-confirmation-modal/flatten-confimation-modal.component.ts [deleted file]
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-list/rbd-list.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-list/rbd-list.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-snapshot-list/rbd-snapshot-list.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-snapshot-list/rbd-snapshot-list.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rollback-confirmation-modal/rollback-confimation-modal.component.html [deleted file]
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rollback-confirmation-modal/rollback-confimation-modal.component.scss [deleted file]
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rollback-confirmation-modal/rollback-confimation-modal.component.spec.ts [deleted file]
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rollback-confirmation-modal/rollback-confimation-modal.component.ts [deleted file]
src/pybind/mgr/dashboard/frontend/src/app/shared/components/components.module.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/components/confirmation-modal/confirmation-modal.component.html [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/shared/components/confirmation-modal/confirmation-modal.component.scss [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/shared/components/confirmation-modal/confirmation-modal.component.spec.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/shared/components/confirmation-modal/confirmation-modal.component.ts [new file with mode: 0644]

index 477b80d4a0b63012ffcd30222a4d5dc7f2441043..78b2c4a013dc6deb1366c4330650c5daa7663b1c 100644 (file)
@@ -7,9 +7,6 @@ import { BsDropdownModule, ModalModule, TabsModule, TooltipModule } from 'ngx-bo
 import { ProgressbarModule } from 'ngx-bootstrap/progressbar';
 
 import { SharedModule } from '../../shared/shared.module';
-import {
-  FlattenConfirmationModalComponent
-} from './flatten-confirmation-modal/flatten-confimation-modal.component';
 import { IscsiComponent } from './iscsi/iscsi.component';
 import { MirrorHealthColorPipe } from './mirror-health-color.pipe';
 import { MirroringComponent } from './mirroring/mirroring.component';
@@ -18,17 +15,9 @@ import { RbdFormComponent } from './rbd-form/rbd-form.component';
 import { RbdListComponent } from './rbd-list/rbd-list.component';
 import { RbdSnapshotFormComponent } from './rbd-snapshot-form/rbd-snapshot-form.component';
 import { RbdSnapshotListComponent } from './rbd-snapshot-list/rbd-snapshot-list.component';
-import {
-  RollbackConfirmationModalComponent
-} from './rollback-confirmation-modal/rollback-confimation-modal.component';
 
 @NgModule({
-  entryComponents: [
-    RbdDetailsComponent,
-    RbdSnapshotFormComponent,
-    RollbackConfirmationModalComponent,
-    FlattenConfirmationModalComponent
-  ],
+  entryComponents: [RbdDetailsComponent, RbdSnapshotFormComponent],
   imports: [
     CommonModule,
     FormsModule,
@@ -49,9 +38,7 @@ import {
     RbdDetailsComponent,
     RbdFormComponent,
     RbdSnapshotListComponent,
-    RbdSnapshotFormComponent,
-    RollbackConfirmationModalComponent,
-    FlattenConfirmationModalComponent
+    RbdSnapshotFormComponent
   ]
 })
-export class BlockModule { }
+export class BlockModule {}
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/flatten-confirmation-modal/flatten-confimation-modal.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/flatten-confirmation-modal/flatten-confimation-modal.component.html
deleted file mode 100644 (file)
index 36d06d8..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-<div class="modal-header">
-  <h4 i18n
-      class="modal-title pull-left">RBD flatten</h4>
-  <button type="button" class="close pull-right" aria-label="Close" (click)="modalRef.hide()">
-    <span aria-hidden="true">&times;</span>
-  </button>
-</div>
-<form name="flattenForm"
-      class="form-horizontal"
-      #formDir="ngForm"
-      [formGroup]="flattenForm"
-      novalidate>
-  <div class="modal-body">
-    You are about to flatten <strong>{{ child }}</strong>.
-    <br>
-    <br>
-    All blocks will be copied from parent <strong>{{ parent }}</strong> to child <strong>{{ child }}</strong>.
-  </div>
-  <div class="modal-footer">
-    <div class="button-group text-right">
-      <cd-submit-button i18n
-                        [form]="flattenForm"
-                        (submitAction)="submit()">
-        Flatten
-      </cd-submit-button>
-      <button i18n type="button" class="btn btn-sm btn-default" (click)="modalRef.hide()">Cancel</button>
-    </div>
-  </div>
-</form>
-
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/flatten-confirmation-modal/flatten-confimation-modal.component.scss b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/flatten-confirmation-modal/flatten-confimation-modal.component.scss
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/flatten-confirmation-modal/flatten-confimation-modal.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/flatten-confirmation-modal/flatten-confimation-modal.component.spec.ts
deleted file mode 100644 (file)
index e4f094f..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-import { HttpClientTestingModule } from '@angular/common/http/testing';
-import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { ReactiveFormsModule } from '@angular/forms';
-
-import { ToastModule } from 'ng2-toastr';
-import { BsModalRef, BsModalService } from 'ngx-bootstrap';
-
-import { ApiModule } from '../../../shared/api/api.module';
-import { ServicesModule } from '../../../shared/services/services.module';
-import { SharedModule } from '../../../shared/shared.module';
-import { configureTestBed } from '../../../shared/unit-test-helper';
-import { FlattenConfirmationModalComponent } from './flatten-confimation-modal.component';
-
-describe('FlattenConfirmationModalComponent', () => {
-  let component: FlattenConfirmationModalComponent;
-  let fixture: ComponentFixture<FlattenConfirmationModalComponent>;
-
-  configureTestBed({
-    imports: [
-      ReactiveFormsModule,
-      HttpClientTestingModule,
-      SharedModule,
-      ServicesModule,
-      ApiModule,
-      ToastModule.forRoot()
-    ],
-    declarations: [FlattenConfirmationModalComponent],
-    providers: [BsModalRef, BsModalService]
-  });
-
-  beforeEach(() => {
-    fixture = TestBed.createComponent(FlattenConfirmationModalComponent);
-    component = fixture.componentInstance;
-    fixture.detectChanges();
-  });
-
-  it('should create', () => {
-    expect(component).toBeTruthy();
-  });
-});
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/flatten-confirmation-modal/flatten-confimation-modal.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/flatten-confirmation-modal/flatten-confimation-modal.component.ts
deleted file mode 100644 (file)
index bf9cd16..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-import { FormGroup } from '@angular/forms';
-
-import { BsModalRef } from 'ngx-bootstrap';
-import { Subject } from 'rxjs';
-
-@Component({
-  selector: 'cd-flatten-confimation-modal',
-  templateUrl: './flatten-confimation-modal.component.html',
-  styleUrls: ['./flatten-confimation-modal.component.scss']
-})
-export class FlattenConfirmationModalComponent implements OnInit {
-  child: string;
-  parent: string;
-
-  flattenForm: FormGroup;
-
-  public onSubmit: Subject<string>;
-
-  constructor(public modalRef: BsModalRef) {
-    this.createForm();
-  }
-
-  createForm() {
-    this.flattenForm = new FormGroup({});
-  }
-
-  ngOnInit() {
-    this.onSubmit = new Subject();
-  }
-
-  submit() {
-    this.onSubmit.next();
-  }
-}
index 1a3bbd06d9f7e550133de013b94f437a1bb0a82b..896d225724e02d2b47cc2d2d4ef9e5d5ca79d8bd 100644 (file)
           selectionType="single"
           (updateSelection)="updateSelection($event)">
   <div class="table-actions">
-    <div class="btn-group" dropdown>
+    <div class="btn-group"
+         dropdown>
       <button type="button"
               class="btn btn-sm btn-primary"
               *ngIf="!selection.hasSingleSelection"
               routerLink="/rbd/add">
-        <i class="fa fa-fw fa-plus"></i><span i18n>Add</span>
+        <i class="fa fa-fw fa-plus"></i>
+        <span i18n>Add</span>
       </button>
       <button type="button"
               class="btn btn-sm btn-primary"
               *ngIf="selection.hasSingleSelection"
               [ngClass]="{'disabled': selection.first().executing}"
               routerLink="/rbd/edit/{{ selection.first()?.pool_name }}/{{ selection.first()?.name }}">
-        <i class="fa fa-fw fa-pencil"></i><span i18n>Edit</span>
+        <i class="fa fa-fw fa-pencil"></i>
+        <span i18n>Edit</span>
       </button>
-      <button type="button" dropdownToggle class="btn btn-sm btn-primary dropdown-toggle dropdown-toggle-split">
+      <button type="button"
+              dropdownToggle
+              class="btn btn-sm btn-primary dropdown-toggle dropdown-toggle-split">
         <span class="caret"></span>
         <span class="sr-only"></span>
       </button>
-      <ul *dropdownMenu class="dropdown-menu" role="menu">
+      <ul *dropdownMenu
+          class="dropdown-menu"
+          role="menu">
         <li role="menuitem">
-          <a class="dropdown-item" routerLink="/rbd/add"><i class="fa fa-fw fa-plus"></i><span i18n>Add</span></a>
+          <a class="dropdown-item"
+             routerLink="/rbd/add">
+            <i class="fa fa-fw fa-plus"></i>
+            <span i18n>Add</span>
+          </a>
         </li>
         <li role="menuitem"
             [ngClass]="{'disabled': !selection.hasSingleSelection || selection.first().executing}">
-          <a class="dropdown-item" routerLink="/rbd/edit/{{ selection.first()?.pool_name }}/{{ selection.first()?.name }}"><i class="fa fa-fw fa-pencil"></i><span i18n>Edit</span></a>
+          <a class="dropdown-item"
+             routerLink="/rbd/edit/{{ selection.first()?.pool_name }}/{{ selection.first()?.name }}">
+            <i class="fa fa-fw fa-pencil"></i>
+            <span i18n>Edit</span>
+          </a>
         </li>
         <li role="menuitem"
             [ngClass]="{'disabled': !selection.hasSingleSelection || selection.first().executing}">
-          <a class="dropdown-item" routerLink="/rbd/copy/{{ selection.first()?.pool_name }}/{{ selection.first()?.name }}"><i class="fa fa-fw fa-copy"></i><span i18n>Copy</span></a>
+          <a class="dropdown-item"
+             routerLink="/rbd/copy/{{ selection.first()?.pool_name }}/{{ selection.first()?.name }}">
+            <i class="fa fa-fw fa-copy"></i>
+            <span i18n>Copy</span>
+          </a>
         </li>
         <li role="menuitem"
             [ngClass]="{'disabled': !selection.hasSingleSelection || selection.first().executing || !selection.first().parent}">
-          <a class="dropdown-item" (click)="flattenRbdModal()"><i class="fa fa-fw fa-chain-broken"></i><span i18n>Flatten</span></a>
+          <a class="dropdown-item"
+             (click)="flattenRbdModal()">
+            <i class="fa fa-fw fa-chain-broken"></i>
+            <span i18n>Flatten</span>
+          </a>
         </li>
         <li role="menuitem"
             [ngClass]="{'disabled': !selection.hasSingleSelection || selection.first().executing}">
-          <a class="dropdown-item" (click)="deleteRbdModal()"><i class="fa fa-fw fa-trash-o"></i><span i18n>Delete</span></a>
+          <a class="dropdown-item"
+             (click)="deleteRbdModal()">
+            <i class="fa fa-fw fa-trash-o"></i>
+            <span i18n>Delete</span>
+          </a>
         </li>
       </ul>
     </div>
@@ -67,7 +94,8 @@
 </cd-table>
 
 <ng-template #usageNotAvailableTooltipTpl>
-  <div i18n [innerHtml]="'Only available for RBD images with <strong>fast-diff</strong> enabled'"></div>
+  <div i18n
+       [innerHtml]="'Only available for RBD images with <strong>fast-diff</strong> enabled'"></div>
 </ng-template>
 
 <ng-template #parentTpl
   <span *ngIf="value">{{ value.pool_name }}/{{ value.image_name }}@{{ value.snap_name }}</span>
   <span *ngIf="!value">-</span>
 </ng-template>
+
+<ng-template #flattenTpl
+             let-value>
+  You are about to flatten
+  <strong>{{ value.child }}</strong>.
+  <br>
+  <br> All blocks will be copied from parent
+  <strong>{{ value.parent }}</strong> to child
+  <strong>{{ value.child }}</strong>.
+</ng-template>
index e31f15c9e034b4d05736b464e9c9da7a8faa372c..2587f086db2e3a1575b7727c176c1f659acc9c59 100644 (file)
@@ -4,9 +4,8 @@ import * as _ from 'lodash';
 import { BsModalRef, BsModalService } from 'ngx-bootstrap';
 
 import { RbdService } from '../../../shared/api/rbd.service';
-import {
-  DeletionModalComponent
-} from '../../../shared/components/deletion-modal/deletion-modal.component';
+import { ConfirmationModalComponent } from '../../../shared/components/confirmation-modal/confirmation-modal.component';
+import { DeletionModalComponent } from '../../../shared/components/deletion-modal/deletion-modal.component';
 import { CellTemplate } from '../../../shared/enum/cell-template.enum';
 import { ViewCacheStatus } from '../../../shared/enum/view-cache-status.enum';
 import { CdTableColumn } from '../../../shared/models/cd-table-column';
@@ -17,9 +16,6 @@ import { DimlessBinaryPipe } from '../../../shared/pipes/dimless-binary.pipe';
 import { DimlessPipe } from '../../../shared/pipes/dimless.pipe';
 import { SummaryService } from '../../../shared/services/summary.service';
 import { TaskWrapperService } from '../../../shared/services/task-wrapper.service';
-import {
-  FlattenConfirmationModalComponent
-} from '../flatten-confirmation-modal/flatten-confimation-modal.component';
 import { RbdParentModel } from '../rbd-form/rbd-parent.model';
 import { RbdModel } from './rbd-model';
 
@@ -29,11 +25,12 @@ import { RbdModel } from './rbd-model';
   styleUrls: ['./rbd-list.component.scss']
 })
 export class RbdListComponent implements OnInit, OnDestroy {
-
   @ViewChild('usageTpl') usageTpl: TemplateRef<any>;
   @ViewChild('parentTpl') parentTpl: TemplateRef<any>;
   @ViewChild('nameTpl') nameTpl: TemplateRef<any>;
 
+  @ViewChild('flattenTpl') flattenTpl: TemplateRef<any>;
+
   images: any;
   executingTasks: ExecutingTask[] = [];
   columns: CdTableColumn[];
@@ -45,13 +42,14 @@ export class RbdListComponent implements OnInit, OnDestroy {
 
   modalRef: BsModalRef;
 
-  constructor(private rbdService: RbdService,
-              private dimlessBinaryPipe: DimlessBinaryPipe,
-              private dimlessPipe: DimlessPipe,
-              private summaryService: SummaryService,
-              private modalService: BsModalService,
-              private taskWrapper: TaskWrapperService) {
-  }
+  constructor(
+    private rbdService: RbdService,
+    private dimlessBinaryPipe: DimlessBinaryPipe,
+    private dimlessPipe: DimlessPipe,
+    private summaryService: SummaryService,
+    private modalService: BsModalService,
+    private taskWrapper: TaskWrapperService
+  ) {}
 
   ngOnInit() {
     this.columns = [
@@ -115,7 +113,6 @@ export class RbdListComponent implements OnInit, OnDestroy {
         this.loadImages(data.executing_tasks);
       });
     });
-
   }
 
   ngOnDestroy() {
@@ -128,12 +125,11 @@ export class RbdListComponent implements OnInit, OnDestroy {
     if (executingTasks === null) {
       executingTasks = this.executingTasks;
     }
-    this.rbdService.list()
-      .subscribe(
+    this.rbdService.list().subscribe(
       (resp: any[]) => {
         let images = [];
         const viewCacheStatusMap = {};
-        resp.forEach(pool => {
+        resp.forEach((pool) => {
           if (_.isUndefined(viewCacheStatusMap[pool.status])) {
             viewCacheStatusMap[pool.status] = [];
           }
@@ -144,33 +140,43 @@ export class RbdListComponent implements OnInit, OnDestroy {
         _.forEach(viewCacheStatusMap, (value, key) => {
           viewCacheStatusList.push({
             status: parseInt(key, 10),
-            statusFor: (value.length > 1 ? 'pools ' : 'pool ') +
-            '<strong>' + value.join('</strong>, <strong>') + '</strong>'
+            statusFor:
+              (value.length > 1 ? 'pools ' : 'pool ') +
+              '<strong>' +
+              value.join('</strong>, <strong>') +
+              '</strong>'
           });
         });
         this.viewCacheStatusList = viewCacheStatusList;
-        images.forEach(image => {
-          image.executingTasks = this._getExecutingTasks(executingTasks,
-            image.pool_name, image.name);
+        images.forEach((image) => {
+          image.executingTasks = this._getExecutingTasks(
+            executingTasks,
+            image.pool_name,
+            image.name
+          );
         });
         this.images = this.merge(images, executingTasks);
         this.executingTasks = executingTasks;
       },
       () => {
-        this.viewCacheStatusList = [{status: ViewCacheStatus.ValueException}];
+        this.viewCacheStatusList = [{ status: ViewCacheStatus.ValueException }];
       }
     );
   }
 
   _getExecutingTasks(executingTasks: ExecutingTask[], poolName, imageName): ExecutingTask[] {
     const result: ExecutingTask[] = [];
-    executingTasks.forEach(executingTask => {
-      if (executingTask.name === 'rbd/snap/create' ||
+    executingTasks.forEach((executingTask) => {
+      if (
+        executingTask.name === 'rbd/snap/create' ||
         executingTask.name === 'rbd/snap/delete' ||
         executingTask.name === 'rbd/snap/edit' ||
-        executingTask.name === 'rbd/snap/rollback') {
-        if (poolName === executingTask.metadata['pool_name'] &&
-          imageName === executingTask.metadata['image_name']) {
+        executingTask.name === 'rbd/snap/rollback'
+      ) {
+        if (
+          poolName === executingTask.metadata['pool_name'] &&
+          imageName === executingTask.metadata['image_name']
+        ) {
           result.push(executingTask);
         }
       }
@@ -182,34 +188,31 @@ export class RbdListComponent implements OnInit, OnDestroy {
     const resultRBDs = _.clone(rbds);
     executingTasks.forEach((executingTask) => {
       const rbdExecuting = resultRBDs.find((rbd) => {
-        return rbd.pool_name === executingTask.metadata['pool_name'] &&
-          rbd.name === executingTask.metadata['image_name'];
+        return (
+          rbd.pool_name === executingTask.metadata['pool_name'] &&
+          rbd.name === executingTask.metadata['image_name']
+        );
       });
       if (rbdExecuting) {
         if (executingTask.name === 'rbd/delete') {
           rbdExecuting.cdExecuting = 'deleting';
-
         } else if (executingTask.name === 'rbd/edit') {
           rbdExecuting.cdExecuting = 'updating';
-
         } else if (executingTask.name === 'rbd/flatten') {
           rbdExecuting.cdExecuting = 'flattening';
         }
-
       } else if (executingTask.name === 'rbd/create') {
         const rbdModel = new RbdModel();
         rbdModel.name = executingTask.metadata['image_name'];
         rbdModel.pool_name = executingTask.metadata['pool_name'];
         rbdModel.cdExecuting = 'creating';
         this.pushIfNotExists(resultRBDs, rbdModel);
-
       } else if (executingTask.name === 'rbd/clone') {
         const rbdModel = new RbdModel();
         rbdModel.name = executingTask.metadata['child_image_name'];
         rbdModel.pool_name = executingTask.metadata['child_pool_name'];
         rbdModel.cdExecuting = 'cloning';
         this.pushIfNotExists(resultRBDs, rbdModel);
-
       } else if (executingTask.name === 'rbd/copy') {
         const rbdModel = new RbdModel();
         rbdModel.name = executingTask.metadata['dest_image_name'];
@@ -255,32 +258,39 @@ export class RbdListComponent implements OnInit, OnDestroy {
   }
 
   flattenRbd(poolName, imageName) {
-    this.taskWrapper.wrapTaskAroundCall({
-      task: new FinishedTask('rbd/flatten', {
-        pool_name: poolName,
-        image_name: imageName
-      }),
-      tasks: this.executingTasks,
-      call: this.rbdService.flatten(poolName, imageName)
-    }).subscribe(
-      undefined,
-      undefined,
-      () => {
+    this.taskWrapper
+      .wrapTaskAroundCall({
+        task: new FinishedTask('rbd/flatten', {
+          pool_name: poolName,
+          image_name: imageName
+        }),
+        tasks: this.executingTasks,
+        call: this.rbdService.flatten(poolName, imageName)
+      })
+      .subscribe(undefined, undefined, () => {
         this.modalRef.hide();
         this.loadImages(null);
-      }
-    );
+      });
   }
 
   flattenRbdModal() {
     const poolName = this.selection.first().pool_name;
     const imageName = this.selection.first().name;
-    this.modalRef = this.modalService.show(FlattenConfirmationModalComponent);
     const parent: RbdParentModel = this.selection.first().parent;
-    this.modalRef.content.parent = `${parent.pool_name}/${parent.image_name}@${parent.snap_name}`;
-    this.modalRef.content.child = `${poolName}/${imageName}`;
-    this.modalRef.content.onSubmit.subscribe(() => {
-      this.flattenRbd(poolName, imageName);
-    });
+
+    const initialState = {
+      titleText: 'RBD flatten',
+      buttonText: 'Flatten',
+      bodyTpl: this.flattenTpl,
+      bodyData: {
+        parent: `${parent.pool_name}/${parent.image_name}@${parent.snap_name}`,
+        child: `${poolName}/${imageName}`
+      },
+      onSubmit: () => {
+        this.flattenRbd(poolName, imageName);
+      }
+    };
+
+    this.modalRef = this.modalService.show(ConfirmationModalComponent, { initialState });
   }
 }
index 603afdb0d32d72326c6005f7cc0c5280483da1a9..3f50579a371ef005fc6565a8e42b995deb400fd5 100644 (file)
@@ -4,54 +4,92 @@
           (updateSelection)="updateSelection($event)"
           [columns]="columns">
   <div class="table-actions">
-    <div class="btn-group" dropdown>
+    <div class="btn-group"
+         dropdown>
       <button type="button"
               class="btn btn-sm btn-primary"
               *ngIf="!selection.hasSingleSelection"
               (click)="openCreateSnapshotModal()">
-        <i class="fa fa-fw fa-plus"></i><span i18n>Create</span>
+        <i class="fa fa-fw fa-plus"></i>
+        <span i18n>Create</span>
       </button>
       <button type="button"
               class="btn btn-sm btn-primary"
               *ngIf="selection.hasSingleSelection"
               [ngClass]="{'disabled': selection.first().executing}"
               (click)="openEditSnapshotModal()">
-        <i class="fa fa-fw fa-pencil"></i><span i18n>Rename</span>
+        <i class="fa fa-fw fa-pencil"></i>
+        <span i18n>Rename</span>
       </button>
-      <button type="button" dropdownToggle class="btn btn-sm btn-primary dropdown-toggle dropdown-toggle-split">
+      <button type="button"
+              dropdownToggle
+              class="btn btn-sm btn-primary dropdown-toggle dropdown-toggle-split">
         <span class="caret"></span>
         <span class="sr-only"></span>
       </button>
-      <ul *dropdownMenu class="dropdown-menu" role="menu">
-        <li role="menuitem"><a class="dropdown-item" (click)="openCreateSnapshotModal()"><i class="fa fa-fw fa-plus"></i><span i18n>Create</span></a></li>
+      <ul *dropdownMenu
+          class="dropdown-menu"
+          role="menu">
+        <li role="menuitem">
+          <a class="dropdown-item"
+             (click)="openCreateSnapshotModal()">
+            <i class="fa fa-fw fa-plus"></i>
+            <span i18n>Create</span>
+          </a>
+        </li>
         <li role="menuitem"
-            [ngClass]="{'disabled': !selection.hasSingleSelection || selection.first().executing}"><a class="dropdown-item" (click)="openEditSnapshotModal()"><i class="fa fa-fw fa-pencil"></i><span i18n>Rename</span></a></li>
+            [ngClass]="{'disabled': !selection.hasSingleSelection || selection.first().executing}">
+          <a class="dropdown-item"
+             (click)="openEditSnapshotModal()">
+            <i class="fa fa-fw fa-pencil"></i>
+            <span i18n>Rename</span>
+          </a>
+        </li>
         <li role="menuitem"
             [ngClass]="{'disabled': !selection.hasSingleSelection || selection.first().executing}">
-          <a class="dropdown-item" (click)="toggleProtection()">
-            <span *ngIf="!selection.first()?.is_protected"><i class="fa fa-fw fa-lock"></i><span i18n>Protect</span></span>
-            <span *ngIf="selection.first()?.is_protected"><i class="fa fa-fw fa-unlock"></i><span i18n>Unprotect</span></span>
+          <a class="dropdown-item"
+             (click)="toggleProtection()">
+            <span *ngIf="!selection.first()?.is_protected">
+              <i class="fa fa-fw fa-lock"></i>
+              <span i18n>Protect</span>
+            </span>
+            <span *ngIf="selection.first()?.is_protected">
+              <i class="fa fa-fw fa-unlock"></i>
+              <span i18n>Unprotect</span>
+            </span>
           </a>
         </li>
         <li role="menuitem"
             [ngClass]="{'disabled': !selection.hasSingleSelection || selection.first().executing}">
-          <a class="dropdown-item" routerLink="/rbd/clone/{{ poolName }}/{{ rbdName }}/{{ selection.first()?.name }}">
-            <i class="fa fa-fw fa-clone"></i><span i18n>Clone</span>
+          <a class="dropdown-item"
+             routerLink="/rbd/clone/{{ poolName }}/{{ rbdName }}/{{ selection.first()?.name }}">
+            <i class="fa fa-fw fa-clone"></i>
+            <span i18n>Clone</span>
           </a>
         </li>
         <li role="menuitem"
             [ngClass]="{'disabled': !selection.hasSingleSelection || selection.first().executing}">
-          <a class="dropdown-item" routerLink="/rbd/copy/{{ poolName }}/{{ rbdName }}/{{ selection.first()?.name }}">
-            <i class="fa fa-fw fa-copy"></i><span i18n>Copy</span>
+          <a class="dropdown-item"
+             routerLink="/rbd/copy/{{ poolName }}/{{ rbdName }}/{{ selection.first()?.name }}">
+            <i class="fa fa-fw fa-copy"></i>
+            <span i18n>Copy</span>
           </a>
         </li>
         <li role="menuitem"
             [ngClass]="{'disabled': !selection.hasSingleSelection || selection.first().executing}">
-          <a class="dropdown-item" (click)="rollbackModal()"><i class="fa fa-fw fa-undo"></i><span i18n>Rollback</span></a>
+          <a class="dropdown-item"
+             (click)="rollbackModal()">
+            <i class="fa fa-fw fa-undo"></i>
+            <span i18n>Rollback</span>
+          </a>
         </li>
         <li role="menuitem"
             [ngClass]="{'disabled': !selection.hasSingleSelection || selection.first().executing || selection.first().is_protected}">
-          <a class="dropdown-item" (click)="deleteSnapshotModal()"><i class="fa fa-fw fa-trash-o"></i><span i18n>Delete</span></a>
+          <a class="dropdown-item"
+             (click)="deleteSnapshotModal()">
+            <i class="fa fa-fw fa-trash-o"></i>
+            <span i18n>Delete</span>
+          </a>
         </li>
       </ul>
     </div>
 
 <ng-template #protectTpl
              let-value="value">
-  <span *ngIf="value" class="label label-success">PROTECTED</span>
-  <span *ngIf="!value" class="label label-info">UNPROTECTED</span>
+  <span *ngIf="value"
+        class="label label-success">PROTECTED</span>
+  <span *ngIf="!value"
+        class="label label-info">UNPROTECTED</span>
+</ng-template>
+
+<ng-template #rollbackTpl
+             let-value>
+  You are about to rollback
+  <strong>{{ value.snapName }}</strong>.
 </ng-template>
index 2e3ecbaec5514aa80b63e245b449eda1cc5afe8f..aa3a2f1b614e414c64b39ac330e41485fb5e4360 100644 (file)
@@ -4,6 +4,7 @@ import * as _ from 'lodash';
 import { BsModalRef, BsModalService } from 'ngx-bootstrap';
 
 import { RbdService } from '../../../shared/api/rbd.service';
+import { ConfirmationModalComponent } from '../../../shared/components/confirmation-modal/confirmation-modal.component';
 import { DeletionModalComponent } from '../../../shared/components/deletion-modal/deletion-modal.component';
 import { CellTemplate } from '../../../shared/enum/cell-template.enum';
 import { CdTableColumn } from '../../../shared/models/cd-table-column';
@@ -15,7 +16,6 @@ import { DimlessBinaryPipe } from '../../../shared/pipes/dimless-binary.pipe';
 import { NotificationService } from '../../../shared/services/notification.service';
 import { TaskManagerService } from '../../../shared/services/task-manager.service';
 import { RbdSnapshotFormComponent } from '../rbd-snapshot-form/rbd-snapshot-form.component';
-import { RollbackConfirmationModalComponent } from '../rollback-confirmation-modal/rollback-confimation-modal.component';
 import { RbdSnapshotModel } from './rbd-snapshot.model';
 
 @Component({
@@ -31,6 +31,7 @@ export class RbdSnapshotListComponent implements OnInit, OnChanges {
 
   @ViewChild('nameTpl') nameTpl: TemplateRef<any>;
   @ViewChild('protectTpl') protectTpl: TemplateRef<any>;
+  @ViewChild('rollbackTpl') rollbackTpl: TemplateRef<any>;
 
   data: RbdSnapshotModel[];
 
@@ -209,11 +210,19 @@ export class RbdSnapshotListComponent implements OnInit, OnChanges {
 
   rollbackModal() {
     const snapshotName = this.selection.selected[0].name;
-    this.modalRef = this.modalService.show(RollbackConfirmationModalComponent);
-    this.modalRef.content.snapName = `${this.poolName}/${this.rbdName}@${snapshotName}`;
-    this.modalRef.content.onSubmit.subscribe((itemName: string) => {
-      this._asyncTask('rollbackSnapshot', 'rbd/snap/rollback', snapshotName);
-    });
+    const initialState = {
+      titleText: 'RBD snapshot rollback',
+      buttonText: 'Rollback',
+      bodyTpl: this.rollbackTpl,
+      bodyData: {
+        snapName: `${this.poolName}/${this.rbdName}@${snapshotName}`
+      },
+      onSubmit: () => {
+        this._asyncTask('rollbackSnapshot', 'rbd/snap/rollback', snapshotName);
+      }
+    };
+
+    this.modalRef = this.modalService.show(ConfirmationModalComponent, { initialState });
   }
 
   deleteSnapshotModal() {
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rollback-confirmation-modal/rollback-confimation-modal.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rollback-confirmation-modal/rollback-confimation-modal.component.html
deleted file mode 100644 (file)
index e1ef7d4..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-<div class="modal-header">
-  <h4 i18n
-      class="modal-title pull-left">RBD snapshot rollback</h4>
-  <button type="button" class="close pull-right" aria-label="Close" (click)="modalRef.hide()">
-    <span aria-hidden="true">&times;</span>
-  </button>
-</div>
-<form name="rollbackForm"
-      class="form-horizontal"
-      #formDir="ngForm"
-      [formGroup]="rollbackForm"
-      novalidate>
-  <div class="modal-body">
-    You are about to rollback <strong>{{ snapName }}</strong>.
-  </div>
-  <div class="modal-footer">
-    <div class="button-group text-right">
-      <cd-submit-button i18n
-                        [form]="rollbackForm"
-                        (submitAction)="submit()">
-        Rollback
-      </cd-submit-button>
-      <button i18n type="button" class="btn btn-sm btn-default" (click)="modalRef.hide()">Cancel</button>
-    </div>
-  </div>
-</form>
-
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rollback-confirmation-modal/rollback-confimation-modal.component.scss b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rollback-confirmation-modal/rollback-confimation-modal.component.scss
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rollback-confirmation-modal/rollback-confimation-modal.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rollback-confirmation-modal/rollback-confimation-modal.component.spec.ts
deleted file mode 100644 (file)
index 82e2924..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-import { HttpClientTestingModule } from '@angular/common/http/testing';
-import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { ReactiveFormsModule } from '@angular/forms';
-
-import { ToastModule } from 'ng2-toastr';
-import { BsModalRef, BsModalService } from 'ngx-bootstrap';
-
-import { ApiModule } from '../../../shared/api/api.module';
-import { ServicesModule } from '../../../shared/services/services.module';
-import { SharedModule } from '../../../shared/shared.module';
-import { configureTestBed } from '../../../shared/unit-test-helper';
-import { RollbackConfirmationModalComponent } from './rollback-confimation-modal.component';
-
-describe('RollbackConfirmationModalComponent', () => {
-  let component: RollbackConfirmationModalComponent;
-  let fixture: ComponentFixture<RollbackConfirmationModalComponent>;
-
-  configureTestBed({
-    imports: [
-      ReactiveFormsModule,
-      HttpClientTestingModule,
-      SharedModule,
-      ServicesModule,
-      ApiModule,
-      ToastModule.forRoot()
-    ],
-    declarations: [RollbackConfirmationModalComponent],
-    providers: [BsModalRef, BsModalService]
-  });
-
-  beforeEach(() => {
-    fixture = TestBed.createComponent(RollbackConfirmationModalComponent);
-    component = fixture.componentInstance;
-    fixture.detectChanges();
-  });
-
-  it('should create', () => {
-    expect(component).toBeTruthy();
-  });
-});
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rollback-confirmation-modal/rollback-confimation-modal.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rollback-confirmation-modal/rollback-confimation-modal.component.ts
deleted file mode 100644 (file)
index bef646f..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-import { FormGroup } from '@angular/forms';
-
-import { BsModalRef } from 'ngx-bootstrap';
-import { Subject } from 'rxjs';
-
-@Component({
-  selector: 'cd-rollback-confimation-modal',
-  templateUrl: './rollback-confimation-modal.component.html',
-  styleUrls: ['./rollback-confimation-modal.component.scss']
-})
-export class RollbackConfirmationModalComponent implements OnInit {
-
-  snapName: string;
-
-  rollbackForm: FormGroup;
-
-  public onSubmit: Subject<string>;
-
-  constructor(public modalRef: BsModalRef) {
-    this.createForm();
-  }
-
-  createForm() {
-    this.rollbackForm = new FormGroup({});
-  }
-
-  ngOnInit() {
-    this.onSubmit = new Subject();
-  }
-
-  submit() {
-    this.onSubmit.next(this.snapName);
-  }
-
-  stopLoadingSpinner() {
-    this.rollbackForm.setErrors({'cdSubmitButton': true});
-  }
-}
index ff6f46268e77d411ef13c802f575157fa5dd3406..8c6e87be16a6399ba65fa3f86d33a8eecc0be918 100644 (file)
@@ -6,6 +6,7 @@ import { ChartsModule } from 'ng2-charts/ng2-charts';
 import { AlertModule, ModalModule, PopoverModule, TooltipModule } from 'ngx-bootstrap';
 
 import { PipesModule } from '../pipes/pipes.module';
+import { ConfirmationModalComponent } from './confirmation-modal/confirmation-modal.component';
 import { DeletionModalComponent } from './deletion-modal/deletion-modal.component';
 import { ErrorPanelComponent } from './error-panel/error-panel.component';
 import { HelperComponent } from './helper/helper.component';
@@ -40,7 +41,8 @@ import { ViewCacheComponent } from './view-cache/view-cache.component';
     LoadingPanelComponent,
     InfoPanelComponent,
     ModalComponent,
-    DeletionModalComponent
+    DeletionModalComponent,
+    ConfirmationModalComponent
   ],
   providers: [],
   exports: [
@@ -56,7 +58,8 @@ import { ViewCacheComponent } from './view-cache/view-cache.component';
   ],
   entryComponents: [
     ModalComponent,
-    DeletionModalComponent
+    DeletionModalComponent,
+    ConfirmationModalComponent
   ]
 })
 export class ComponentsModule { }
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/confirmation-modal/confirmation-modal.component.html b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/confirmation-modal/confirmation-modal.component.html
new file mode 100644 (file)
index 0000000..c8a4901
--- /dev/null
@@ -0,0 +1,27 @@
+<cd-modal>
+  <ng-container class="modal-title">{{ titleText }}</ng-container>
+  <ng-container class="modal-content">
+    <form name="confirmationForm"
+          class="form-horizontal"
+          #formDir="ngForm"
+          [formGroup]="confirmationForm"
+          novalidate>
+      <div class="modal-body">
+        <ng-container *ngTemplateOutlet="bodyTpl; context: bodyContext"></ng-container>
+      </div>
+      <div class="modal-footer">
+        <div class="button-group text-right">
+          <cd-submit-button i18n
+                            [form]="confirmationForm"
+                            (submitAction)="submit()">
+            {{ buttonText }}
+          </cd-submit-button>
+          <button i18n
+                  type="button"
+                  class="btn btn-sm btn-default"
+                  (click)="modalRef.hide()">Cancel</button>
+        </div>
+      </div>
+    </form>
+  </ng-container>
+</cd-modal>
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/confirmation-modal/confirmation-modal.component.scss b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/confirmation-modal/confirmation-modal.component.scss
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/confirmation-modal/confirmation-modal.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/confirmation-modal/confirmation-modal.component.spec.ts
new file mode 100644 (file)
index 0000000..cb0ea16
--- /dev/null
@@ -0,0 +1,31 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { ReactiveFormsModule } from '@angular/forms';
+
+import { BsModalRef } from 'ngx-bootstrap';
+
+import { ModalComponent } from '../modal/modal.component';
+import { SubmitButtonComponent } from '../submit-button/submit-button.component';
+import { ConfirmationModalComponent } from './confirmation-modal.component';
+
+describe('ConfirmationModalComponent', () => {
+  let component: ConfirmationModalComponent;
+  let fixture: ComponentFixture<ConfirmationModalComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ConfirmationModalComponent, SubmitButtonComponent, ModalComponent],
+      imports: [ReactiveFormsModule],
+      providers: [BsModalRef]
+    }).compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(ConfirmationModalComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/confirmation-modal/confirmation-modal.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/confirmation-modal/confirmation-modal.component.ts
new file mode 100644 (file)
index 0000000..bbb155b
--- /dev/null
@@ -0,0 +1,38 @@
+import { Component, OnInit, TemplateRef } from '@angular/core';
+import { FormGroup } from '@angular/forms';
+
+import { BsModalRef } from 'ngx-bootstrap';
+
+@Component({
+  selector: 'cd-confirmation-modal',
+  templateUrl: './confirmation-modal.component.html',
+  styleUrls: ['./confirmation-modal.component.scss']
+})
+export class ConfirmationModalComponent implements OnInit {
+  bodyData: object;
+  bodyTpl: TemplateRef<any>;
+  buttonText: string;
+  onSubmit: Function;
+  titleText: string;
+
+  bodyContext: object;
+  confirmationForm: FormGroup;
+
+  constructor(public modalRef: BsModalRef) {
+    this.confirmationForm = new FormGroup({});
+  }
+
+  ngOnInit() {
+    this.bodyContext = {
+      $implicit: this.bodyData
+    };
+  }
+
+  submit() {
+    this.onSubmit();
+  }
+
+  stopLoadingSpinner() {
+    this.confirmationForm.setErrors({ cdSubmitButton: true });
+  }
+}