Browse Source

ux: dashboard settings updated

Torkel Ödegaard 8 years ago
parent
commit
0a8bd5d289

+ 0 - 2
public/app/core/components/dashboard_selector.ts

@@ -1,5 +1,3 @@
-///<reference path="../../headers/common.d.ts" />
-
 import coreModule from 'app/core/core_module';
 import coreModule from 'app/core/core_module';
 
 
 var template = `
 var template = `

+ 1 - 1
public/app/features/dashboard/all.ts

@@ -4,7 +4,7 @@ import './history/history';
 import './dashboardLoaderSrv';
 import './dashboardLoaderSrv';
 import './dashnav/dashnav';
 import './dashnav/dashnav';
 import './submenu/submenu';
 import './submenu/submenu';
-import './dashboard_save_as';
+import './save_as_modal';
 import './save_modal';
 import './save_modal';
 import './shareModalCtrl';
 import './shareModalCtrl';
 import './shareSnapshotCtrl';
 import './shareSnapshotCtrl';

+ 0 - 81
public/app/features/dashboard/dashboard_save_as.ts

@@ -1,81 +0,0 @@
-import coreModule from 'app/core/core_module';
-
-const  template = `
-<h3 class="dashboard-settings__header">Save As</h3>
-
-<form name="ctrl.saveForm" ng-submit="ctrl.save()" novalidate>
-  <div class="p-t-2">
-    <div class="gf-form">
-      <label class="gf-form-label width-6">New name</label>
-      <input type="text" class="gf-form-input max-width-25" ng-model="ctrl.clone.title" give-focus="true" required>
-    </div>
-    <div class="gf-form">
-      <folder-picker initial-folder-id="ctrl.folderId"
-                      on-change="ctrl.onFolderChange($folder)"
-                      label-class="width-6">
-      </folder-picker>
-    </div>
-  </div>
-
-  <div class="gf-form-button-row">
-    <button type="submit" class="btn btn-success" ng-disabled="ctrl.saveForm.$invalid">Save As</button>
-  </div>
-</form>
-`;
-
-export class SaveDashboardAsCtrl {
-  clone: any;
-  folderId: any;
-  dismiss: () => void;
-
-  /** @ngInject */
-  constructor(private dashboardSrv) {
-    var dashboard = this.dashboardSrv.getCurrent();
-    this.clone = dashboard.getSaveModelClone();
-    this.clone.id = null;
-    this.clone.title += ' Copy';
-    this.clone.editable = true;
-    this.clone.hideControls = false;
-    this.folderId = dashboard.folderId;
-
-    // remove alerts if source dashboard is already persisted
-    // do not want to create alert dupes
-    if (dashboard.id > 0) {
-      this.clone.panels.forEach(panel => {
-        if (panel.type === "graph" && panel.alert) {
-          delete panel.thresholds;
-        }
-        delete panel.alert;
-      });
-    }
-
-    delete this.clone.autoUpdate;
-  }
-
-  save() {
-    return this.dashboardSrv.save(this.clone).then(this.dismiss);
-  }
-
-  keyDown(evt) {
-    if (evt.keyCode === 13) {
-      this.save();
-    }
-  }
-
-  onFolderChange(folder) {
-    this.clone.folderId = folder.id;
-  }
-}
-
-export function saveDashboardAsDirective() {
-  return {
-    restrict: 'E',
-    template: template,
-    controller: SaveDashboardAsCtrl,
-    bindToController: true,
-    controllerAs: 'ctrl',
-    scope: {}
-  };
-}
-
-coreModule.directive('saveDashboardAs',  saveDashboardAsDirective);

+ 93 - 0
public/app/features/dashboard/save_as_modal.ts

