Commit 0a8bd5d2 by Torkel Ödegaard

ux: dashboard settings updated

parent cc046f03
///<reference path="../../headers/common.d.ts" />
import coreModule from 'app/core/core_module'; import coreModule from 'app/core/core_module';
var template = ` var template = `
......
...@@ -4,7 +4,7 @@ import './history/history'; ...@@ -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';
......
import coreModule from 'app/core/core_module'; import coreModule from 'app/core/core_module';
const template = ` const template = `
<h3 class="dashboard-settings__header">Save As</h3> <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>
<form name="ctrl.saveForm" ng-submit="ctrl.save()" novalidate> <a class="modal-header-close" ng-click="ctrl.dismiss();">
<div class="p-t-2"> <i class="fa fa-remove"></i>
<div class="gf-form"> </a>
<label class="gf-form-label width-6">New name</label> </div>
<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"> <form name="ctrl.saveForm" ng-submit="ctrl.save()" class="modal-content" novalidate>
<button type="submit" class="btn btn-success" ng-disabled="ctrl.saveForm.$invalid">Save As</button> <div class="p-t-2">
</div> <div class="gf-form">
</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 SaveDashboardAsCtrl { export class SaveDashboardAsModalCtrl {
clone: any; clone: any;
folderId: any; folderId: any;
dismiss: () => void; dismiss: () => void;
...@@ -71,11 +83,11 @@ export function saveDashboardAsDirective() { ...@@ -71,11 +83,11 @@ export function saveDashboardAsDirective() {
return { return {
restrict: 'E', restrict: 'E',
template: template, template: template,
controller: SaveDashboardAsCtrl, controller: SaveDashboardAsModalCtrl,
bindToController: true, bindToController: true,
controllerAs: 'ctrl', controllerAs: 'ctrl',
scope: {} scope: {dismiss: "&"}
}; };
} }
coreModule.directive('saveDashboardAs', saveDashboardAsDirective); coreModule.directive('saveDashboardAsModal', saveDashboardAsDirective);
...@@ -8,53 +8,65 @@ ...@@ -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"> <h3 class="dashboard-settings__header">
General General
</h3> </h3>
<div class="gf-form-group"> <div class="gf-form-group">
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label width-7">Name</label> <label class="gf-form-label width-7">Name</label>
<input type="text" class="gf-form-input width-30" ng-model='ctrl.dashboard.title'></input> <input type="text" class="gf-form-input width-30" ng-model='ctrl.dashboard.title'></input>
</div> </div>
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label width-7">Description</label> <label class="gf-form-label width-7">Description</label>
<input type="text" class="gf-form-input width-30" ng-model='ctrl.dashboard.description'></input> <input type="text" class="gf-form-input width-30" ng-model='ctrl.dashboard.description'></input>
</div> </div>
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label width-7"> <label class="gf-form-label width-7">
Tags Tags
<info-popover mode="right-normal">Press enter to add a tag</info-popover> <info-popover mode="right-normal">Press enter to add a tag</info-popover>
</label> </label>
<bootstrap-tagsinput ng-model="ctrl.dashboard.tags" tagclass="label label-tag" placeholder="add tags"> <bootstrap-tagsinput ng-model="ctrl.dashboard.tags" tagclass="label label-tag" placeholder="add tags">
</bootstrap-tagsinput> </bootstrap-tagsinput>
</div> </div>
<folder-picker initial-title="ctrl.dashboard.meta.folderTitle" <folder-picker initial-title="ctrl.dashboard.meta.folderTitle"
initial-folder-id="ctrl.dashboard.folderId" initial-folder-id="ctrl.dashboard.folderId"
on-change="ctrl.onFolderChange($folder)" on-change="ctrl.onFolderChange($folder)"
label-class="width-7"> label-class="width-7">
</folder-picker> </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 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> </gf-form-switch>
</div> </div>
<gf-time-picker-settings dashboard="ctrl.dashboard"></gf-time-picker-settings> <gf-time-picker-settings dashboard="ctrl.dashboard"></gf-time-picker-settings>
<h5 class="section-heading">Panel Options</h5> <h5 class="section-heading">Panel Options</h5>
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label width-11"> <label class="gf-form-label width-11">
Graph Tooltip Graph Tooltip
<info-popover mode="right-normal"> <info-popover mode="right-normal">
Cycle between options using Shortcut: CTRL+O or CMD+O Cycle between options using Shortcut: CTRL+O or CMD+O
</info-popover> </info-popover>
</label> </label>
<div class="gf-form-select-wrapper"> <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> <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> </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 @@ ...@@ -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> <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>
<div class="dashboard-settings__content" ng-if="ctrl.viewId === 'save_as'"> <div class="gf-form">
<save-dashboard-as></save-dashboard-as> <textarea class="gf-form-input" ng-model="ctrl.json" rows="30" spellcheck="false"></textarea>
</div> </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> </div>
<div class="dashboard-settings__content" ng-if="ctrl.viewId === '404'"> <div class="dashboard-settings__content" ng-if="ctrl.viewId === '404'">
......
...@@ -9,8 +9,8 @@ export class SettingsCtrl { ...@@ -9,8 +9,8 @@ export class SettingsCtrl {
viewId: string; viewId: string;
json: string; json: string;
alertCount: number; alertCount: number;
confirmValid: boolean; canSaveAs: boolean;
confirmText: string; canDelete: boolean;
sections: any[]; sections: any[];
/** @ngInject */ /** @ngInject */
...@@ -24,13 +24,11 @@ export class SettingsCtrl { ...@@ -24,13 +24,11 @@ export class SettingsCtrl {
this.$rootScope.$broadcast('refresh'); this.$rootScope.$broadcast('refresh');
}); });
this.alertCount = _.sumBy(this.dashboard.panels, panel => { this.canSaveAs = contextSrv.isEditor;
return panel.alert ? 1 : 0; 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 { ...@@ -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 { ...@@ -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 { ...@@ -79,12 +77,8 @@ export class SettingsCtrl {
} }
} }
onRouteUpdated() { openSaveAsModal() {
this.viewId = this.$location.search().editview; this.dashboardSrv.showSaveAsModal();
if (this.viewId) {
this.json = JSON.stringify(this.dashboard.getSaveModelClone(), null, 2);
}
} }
hideSettings() { hideSettings() {
...@@ -106,11 +100,34 @@ export class SettingsCtrl { ...@@ -106,11 +100,34 @@ export class SettingsCtrl {
}); });
} }
confirmTextChanged() { deleteDashboard() {
this.confirmValid = this.confirmText === 'DELETE'; 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('/');
......
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', () => { ...@@ -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,
......
...@@ -67,3 +67,15 @@ ...@@ -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;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment