ux: popover forms

This commit is contained in:
Torkel Ödegaard 2017-04-14 10:18:49 +02:00
commit 2fce88ee62
14 changed files with 176 additions and 146 deletions

View File

@ -0,0 +1,26 @@
package migrations
import (
. "github.com/grafana/grafana/pkg/services/sqlstore/migrator"
)
func addAnnotationCategoryMig(mg *Migrator) {
category := Table{
Name: "annotation_category",
Columns: []*Column{
{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
{Name: "org_id", Type: DB_BigInt, Nullable: false},
{Name: "user_id", Type: DB_BigInt, Nullable: true},
{Name: "name", Type: DB_Text, Nullable: false},
},
Indices: []*Index{
{Cols: []string{"org_id", "name"}, Type: IndexType},
},
}
// create table
mg.AddMigration("create annotation_category table", NewAddTableMigration(category))
// create indices
mg.AddMigration("add index org_id & name", NewAddIndexMigration(category, category.Indices[0]))
}

View File

@ -26,6 +26,7 @@ func AddMigrations(mg *Migrator) {
addAnnotationMig(mg)
addStatsMigrations(mg)
addTestDataMigrations(mg)
// addAnnotationCategoryMig(mg)
}
func addMigrationLogMigrations(mg *Migrator) {

View File

@ -1,7 +1,7 @@
define([
'./panellinks/module',
'./dashlinks/module',
'./annotations/annotations_srv',
'./annotations/all',
'./templating/all',
'./dashboard/all',
'./playlist/all',

View File

@ -0,0 +1,8 @@
import {AnnotationsSrv} from './annotations_srv';
import {eventEditor} from './event_editor';
export {
AnnotationsSrv,
eventEditor
};

View File

@ -60,7 +60,7 @@ export class AnnotationsSrv {
var panel = options.panel;
var dashboard = options.dashboard;
if (panel && panel.alert) {
if (panel) {
return this.backendSrv.get('/api/annotations', {
from: options.range.from.valueOf(),
to: options.range.to.valueOf(),
@ -133,10 +133,8 @@ export class AnnotationsSrv {
return this.globalAnnotationsPromise;
}
postAnnotation(annotations) {
return Promise.all(_.map(annotations, annotation => {
return this.backendSrv.post('/api/annotations', annotation);
}));
postAnnotation(annotation) {
return this.backendSrv.post('/api/annotations', annotation);
}
translateQueryResult(annotation, results) {

View File

@ -0,0 +1,60 @@
///<reference path="../../headers/common.d.ts" />
import _ from 'lodash';
import moment from 'moment';
import coreModule from 'app/core/core_module';
import {MetricsPanelCtrl} from 'app/plugins/sdk';
export class AnnotationItem {
dashboardId: number;
panelId: number;
time: Date;
timeEnd: Date;
isRegion: boolean;
title: string;
text: string;
}
export class EventEditorCtrl {
panelCtrl: MetricsPanelCtrl;
timeFormat = 'YYYY-MM-DD HH:mm:ss';
annotation: AnnotationItem;
timeRange: {from: number, to: number};
form: any;
/** @ngInject **/
constructor() {
this.annotation = new AnnotationItem();
this.annotation.panelId = this.panelCtrl.panel.id;
this.annotation.dashboardId = this.panelCtrl.dashboard.id;
this.annotation.text = "hello";
this.annotation.time = moment(this.timeRange.from);
if (this.timeRange.to) {
this.annotation.timeEnd = moment(this.timeRange.to);
this.annotation.isRegion = true;
}
}
save() {
if (!this.form.$valid) {
return;
}
}
}
export function eventEditor() {
return {
restrict: 'E',
controller: EventEditorCtrl,
bindToController: true,
controllerAs: 'ctrl',
templateUrl: 'public/app/features/annotations/partials/event_editor.html',
scope: {
"panelCtrl": "=",
"timeRange": "="
}
};
}
coreModule.directive('eventEditor', eventEditor);

View File

@ -0,0 +1,39 @@
<h5 class="section-heading text-center">Add annotation</h5>
<form name="ctrl.form" class="text-center">
<div class="gf-form-group" style="display: inline-block">
<div class="gf-form">
<span class="gf-form-label width-7">Title</span>
<input type="text" ng-model="ctrl.annotation.title" class="gf-form-input max-width-20" required>
</div>
<!-- single event -->
<div ng-if="!ctrl.annotation.isRegion">
<div class="gf-form-inline">
<div class="gf-form">
<span class="gf-form-label width-7">Time</span>
<input type="text" ng-model="ctrl.annotation.time" class="gf-form-input max-width-20" input-datetime required>
</div>
</div>
</div>
<!-- region event -->
<div ng-if="ctrl.annotation.isRegion">
<div class="gf-form">
<span class="gf-form-label width-7">Start</span>
<input type="text" ng-model="ctrl.annotation.time" class="gf-form-input max-width-20" input-datetime required>
</div>
<div class="gf-form">
<span class="gf-form-label width-7">End</span>
<input type="text" ng-model="ctrl.annotation.timeEnd" class="gf-form-input max-width-20" input-datetime required>
</div>
</div>
<div class="gf-form gf-form--v-stretch">
<span class="gf-form-label width-7">Description</span>
<textarea class="gf-form-input width-20" rows="3" ng-model="ctrl.annotation.text" placeholder="Event description"></textarea>
</div>
<div class="gf-form-button-row">
<button type="submit" class="btn gf-form-btn btn-success" ng-click="ctrl.save()">Save</button>
</div>
</div>
</form>

View File

@ -4,12 +4,8 @@ import angular from 'angular';
import moment from 'moment';
export class AddAnnotationModalCtrl {
annotationTimeFormat = 'YYYY-MM-DD HH:mm:ss';
annotationTimeFrom: any;
annotationTimeTo: any = null;
annotationTitle: string;
annotationTextFrom: string;
annotationTextTo: string;
timeFormat = 'YYYY-MM-DD HH:mm:ss';
annotation: any;
graphCtrl: any;
/** @ngInject */
@ -17,41 +13,31 @@ export class AddAnnotationModalCtrl {
this.graphCtrl = $scope.ctrl;
$scope.ctrl = this;
this.annotationTimeFrom = moment($scope.annotationTimeRange.from).format(this.annotationTimeFormat);
let dashboardId = this.graphCtrl.dashboard.id;
let panelId = this.graphCtrl.panel.id;
this.annotation = {
dashboardId: dashboardId,
panelId: panelId,
time: null,
timeTo: null,
title: "",
text: ""
};
this.annotation.time = moment($scope.annotationTimeRange.from).format(this.timeFormat);0
if ($scope.annotationTimeRange.to) {
this.annotationTimeTo = moment($scope.annotationTimeRange.to).format(this.annotationTimeFormat);
this.annotation.timeTo = moment($scope.annotationTimeRange.to).format(this.timeFormat);
}
}
addAnnotation() {
let dashboardId = this.graphCtrl.dashboard.id;
let panelId = this.graphCtrl.panel.id;
let timeFrom = moment(this.annotationTimeFrom, this.annotationTimeFormat).valueOf();
let annotationFrom = {
dashboardId: dashboardId,
panelId: panelId,
time: timeFrom,
title: this.annotationTitle,
text: this.annotationTextFrom
};
let annotations = [annotationFrom];
if (this.annotationTimeTo) {
let timeTo = moment(this.annotationTimeTo, this.annotationTimeFormat).valueOf();
let annotationTo = {
dashboardId: dashboardId,
panelId: panelId,
time: timeTo,
title: this.annotationTitle,
text: this.annotationTextTo
};
annotations.push(annotationTo);
this.annotation.time = moment(this.annotation.time, this.timeFormat).valueOf();
if (this.annotation.timeTo) {
this.annotation.timeTo = moment(this.annotation.timeTo, this.timeFormat).valueOf();
}
this.graphCtrl.pushAnnotations(annotations)
this.graphCtrl.pushAnnotation(this.annotation)
.then(response => {
console.log(response);
this.close();
})
.catch(error => {

View File

@ -22,5 +22,4 @@ define([
'./ad_hoc_filters',
'./row/row_ctrl',
'./repeat_option/repeat_option',
'./event_editor',
], function () {});

View File

@ -1,22 +0,0 @@
///<reference path="../../headers/common.d.ts" />
import _ from 'lodash';
import coreModule from 'app/core/core_module';
export class EventEditorCtrl {
/** @ngInject */
constructor() {
}
}
export function eventEditor() {
return {
restrict: 'E',
controller: EventEditorCtrl,
bindToController: true,
controllerAs: 'ctrl',
templateUrl: 'public/app/features/dashboard/partials/event_editor.html',
};
}
coreModule.directive('eventEditor', eventEditor);

View File

@ -28,38 +28,25 @@
<div class="gf-form">
<span class="gf-form-label width-8">Title</span>
<input type="text" ng-model="ctrl.annotationTitle" class="gf-form-input max-width-20">
<input type="text" ng-model="ctrl.annotation.title" class="gf-form-input max-width-20">
</div>
<div class="gf-form">
<span class="gf-form-label width-8" ng-if="!ctrl.annotationTimeTo">Time</span>
<span class="gf-form-label width-8" ng-if="ctrl.annotationTimeTo">Time Start</span>
<input type="text" ng-model="ctrl.annotationTimeFrom" class="gf-form-input max-width-20">
<span class="gf-form-label width-8" ng-if="!ctrl.annotation.timeTo">Time</span>
<span class="gf-form-label width-8" ng-if="ctrl.annotation.timeTo">Time Start</span>
<input type="text" ng-model="ctrl.annotation.time" class="gf-form-input max-width-20">
</div>
<div class="gf-form" ng-if="ctrl.annotationTimeTo">
<div class="gf-form" ng-if="ctrl.annotation.timeTo">
<span class="gf-form-label width-8">Time Stop</span>
<input type="text" ng-model="ctrl.annotationTimeTo" class="gf-form-input max-width-20">
<input type="text" ng-model="ctrl.annotation.timeTo" class="gf-form-input max-width-20">
</div>
</div>
<div>
<h6 ng-if="!ctrl.annotationTimeTo">Description</h6>
<h6 ng-if="ctrl.annotationTimeTo">Description Start</h6>
<h6>Description</h6>
</div>
<div class="gf-form-group share-modal-options">
<div class="gf-form">
<textarea rows="3" class="gf-form-input width-27" ng-model="ctrl.annotationTextFrom"></textarea>
</div>
</div>
<div ng-if="ctrl.annotationTimeTo">
<div>
<h6>Description Stop</h6>
</div>
<div class="gf-form-group share-modal-options">
<div class="gf-form">
<textarea rows="3" class="gf-form-input width-27" ng-model="ctrl.annotationTextTo"></textarea>
</div>
<textarea rows="3" class="gf-form-input width-27" ng-model="ctrl.annotation.text"></textarea>
</div>
</div>

View File

@ -1,27 +0,0 @@
<h5 class="section-heading text-center">Create event</h5>
<div class="text-center">
<div class="gf-form-group" style="display: inline-block">
<div class="gf-form">
<span class="gf-form-label width-7">Title</span>
<input type="text" ng-model="ctrl.event.title" class="gf-form-input max-width-20">
</div>
<div class="gf-form">
<span class="gf-form-label width-7">Time</span>
<input type="text" ng-model="ctrl.event.time" class="gf-form-input max-width-20">
</div>
<div class="gf-form" ng-if="ctrl.event.isRegion">
<span class="gf-form-label width-7">To</span>
<input type="text" ng-model="ctrl.event.endTime" class="gf-form-input max-width-20">
</div>
<div class="gf-form gf-form--v-stretch">
<span class="gf-form-label width-7">Description</span>
<textarea class="gf-form-input width-20" rows="3" ng-model="ctrl.event.description" placeholder="Event description"></textarea>
</div>
<div class="gf-form-button-row">
<button class="btn gf-form-btn btn-success" ng-click="ctrl.addAnnotation()">Save</button>
</div>
</div>
</div>

View File

@ -79,29 +79,13 @@ coreModule.directive('grafanaGraph', function($rootScope, timeSrv, popoverSrv) {
}
}, scope);
appEvents.on('graph-click', (event) => {
// Add event only for selected panel
let thisPanelEvent = event.panel.id === ctrl.panel.id;
// Select time for new annotation
let createAnnotation = event.pos.ctrlKey || event.pos.metaKey;
if (createAnnotation && thisPanelEvent) {
let timeRange = {
from: event.pos.x,
to: null
};
showAddAnnotationView(timeRange);
}
}, scope);
function showAddAnnotationView(timeRange) {
popoverSrv.show({
element: elem[0],
classNames: 'drop-popover drop-popover--form',
position: 'bottom center',
openOn: 'click',
template: '<event-editor panelCtrl="ctrl" timeRange="timeRange"></event-editor>',
template: '<event-editor panel-ctrl="panelCtrl" time-range="timeRange"></event-editor>',
model: {
timeRange: timeRange,
panelCtrl: ctrl,
@ -670,10 +654,7 @@ coreModule.directive('grafanaGraph', function($rootScope, timeSrv, popoverSrv) {
elem.bind("plotselected", function (event, ranges) {
if (ranges.ctrlKey || ranges.metaKey) {
// Create new annotation from time range
let timeRange = ranges.xaxis;
showAddAnnotationView(timeRange);
//plot.clearSelection();
showAddAnnotationView(ranges.xaxis);
} else {
scope.$apply(function() {
timeSrv.setTime({
@ -684,6 +665,15 @@ coreModule.directive('grafanaGraph', function($rootScope, timeSrv, popoverSrv) {
}
});
elem.bind("plotclick", function (event, pos, item) {
// Skip if range selected (added in "plotselected" event handler)
let isRangeSelection = pos.x !== pos.x1;
let createAnnotation = !isRangeSelection && (pos.ctrlKey || pos.metaKey);
if (createAnnotation) {
showAddAnnotationView({from: pos.x, to: null});
}
});
scope.$on('$destroy', function() {
tooltip.destroy();
elem.off();

View File

@ -306,21 +306,6 @@ class GraphCtrl extends MetricsPanelCtrl {
alert('selection region while holding down CTRL or CMD');
}
// Get annotation info from dialog and push it to backend
pushAnnotations(annotations) {
return this.annotationsSrv.postAnnotation(annotations);
}
showAddAnnotationModal(timeRange) {
let addAnnotationScope = this.$scope.$new();
addAnnotationScope.annotationTimeRange = timeRange;
this.publishAppEvent('show-modal', {
src: 'public/app/features/dashboard/partials/addAnnotationModal.html',
scope: addAnnotationScope
});
}
legendValuesOptionChanged() {
var legend = this.panel.legend;
legend.values = legend.min || legend.max || legend.avg || legend.current || legend.total;