@@ -0,0 +1,93 @@
+import coreModule from 'app/core/core_module';
+
+const  template = `
+<div class="modal-body">
+	<div class="modal-header">
+		<h2 class="modal-header-title">
+			<i class="fa fa-copy"></i>
+			<span class="p-l-1">Save As...</span>
+		</h2>
+
+		<a class="modal-header-close" ng-click="ctrl.dismiss();">
+			<i class="fa fa-remove"></i>
+		</a>
+	</div>
+
+	<form name="ctrl.saveForm" ng-submit="ctrl.save()" class="modal-content" novalidate>
+		<div class="p-t-2">
+			<div class="gf-form">
+				<label class="gf-form-label width-7">New name</label>
+				<input type="text" class="gf-form-input" ng-model="ctrl.clone.title" give-focus="true" required>
+			</div>
+      <div class="gf-form">
+        <folder-picker initial-folder-id="ctrl.folderId"
+                       on-change="ctrl.onFolderChange($folder)"
+                       label-class="width-7">
+        </folder-picker>
+      </div>
+		</div>
+
+		<div class="gf-form-button-row text-center">
+			<button type="submit" class="btn btn-success" ng-disabled="ctrl.saveForm.$invalid">Save</button>
+			<a class="btn-text" ng-click="ctrl.dismiss();">Cancel</a>
+		</div>
+	</form>
+</div>
+`;
+
+export class SaveDashboardAsModalCtrl {
+  clone: any;
+  folderId: any;
+  dismiss: () => void;
+
+  /** @ngInject */
+  constructor(private dashboardSrv) {
+    var dashboard = this.dashboardSrv.getCurrent();
+    this.clone = dashboard.getSaveModelClone();
+    this.clone.id = null;
+    this.clone.title += ' Copy';
+    this.clone.editable = true;
+    this.clone.hideControls = false;
+    this.folderId = dashboard.folderId;
+
+    // remove alerts if source dashboard is already persisted
+    // do not want to create alert dupes
+    if (dashboard.id > 0) {
+      this.clone.panels.forEach(panel => {
+        if (panel.type === "graph" && panel.alert) {
+          delete panel.thresholds;
+        }
+        delete panel.alert;
+      });
+    }
+
+    delete this.clone.autoUpdate;
+  }
+
+  save() {
+    return this.dashboardSrv.save(this.clone).then(this.dismiss);
+  }
+
+  keyDown(evt) {
+    if (evt.keyCode === 13) {
+      this.save();
+    }
+  }
+
+  onFolderChange(folder) {
+    this.clone.folderId = folder.id;
+  }
+}
+
+export function saveDashboardAsDirective() {
+  return {
+    restrict: 'E',
+    template: template,
+    controller: SaveDashboardAsModalCtrl,
+    bindToController: true,
+    controllerAs: 'ctrl',
+    scope: {dismiss: "&"}
+  };
+}
+
+coreModule.directive('saveDashboardAsModal',  saveDashboardAsDirective);

+ 60 - 66
public/app/features/dashboard/settings/settings.html

@@ -8,53 +8,65 @@
     <i class="{{::section.icon}}"></i>
     <i class="{{::section.icon}}"></i>
 		{{::section.title}}
 		{{::section.title}}
 	</a>
 	</a>
+
+	<div class="dashboard-settings__aside-actions">
+		<button class="btn btn-inverse" ng-click="ctrl.openSaveAsModal()" ng-show="ctrl.canSaveAs">
+			<i class="fa fa-copy"></i>
+			Save As...
+		</button>
+
+		<button class="btn btn-danger" ng-click="ctrl.deleteDashboard()" ng-show="ctrl.canDelete">
+			<i class="fa fa-trash"></i>
+			Delete
+		</button>
+	</div>
 </aside>
 </aside>
 
 
 <div class="dashboard-settings__content" ng-if="ctrl.viewId === 'settings'">
 <div class="dashboard-settings__content" ng-if="ctrl.viewId === 'settings'">
-  <h3 class="dashboard-settings__header">
-    General
-  </h3>
-
-  <div class="gf-form-group">
-    <div class="gf-form">
-      <label class="gf-form-label width-7">Name</label>
-      <input type="text" class="gf-form-input width-30" ng-model='ctrl.dashboard.title'></input>
-    </div>
-    <div class="gf-form">
-      <label class="gf-form-label width-7">Description</label>
-      <input type="text" class="gf-form-input width-30" ng-model='ctrl.dashboard.description'></input>
-    </div>
-    <div class="gf-form">
-      <label class="gf-form-label width-7">
-        Tags
-        <info-popover mode="right-normal">Press enter to add a tag</info-popover>
-      </label>
-      <bootstrap-tagsinput ng-model="ctrl.dashboard.tags" tagclass="label label-tag" placeholder="add tags">
-      </bootstrap-tagsinput>
-    </div>
-    <folder-picker initial-title="ctrl.dashboard.meta.folderTitle"
-                   initial-folder-id="ctrl.dashboard.folderId"
-                   on-change="ctrl.onFolderChange($folder)"
-                   label-class="width-7">
-    </folder-picker>
-    <gf-form-switch class="gf-form" label="Editable" tooltip="Uncheck, then save and reload to disable all dashboard editing" checked="ctrl.dashboard.editable" label-class="width-7">
-    </gf-form-switch>
-  </div>
-
-  <gf-time-picker-settings dashboard="ctrl.dashboard"></gf-time-picker-settings>
-
-  <h5 class="section-heading">Panel Options</h5>
-  <div class="gf-form">
-    <label class="gf-form-label width-11">
-      Graph Tooltip
-      <info-popover mode="right-normal">
-        Cycle between options using Shortcut: CTRL+O or CMD+O
-      </info-popover>
-    </label>
-    <div class="gf-form-select-wrapper">
-      <select ng-model="ctrl.dashboard.graphTooltip" class='gf-form-input' ng-options="f.value as f.text for f in [{value: 0, text: 'Default'}, {value: 1, text: 'Shared crosshair'},{value: 2, text: 'Shared Tooltip'}]"></select>
-    </div>
-  </div>
+	<h3 class="dashboard-settings__header">
+		General
+	</h3>
+
+	<div class="gf-form-group">
+		<div class="gf-form">
+			<label class="gf-form-label width-7">Name</label>
+			<input type="text" class="gf-form-input width-30" ng-model='ctrl.dashboard.title'></input>
+		</div>
+		<div class="gf-form">
+			<label class="gf-form-label width-7">Description</label>
+			<input type="text" class="gf-form-input width-30" ng-model='ctrl.dashboard.description'></input>
+		</div>
+		<div class="gf-form">
+			<label class="gf-form-label width-7">
+				Tags
+				<info-popover mode="right-normal">Press enter to add a tag</info-popover>
+			</label>
+			<bootstrap-tagsinput ng-model="ctrl.dashboard.tags" tagclass="label label-tag" placeholder="add tags">
+			</bootstrap-tagsinput>
+		</div>
+		<folder-picker initial-title="ctrl.dashboard.meta.folderTitle"
+								 initial-folder-id="ctrl.dashboard.folderId"
+				 on-change="ctrl.onFolderChange($folder)"
+		 label-class="width-7">
+		</folder-picker>
+		<gf-form-switch class="gf-form" label="Editable" tooltip="Uncheck, then save and reload to disable all dashboard editing" checked="ctrl.dashboard.editable" label-class="width-7">
+		</gf-form-switch>
+	</div>
+
+	<gf-time-picker-settings dashboard="ctrl.dashboard"></gf-time-picker-settings>
+
+	<h5 class="section-heading">Panel Options</h5>
+	<div class="gf-form">
+		<label class="gf-form-label width-11">
+			Graph Tooltip
+			<info-popover mode="right-normal">
+				Cycle between options using Shortcut: CTRL+O or CMD+O
+			</info-popover>
+		</label>
+		<div class="gf-form-select-wrapper">
+			<select ng-model="ctrl.dashboard.graphTooltip" class='gf-form-input' ng-options="f.value as f.text for f in [{value: 0, text: 'Default'}, {value: 1, text: 'Shared crosshair'},{value: 2, text: 'Shared Tooltip'}]"></select>
+		</div>
+	</div>
 </div>
 </div>
 
 
 <div class="dashboard-settings__content" ng-if="ctrl.viewId === 'annotations'" ng-include="'public/app/features/annotations/partials/editor.html'">
 <div class="dashboard-settings__content" ng-if="ctrl.viewId === 'annotations'" ng-include="'public/app/features/annotations/partials/editor.html'">
