mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
ux: popover forms
This commit is contained in:
commit
2fce88ee62
26
pkg/services/sqlstore/migrations/annotation_category_mig.go
Normal file
26
pkg/services/sqlstore/migrations/annotation_category_mig.go
Normal 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]))
|
||||
}
|
@ -26,6 +26,7 @@ func AddMigrations(mg *Migrator) {
|
||||
addAnnotationMig(mg)
|
||||
addStatsMigrations(mg)
|
||||
addTestDataMigrations(mg)
|
||||
// addAnnotationCategoryMig(mg)
|
||||
}
|
||||
|
||||
func addMigrationLogMigrations(mg *Migrator) {
|
||||
|
@ -1,7 +1,7 @@
|
||||
define([
|
||||
'./panellinks/module',
|
||||
'./dashlinks/module',
|
||||
'./annotations/annotations_srv',
|
||||
'./annotations/all',
|
||||
'./templating/all',
|
||||
'./dashboard/all',
|
||||
'./playlist/all',
|
||||
|
8
public/app/features/annotations/all.ts
Normal file
8
public/app/features/annotations/all.ts
Normal file
@ -0,0 +1,8 @@
|
||||
|
||||
import {AnnotationsSrv} from './annotations_srv';
|
||||
import {eventEditor} from './event_editor';
|
||||
|
||||
export {
|
||||
AnnotationsSrv,
|
||||
eventEditor
|
||||
};
|
@ -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) {
|
||||
|
60
public/app/features/annotations/event_editor.ts
Normal file
60
public/app/features/annotations/event_editor.ts
Normal 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);
|
39
public/app/features/annotations/partials/event_editor.html
Normal file
39
public/app/features/annotations/partials/event_editor.html
Normal 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>
|
@ -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 => {
|
||||
|
@ -22,5 +22,4 @@ define([
|
||||
'./ad_hoc_filters',
|
||||
'./row/row_ctrl',
|
||||
'./repeat_option/repeat_option',
|
||||
'./event_editor',
|
||||
], function () {});
|
||||
|
@ -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);
|
@ -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>
|
||||
|
||||
|
@ -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>
|
@ -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();
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user