feat(playlist): basic UI support for tags
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
define([
|
define([
|
||||||
'./playlists_ctrl',
|
'./playlists_ctrl',
|
||||||
|
'./playlist_search',
|
||||||
'./playlist_srv',
|
'./playlist_srv',
|
||||||
'./playlist_edit_ctrl',
|
'./playlist_edit_ctrl',
|
||||||
'./playlist_routes'
|
'./playlist_routes'
|
||||||
|
|||||||
@@ -39,34 +39,24 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
<h4>Add dashboards</h4>
|
|
||||||
|
|
||||||
<div style="display: inline-block">
|
|
||||||
<div class="tight-form last">
|
|
||||||
<ul class="tight-form-list">
|
|
||||||
<li class="tight-form-item">
|
|
||||||
Search
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<input type="text"
|
|
||||||
class="tight-form-input input-xlarge last"
|
|
||||||
ng-model="ctrl.searchQuery"
|
|
||||||
placeholder="dashboard search term"
|
|
||||||
ng-trim="true"
|
|
||||||
ng-change="ctrl.search()">
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="span5 pull-left">
|
<div class="span5 pull-left">
|
||||||
<h5>Search results ({{ctrl.filteredPlaylistItems.length}})</h5>
|
<h5>Add dashboards</h5>
|
||||||
|
<div style="">
|
||||||
|
<playlist-search class="playlist-search-container" search-started="ctrl.searchStarted(promise)"></playlist-search>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="span5 pull-left" ng-if="ctrl.filteredDashboards.length > 0">
|
||||||
|
<h5>Search results ({{ctrl.filteredDashboards.length}})</h5>
|
||||||
<table class="grafana-options-table">
|
<table class="grafana-options-table">
|
||||||
<tr ng-repeat="playlistItem in ctrl.filteredPlaylistItems">
|
<tr ng-repeat="playlistItem in ctrl.filteredDashboards">
|
||||||
<td style="white-space: nowrap;">
|
<td style="white-space: nowrap;">
|
||||||
{{playlistItem.title}}
|
{{playlistItem.title}}
|
||||||
</td>
|
</td>
|
||||||
@@ -77,13 +67,22 @@
|
|||||||
</button>
|
</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr ng-if="ctrl.isSearchResultsEmpty()">
|
|
||||||
<td colspan="2">
|
|
||||||
<i class="fa fa-warning"></i> Search results empty
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="playlist-search-results-container" ng-if="ctrl.filteredTags.length > 0">
|
||||||
|
<div class="row">
|
||||||
|
<div class="span6 offset1">
|
||||||
|
<div ng-repeat="tag in ctrl.filteredTags" class="pointer" style="width: 180px; float: left;"
|
||||||
|
ng-class="{'selected': $index === selectedIndex }"
|
||||||
|
ng-click="ctrl.addTagPlaylistItem(tag, $event)">
|
||||||
|
<a class="search-result-tag label label-tag" tag-color-from-name="tag.term">
|
||||||
|
<i class="fa fa-tag"></i>
|
||||||
|
<span>{{tag.term}} ({{tag.count}})</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="span5 pull-left">
|
<div class="span5 pull-left">
|
||||||
<h5>Added dashboards</h5>
|
<h5>Added dashboards</h5>
|
||||||
<table class="grafana-options-table">
|
<table class="grafana-options-table">
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<div class="playlist-search-field-wrapper">
|
||||||
|
<span style="position: relative;">
|
||||||
|
<input type="text" placeholder="Find dashboards by name" give-focus="ctrl.giveSearchFocus" tabindex="1"
|
||||||
|
ng-keydown="ctrl.keyDown($event)" ng-model="ctrl.query.query" ng-model-options="{ debounce: 500 }" spellcheck='false' ng-change="ctrl.searchDashboards()" />
|
||||||
|
</span>
|
||||||
|
<div class="playlist-search-switches">
|
||||||
|
<i class="fa fa-filter"></i>
|
||||||
|
<a class="pointer" href="javascript:void 0;" ng-click="ctrl.showStarred()" tabindex="2">
|
||||||
|
<i class="fa fa-remove" ng-show="ctrl.query.starred"></i>
|
||||||
|
starred
|
||||||
|
</a> |
|
||||||
|
<a class="pointer" href="javascript:void 0;" ng-click="ctrl.getTags()" tabindex="3">
|
||||||
|
<i class="fa fa-remove" ng-show="ctrl.tagsMode"></i>
|
||||||
|
tags
|
||||||
|
</a>
|
||||||
|
<span ng-if="ctrl.query.tag.length">
|
||||||
|
|
|
||||||
|
<span ng-repeat="tagName in ctrl.query.tag">
|
||||||
|
<a ng-click="ctrl.removeTag(tagName, $event)" tag-color-from-name="ctrl.tagName" class="label label-tag">
|
||||||
|
<i class="fa fa-remove"></i>
|
||||||
|
{{tagName}}
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -6,14 +6,16 @@ import coreModule from '../../core/core_module';
|
|||||||
import config from 'app/core/config';
|
import config from 'app/core/config';
|
||||||
|
|
||||||
export class PlaylistEditCtrl {
|
export class PlaylistEditCtrl {
|
||||||
filteredPlaylistItems: any = [];
|
filteredDashboards: any = [];
|
||||||
foundPlaylistItems: any = [];
|
filteredTags: any = [];
|
||||||
searchQuery: string = '';
|
searchQuery: string = '';
|
||||||
loading: boolean = false;
|
loading: boolean = false;
|
||||||
playlist: any = {
|
playlist: any = {
|
||||||
interval: '10m',
|
interval: '10m',
|
||||||
};
|
};
|
||||||
playlistItems: any = [];
|
playlistItems: any = [];
|
||||||
|
dashboardresult: any = [];
|
||||||
|
tagresult: any = [];
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
constructor(private $scope, private playlistSrv, private backendSrv, private $location, private $route) {
|
constructor(private $scope, private playlistSrv, private backendSrv, private $location, private $route) {
|
||||||
@@ -30,35 +32,18 @@ export class PlaylistEditCtrl {
|
|||||||
this.playlistItems = result;
|
this.playlistItems = result;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.search();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
search() {
|
|
||||||
var query: any = {limit: 10};
|
|
||||||
|
|
||||||
if (this.searchQuery) {
|
|
||||||
query.query = this.searchQuery;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.loading = true;
|
|
||||||
|
|
||||||
this.backendSrv.search(query)
|
|
||||||
.then((results) => {
|
|
||||||
this.foundPlaylistItems = results;
|
|
||||||
this.filterFoundPlaylistItems();
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
this.loading = false;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
filterFoundPlaylistItems() {
|
filterFoundPlaylistItems() {
|
||||||
this.filteredPlaylistItems = _.reject(this.foundPlaylistItems, (playlistItem) => {
|
console.log('filter !');
|
||||||
|
console.log(this.dashboardresult);
|
||||||
|
this.filteredDashboards = _.reject(this.dashboardresult, (playlistItem) => {
|
||||||
return _.findWhere(this.playlistItems, (listPlaylistItem) => {
|
return _.findWhere(this.playlistItems, (listPlaylistItem) => {
|
||||||
return parseInt(listPlaylistItem.value) === playlistItem.id;
|
return parseInt(listPlaylistItem.value) === playlistItem.id;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.filteredTags = this.tagresult;
|
||||||
};
|
};
|
||||||
|
|
||||||
addPlaylistItem(playlistItem) {
|
addPlaylistItem(playlistItem) {
|
||||||
@@ -70,6 +55,20 @@ export class PlaylistEditCtrl {
|
|||||||
this.filterFoundPlaylistItems();
|
this.filterFoundPlaylistItems();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
addTagPlaylistItem(tag) {
|
||||||
|
console.log(tag);
|
||||||
|
|
||||||
|
var playlistItem: any = {
|
||||||
|
value: tag.term,
|
||||||
|
type: 'dashboard_by_tag',
|
||||||
|
order: this.playlistItems.length + 1,
|
||||||
|
title: tag.term
|
||||||
|
};
|
||||||
|
|
||||||
|
this.playlistItems.push(playlistItem);
|
||||||
|
this.filterFoundPlaylistItems();
|
||||||
|
}
|
||||||
|
|
||||||
removePlaylistItem(playlistItem) {
|
removePlaylistItem(playlistItem) {
|
||||||
_.remove(this.playlistItems, (listedPlaylistItem) => {
|
_.remove(this.playlistItems, (listedPlaylistItem) => {
|
||||||
return playlistItem === listedPlaylistItem;
|
return playlistItem === listedPlaylistItem;
|
||||||
@@ -104,7 +103,7 @@ export class PlaylistEditCtrl {
|
|||||||
};
|
};
|
||||||
|
|
||||||
isSearchResultsEmpty() {
|
isSearchResultsEmpty() {
|
||||||
return !this.foundPlaylistItems.length;
|
return !this.dashboardresult.length;
|
||||||
};
|
};
|
||||||
|
|
||||||
isSearchQueryEmpty() {
|
isSearchQueryEmpty() {
|
||||||
@@ -119,6 +118,16 @@ export class PlaylistEditCtrl {
|
|||||||
return this.loading;
|
return this.loading;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
searchStarted(promise) {
|
||||||
|
promise.then((data) => {
|
||||||
|
console.log('searchStarted: ', data);
|
||||||
|
|
||||||
|
this.dashboardresult = data.dashboardResult;
|
||||||
|
this.tagresult = data.tagResult;
|
||||||
|
this.filterFoundPlaylistItems();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
movePlaylistItem(playlistItem, offset) {
|
movePlaylistItem(playlistItem, offset) {
|
||||||
var currentPosition = this.playlistItems.indexOf(playlistItem);
|
var currentPosition = this.playlistItems.indexOf(playlistItem);
|
||||||
var newPosition = currentPosition + offset;
|
var newPosition = currentPosition + offset;
|
||||||
|
|||||||
@@ -0,0 +1,95 @@
|
|||||||
|
///<reference path="../../headers/common.d.ts" />
|
||||||
|
|
||||||
|
import angular from 'angular';
|
||||||
|
import config from 'app/core/config';
|
||||||
|
import _ from 'lodash';
|
||||||
|
import $ from 'jquery';
|
||||||
|
import coreModule from '../../core/core_module';
|
||||||
|
|
||||||
|
export class PlaylistSearchCtrl {
|
||||||
|
query: any;
|
||||||
|
tagsMode: boolean;
|
||||||
|
|
||||||
|
searchStarted: any;
|
||||||
|
|
||||||
|
/** @ngInject */
|
||||||
|
constructor(private $scope, private $location, private $timeout, private backendSrv, private contextSrv) {
|
||||||
|
this.query = { query: '', tag: [], starred: false };
|
||||||
|
|
||||||
|
$timeout(() => {
|
||||||
|
this.query.query = '';
|
||||||
|
this.searchDashboards();
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
searchDashboards() {
|
||||||
|
this.tagsMode = false;
|
||||||
|
var prom: any = {};
|
||||||
|
|
||||||
|
/*
|
||||||
|
prom.promise = this.backendSrv.search(this.query).then((results) => {
|
||||||
|
console.log('playlist_search_ctrl: ', results);
|
||||||
|
return results;
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
prom.promise = this.backendSrv.search(this.query).then((result) => {
|
||||||
|
return {
|
||||||
|
dashboardResult: result,
|
||||||
|
tagResult: []
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
this.searchStarted(prom);
|
||||||
|
}
|
||||||
|
|
||||||
|
queryHasNoFilters() {
|
||||||
|
return this.query.query === '' && this.query.starred === false && this.query.tag.length === 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
filterByTag(tag, evt) {
|
||||||
|
this.query.tag.push(tag);
|
||||||
|
this.searchDashboards();
|
||||||
|
if (evt) {
|
||||||
|
evt.stopPropagation();
|
||||||
|
evt.preventDefault();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
getTags() {
|
||||||
|
var prom: any = {};
|
||||||
|
prom.promise = this.backendSrv.get('/api/dashboards/tags').then((result) => {
|
||||||
|
console.log('getTags: result', result);
|
||||||
|
return {
|
||||||
|
dashboardResult: [],
|
||||||
|
tagResult: result
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
this.searchStarted(prom);
|
||||||
|
/*
|
||||||
|
this.searchStarted(prom);
|
||||||
|
|
||||||
|
return this.backendSrv.get('/api/dashboards/tags').then((results) => {
|
||||||
|
this.tagsMode = true;
|
||||||
|
|
||||||
|
|
||||||
|
console.log(results);
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function playlistSearchDirective() {
|
||||||
|
return {
|
||||||
|
restrict: 'E',
|
||||||
|
templateUrl: 'app/features/playlist/partials/playlist_search.html',
|
||||||
|
controller: PlaylistSearchCtrl,
|
||||||
|
bindToController: true,
|
||||||
|
controllerAs: 'ctrl',
|
||||||
|
scope: {
|
||||||
|
searchStarted: '&'
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
coreModule.directive('playlistSearch', playlistSearchDirective);
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
@import "bootstrap-tagsinput.less";
|
@import "bootstrap-tagsinput.less";
|
||||||
@import "tables_lists.less";
|
@import "tables_lists.less";
|
||||||
@import "search.less";
|
@import "search.less";
|
||||||
|
@import "playlist.less";
|
||||||
@import "panel.less";
|
@import "panel.less";
|
||||||
@import "forms.less";
|
@import "forms.less";
|
||||||
@import "tightform.less";
|
@import "tightform.less";
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
.playlist-search-container {
|
||||||
|
//left: 59px;
|
||||||
|
//top: 39px;
|
||||||
|
margin: 15px;
|
||||||
|
z-index: 1000;
|
||||||
|
//position: relative;
|
||||||
|
position: relative;
|
||||||
|
width: 700px;
|
||||||
|
box-shadow: 0px 0px 55px 0px black;
|
||||||
|
//padding: 10px;
|
||||||
|
background-color: @grafanaPanelBackground;
|
||||||
|
//border: 1px solid @grafanaTargetFuncBackground;
|
||||||
|
|
||||||
|
.label-tag {
|
||||||
|
margin-left: 6px;
|
||||||
|
font-size: 11px;
|
||||||
|
padding: 2px 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.playlist-search-switches {
|
||||||
|
position: relative;
|
||||||
|
top: -39px;
|
||||||
|
right: -268px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.playlist-search-field-wrapper {
|
||||||
|
//padding-bottom: 10px;
|
||||||
|
input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 8px 8px;
|
||||||
|
height: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
margin: 0 4px 0 0;
|
||||||
|
}
|
||||||
|
> span {
|
||||||
|
display: block;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.playlist-search-results-container {
|
||||||
|
min-height: 100px;
|
||||||
|
overflow: auto;
|
||||||
|
display: block;
|
||||||
|
line-height: 28px;
|
||||||
|
|
||||||
|
.search-item:hover, .search-item.selected {
|
||||||
|
background-color: @grafanaListHighlight;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected {
|
||||||
|
.search-result-tag {
|
||||||
|
opacity: 0.70;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fa-star, .fa-star-o {
|
||||||
|
padding-left: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fa-star {
|
||||||
|
color: @orange;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-result-link {
|
||||||
|
color: @grafanaListMainLinkColor;
|
||||||
|
.fa {
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-item {
|
||||||
|
display: block;
|
||||||
|
padding: 3px 10px;
|
||||||
|
white-space: nowrap;
|
||||||
|
background-color: @grafanaListBackground;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
.search-result-icon:before {
|
||||||
|
content: "\f009";
|
||||||
|
}
|
||||||
|
|
||||||
|
&.search-item-dash-home .search-result-icon:before {
|
||||||
|
content: "\f015";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-result-tags {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-result-actions {
|
||||||
|
float: right;
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user