@@ -72,29 +84,11 @@
 </div>
 </div>
 
 
 <div class="dashboard-settings__content" ng-if="ctrl.viewId === 'view_json'" >
 <div class="dashboard-settings__content" ng-if="ctrl.viewId === 'view_json'" >
-  <h3 class="dashboard-settings__header">View JSON</h3>
-
-  <div class="gf-form">
-    <textarea class="gf-form-input" ng-model="ctrl.json" rows="30" spellcheck="false"></textarea>
-  </div>
-</div>
+	<h3 class="dashboard-settings__header">View JSON</h3>
 
 
-<div class="dashboard-settings__content" ng-if="ctrl.viewId === 'save_as'">
-  <save-dashboard-as></save-dashboard-as>
-</div>
-
-<div class="dashboard-settings__content" ng-if="ctrl.viewId === 'delete'">
-  <h3 class="dashboard-settings__header">Delete dashboard</h3>
-
-  <div class="p-b-2" ng-if="ctrl.alertCount > 1">
-    <h5>This dashboard contains {{ctrl.alertCount}} alerts. Deleting this dashboard will also delete those alerts</h5>
-    <input type="text" class="gf-form-input width-16" style="display: inline-block;" placeholder="Type DELETE to confirm" ng-model="ctrl.confirmText" ng-change="ctrl.confirmTextChanged()">
-  </div>
-
-  <button class="btn btn-danger" ng-click="ctrl.deleteDashboard()" ng-disabled="!ctrl.confirmValid" >
-    <i class="fa fa-trash"></i>
-    Delete
-  </button>
+	<div class="gf-form">
+		<textarea class="gf-form-input" ng-model="ctrl.json" rows="30" spellcheck="false"></textarea>
+	</div>
 </div>
 </div>
 
 
 <div class="dashboard-settings__content" ng-if="ctrl.viewId === '404'">
 <div class="dashboard-settings__content" ng-if="ctrl.viewId === '404'">

+ 41 - 24
public/app/features/dashboard/settings/settings.ts

@@ -9,8 +9,8 @@ export class SettingsCtrl {
   viewId: string;
   viewId: string;
   json: string;
   json: string;
   alertCount: number;
   alertCount: number;
-  confirmValid: boolean;
-  confirmText: string;
+  canSaveAs: boolean;
+  canDelete: boolean;
   sections: any[];
   sections: any[];
 
 
   /** @ngInject */
   /** @ngInject */
@@ -24,13 +24,11 @@ export class SettingsCtrl {
       this.$rootScope.$broadcast('refresh');
       this.$rootScope.$broadcast('refresh');
     });
     });
 
 
-    this.alertCount = _.sumBy(this.dashboard.panels, panel => {
-      return panel.alert ? 1 : 0;
-    });
+    this.canSaveAs = contextSrv.isEditor;
+    this.canDelete = this.dashboard.meta.canSave;
 
 
-    this.confirmValid = this.alertCount === 0;
-    this.onRouteUpdated();
     this.buildSectionList();
     this.buildSectionList();
+    this.onRouteUpdated();
 
 
     $rootScope.onAppEvent('$routeUpdate', this.onRouteUpdated.bind(this), $scope);
     $rootScope.onAppEvent('$routeUpdate', this.onRouteUpdated.bind(this), $scope);
   }
   }
@@ -55,14 +53,6 @@ export class SettingsCtrl {
 
 
     this.sections.push({ title: 'View JSON', id: 'view_json', icon: 'fa fa-fw fa-code' });
     this.sections.push({ title: 'View JSON', id: 'view_json', icon: 'fa fa-fw fa-code' });
 
 
-    if (contextSrv.isEditor) {
-      this.sections.push({ title: 'Save As', id: 'save_as', icon: 'fa fa-fw fa-copy' });
-    }
-
-    if (this.dashboard.meta.canSave) {
-      this.sections.push({ title: 'Delete', id: 'delete', icon: 'fa fa-fw fa-trash' });
-    }
-
     const params = this.$location.search();
     const params = this.$location.search();
     const url = this.$location.path();
     const url = this.$location.path();
 
 
@@ -70,6 +60,14 @@ export class SettingsCtrl {
       const sectionParams = _.defaults({ editview: section.id }, params);
       const sectionParams = _.defaults({ editview: section.id }, params);
       section.url = url + '?' + $.param(sectionParams);
       section.url = url + '?' + $.param(sectionParams);
     }
     }
