mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
ux: dashboard settings updated
This commit is contained in:
parent
cc046f03aa
commit
0a8bd5d289
@ -1,5 +1,3 @@
|
||||
///<reference path="../../headers/common.d.ts" />
|
||||
|
||||
import coreModule from 'app/core/core_module';
|
||||
|
||||
var template = `
|
||||
|
@ -4,7 +4,7 @@ import './history/history';
|
||||
import './dashboardLoaderSrv';
|
||||
import './dashnav/dashnav';
|
||||
import './submenu/submenu';
|
||||
import './dashboard_save_as';
|
||||
import './save_as_modal';
|
||||
import './save_modal';
|
||||
import './shareModalCtrl';
|
||||
import './shareSnapshotCtrl';
|
||||
|
@ -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
public/app/features/dashboard/save_as_modal.ts
Normal file
93
public/app/features/dashboard/save_as_modal.ts
Normal file
@ -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);
|
@ -8,53 +8,65 @@
|
||||
<i class="{{::section.icon}}"></i>
|
||||
{{::section.title}}
|
||||
</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>
|
||||
|
||||
<div class="dashboard-settings__content" ng-if="ctrl.viewId === 'settings'">
|
||||
<h3 class="dashboard-settings__header">
|
||||
General
|
||||
</h3>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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 class="dashboard-settings__content" ng-if="ctrl.viewId === 'annotations'" ng-include="'public/app/features/annotations/partials/editor.html'">
|
||||
@ -72,29 +84,11 @@
|
||||
</div>
|
||||
|
||||
<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'">
|
||||
<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 class="dashboard-settings__content" ng-if="ctrl.viewId === '404'">
|
||||
|
@ -9,8 +9,8 @@ export class SettingsCtrl {
|
||||
viewId: string;
|
||||
json: string;
|
||||
alertCount: number;
|
||||
confirmValid: boolean;
|
||||
confirmText: string;
|
||||
canSaveAs: boolean;
|
||||
canDelete: boolean;
|
||||
sections: any[];
|
||||
|
||||
/** @ngInject */
|
||||
@ -24,13 +24,11 @@ export class SettingsCtrl {
|
||||
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.onRouteUpdated();
|
||||
|
||||
$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' });
|
||||
|
||||
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 url = this.$location.path();
|
||||
|
||||
@ -70,6 +60,14 @@ export class SettingsCtrl {
|
||||
const sectionParams = _.defaults({ editview: section.id }, params);
|
||||
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 });
|
||||
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() {
|
||||
@ -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(() => {
|
||||
appEvents.emit('alert-success', ['Dashboard Deleted', this.dashboard.title + ' has been deleted']);
|
||||
this.$location.url('/');
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { SaveDashboardAsCtrl } from '../dashboard_save_as';
|
||||
import { SaveDashboardAsModalCtrl } from '../save_as_modal;
|
||||
import { describe, it, expect } from 'test/lib/common';
|
||||
|
||||
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 = {
|
||||
clone: ctrl.clone,
|
||||
ctrl: ctrl,
|
@ -67,3 +67,15 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user