+  }
+
+  onRouteUpdated() {
+    this.viewId = this.$location.search().editview;
+
+    if (this.viewId) {
+      this.json = JSON.stringify(this.dashboard.getSaveModelClone(), null, 2);
+    }
 
 
     const currentSection = _.find(this.sections, { id: this.viewId });
     const currentSection = _.find(this.sections, { id: this.viewId });
     if (!currentSection) {
     if (!currentSection) {
@@ -79,12 +77,8 @@ export class SettingsCtrl {
     }
     }
   }
   }
 
 
-  onRouteUpdated() {
-    this.viewId = this.$location.search().editview;
-
-    if (this.viewId) {
-      this.json = JSON.stringify(this.dashboard.getSaveModelClone(), null, 2);
-    }
+  openSaveAsModal() {
+    this.dashboardSrv.showSaveAsModal();
   }
   }
 
 
   hideSettings() {
   hideSettings() {
@@ -106,11 +100,34 @@ export class SettingsCtrl {
     });
     });
   }
   }
 
 
-  confirmTextChanged() {
-    this.confirmValid = this.confirmText === 'DELETE';
+  deleteDashboard() {
+    var confirmText = '';
+    var text2 = this.dashboard.title;
+
+    const alerts = _.sumBy(this.dashboard.panels, panel => {
+      return panel.alert ? 1 : 0;
+    });
+
+    if (alerts > 0) {
+      confirmText = 'DELETE';
+      text2 = `This dashboard contains ${alerts} alerts. Deleting this dashboard will also delete those alerts`;
+    }
+
+    appEvents.emit('confirm-modal', {
+      title: 'Delete',
+      text: 'Do you want to delete this dashboard?',
+      text2: text2,
+      icon: 'fa-trash',
+      confirmText: confirmText,
+      yesText: 'Delete',
+      onConfirm: () => {
+        this.dashboard.meta.canSave = false;
+        this.deleteDashboardConfirmed();
+      }
+    });
   }
   }
 
 
-  deleteDashboard() {
+  deleteDashboardConfirmed() {
     this.backendSrv.delete('/api/dashboards/db/' + this.dashboard.meta.slug).then(() => {
     this.backendSrv.delete('/api/dashboards/db/' + this.dashboard.meta.slug).then(() => {
       appEvents.emit('alert-success', ['Dashboard Deleted', this.dashboard.title + ' has been deleted']);
       appEvents.emit('alert-success', ['Dashboard Deleted', this.dashboard.title + ' has been deleted']);
       this.$location.url('/');
       this.$location.url('/');

+ 2 - 2
public/app/features/dashboard/specs/dashboard_save_as.jest.ts → public/app/features/dashboard/specs/save_as_modal.jest.ts

@@ -1,4 +1,4 @@
-import { SaveDashboardAsCtrl } from '../dashboard_save_as';
+import { SaveDashboardAsModalCtrl } from '../save_as_modal;
 import { describe, it, expect } from 'test/lib/common';
 import { describe, it, expect } from 'test/lib/common';
 
 
 describe('saving dashboard as', () => {
 describe('saving dashboard as', () => {
@@ -21,7 +21,7 @@ describe('saving dashboard as', () => {
         },
         },
       };
       };
 
 
-      var ctrl = new SaveDashboardAsCtrl(mockDashboardSrv);
+      var ctrl = new SaveDashboardAsModalCtrl(mockDashboardSrv);
       var ctx: any = {
       var ctx: any = {
         clone: ctrl.clone,
         clone: ctrl.clone,
         ctrl: ctrl,
         ctrl: ctrl,

+ 12 - 0
public/sass/components/_dashboard_settings.scss

@@ -67,3 +67,15 @@
     padding-right: 5px;
     padding-right: 5px;
   }
   }
 }
 }
+
+.dashboard-settings__aside-actions {
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+  flex-grow: 1;
+  margin: $spacer*3 $spacer*2 0 0;
+
+  button {
+    margin-bottom: 10px;
+  }
+}