New TV Mode, dashboard toolbar update (layout change & new cycle view mode button) (#13025)

* wip: design update for navbar with kiosk mode button

* feat: progress on new view mode button

* css: view state refactorings

* feat: kiosk modes & playlist support

* feature: cycle tv mode feature, renamed view modes to TV, and Kiosk

* fix: updated the alert notification message

* fix: removed unused parameter

* fix: correct the css class set for tv mode

* some minor improvements to playlist
This commit is contained in:
Torkel Ödegaard
2018-08-30 11:52:31 +02:00
committed by GitHub
parent 36bc8b77fb
commit 154fbe2413
18 changed files with 279 additions and 286 deletions

View File

@@ -1,9 +1,9 @@
<page-header model="ctrl.navModel"></page-header>
<div class="page-container page-body" ng-form="playlistEditForm">
<div class="page-container page-body" ng-form="ctrl.playlistEditForm">
<h3 class="page-sub-heading" ng-hide="ctrl.isNew">Edit Playlist</h3>
<h3 class="page-sub-heading" ng-show="ctrl.isNew">New Playlist</h3>
<h3 class="page-sub-heading" ng-hide="ctrl.isNew">Edit Playlist</h3>
<h3 class="page-sub-heading" ng-show="ctrl.isNew">New Playlist</h3>
<p class="playlist-description">A playlist rotates through a pre-selected list of Dashboards. A Playlist can be a great way to build situational awareness, or just show off your metrics to your team or visitors.</p>
@@ -20,79 +20,71 @@
<div class="gf-form-group">
<h3 class="page-headering">Dashboards</h3>
<table class="filter-table playlist-available-list">
<tr ng-repeat="playlistItem in ctrl.playlistItems">
<td ng-if="playlistItem.type === 'dashboard_by_id'">
<i class="icon-gf icon-gf-dashboard"></i>&nbsp;&nbsp;{{playlistItem.title}}
</td>
<td ng-if="playlistItem.type === 'dashboard_by_tag'">
<a class="search-result-tag label label-tag" tag-color-from-name="playlistItem.title">
<i class="fa fa-tag"></i>
<span>{{playlistItem.title}}</span>
</a>
</td>
<td class="selected-playlistitem-settings">
<button class="btn btn-inverse btn-mini" ng-hide="$first" ng-click="ctrl.movePlaylistItemUp(playlistItem)">
<i class="fa fa-arrow-up"></i>
</button>
<button class="btn btn-inverse btn-mini" ng-hide="$last" ng-click="ctrl.movePlaylistItemDown(playlistItem)">
<i class="fa fa-arrow-down"></i>
</button>
<button class="btn btn-inverse btn-mini" ng-click="ctrl.removePlaylistItem(playlistItem)">
<i class="fa fa-remove"></i>
</button>
</td>
</tr>
<tr ng-if="ctrl.playlistItems.length === 0">
<td><em>Playlist is empty, add dashboards below.</em></td>
</tr>
</table>
</div>
<div class="row">
<div class="col-lg-6">
<div class="playlist-search-containerwrapper">
<div class="max-width-32">
<h5 class="page-headering playlist-column-header">Available</h5>
<div style="">
<playlist-search class="playlist-search-container" search-started="ctrl.searchStarted(promise)"></playlist-search>
</div>
</div>
</div>
<div class="gf-form-group">
<h3 class="page-headering">Add dashboards</h3>
<playlist-search class="playlist-search-container" search-started="ctrl.searchStarted(promise)"></playlist-search>
<div ng-if="ctrl.filteredDashboards.length > 0">
<table class="filter-table playlist-available-list">
<tr ng-repeat="playlistItem in ctrl.filteredDashboards">
<td>
<i class="icon-gf icon-gf-dashboard"></i>
&nbsp;&nbsp;{{playlistItem.title}}
<i class="fa fa-star" ng-show="playlistItem.isStarred"></i>
</td>
<td class="add-dashboard">
<button class="btn btn-inverse btn-mini pull-right" ng-click="ctrl.addPlaylistItem(playlistItem)">
<i class="fa fa-plus"></i>
Add to playlist
</button>
</td>
</tr>
</table>
</div>
<div class="playlist-search-results-container" ng-if="ctrl.filteredTags.length > 0;">
<table class="filter-table playlist-available-list">
<tr ng-repeat="tag in ctrl.filteredTags">
<td>
<a class="search-result-tag label label-tag" tag-color-from-name="tag.term">
<i class="fa fa-tag"></i>
<span>{{tag.term}} &nbsp;({{tag.count}})</span>
</a>
</td>
<td class="add-dashboard">
<button class="btn btn-inverse btn-mini pull-right" ng-click="ctrl.addTagPlaylistItem(tag)">
<i class="fa fa-plus"></i>
Add to playlist
</button>
</td>
</tr>
</table>
</div>
</div>
<div class="col-lg-6">
<h5 class="page headering playlist-column-header">Selected</h5>
<div ng-if="ctrl.filteredDashboards.length > 0">
<table class="filter-table playlist-available-list">
<tr ng-repeat="playlistItem in ctrl.playlistItems">
<td ng-if="playlistItem.type === 'dashboard_by_id'">
<i class="icon-gf icon-gf-dashboard"></i>&nbsp;&nbsp;{{playlistItem.title}}
<tr ng-repeat="playlistItem in ctrl.filteredDashboards">
<td>
<i class="icon-gf icon-gf-dashboard"></i>
&nbsp;&nbsp;{{playlistItem.title}}
<i class="fa fa-star" ng-show="playlistItem.isStarred"></i>
</td>
<td ng-if="playlistItem.type === 'dashboard_by_tag'">
<a class="search-result-tag label label-tag" tag-color-from-name="playlistItem.title">
<td class="add-dashboard">
<button class="btn btn-inverse btn-mini pull-right" ng-click="ctrl.addPlaylistItem(playlistItem)">
<i class="fa fa-plus"></i>
Add to playlist
</button>
</td>
</tr>
</table>
</div>
<div class="playlist-search-results-container" ng-if="ctrl.filteredTags.length > 0;">
<table class="filter-table playlist-available-list">
<tr ng-repeat="tag in ctrl.filteredTags">
<td>
<a class="search-result-tag label label-tag" tag-color-from-name="tag.term">
<i class="fa fa-tag"></i>
<span>{{playlistItem.title}}</span>
<span>{{tag.term}} &nbsp;({{tag.count}})</span>
</a>
</td>
<td class="selected-playlistitem-settings">
<button class="btn btn-inverse btn-mini" ng-hide="$first" ng-click="ctrl.movePlaylistItemUp(playlistItem)">
<i class="fa fa-arrow-up"></i>
</button>
<button class="btn btn-inverse btn-mini" ng-hide="$last" ng-click="ctrl.movePlaylistItemDown(playlistItem)">
<i class="fa fa-arrow-down"></i>
</button>
<button class="btn btn-inverse btn-mini" ng-click="ctrl.removePlaylistItem(playlistItem)">
<i class="fa fa-remove"></i>
<td class="add-dashboard">
<button class="btn btn-inverse btn-mini pull-right" ng-click="ctrl.addTagPlaylistItem(tag)">
<i class="fa fa-plus"></i>
Add to playlist
</button>
</td>
</tr>
@@ -103,12 +95,8 @@
<div class="clearfix"></div>
<div class="gf-form-button-row">
<a class="btn btn-success " ng-show="ctrl.isNew"
ng-disabled="ctrl.playlistEditForm.$invalid || ctrl.isPlaylistEmpty()"
ng-click="ctrl.savePlaylist(ctrl.playlist, ctrl.playlistItems)">Create new playlist</a>
<a class="btn btn-success" ng-show="!ctrl.isNew()"
ng-disabled="ctrl.playlistEditForm.$invalid || ctrl.isPlaylistEmpty()"
ng-click="ctrl.savePlaylist(ctrl.playlist, ctrl.playlistItems)">Save</a>
<a class="btn btn-success" ng-show="ctrl.isNew" ng-disabled="ctrl.playlistEditForm.$invalid || ctrl.isPlaylistEmpty()" ng-click="ctrl.savePlaylist(ctrl.playlist, ctrl.playlistItems)">Create</a>
<a class="btn btn-success" ng-show="!ctrl.isNew" ng-disabled="ctrl.playlistEditForm.$invalid || ctrl.isPlaylistEmpty()" ng-click="ctrl.savePlaylist(ctrl.playlist, ctrl.playlistItems)">Save</a>
<a class="btn-text" ng-click="ctrl.backToList()">Cancel</a>
</div>
</div>

View File

@@ -10,38 +10,42 @@
</a>
</div>
<table class="filter-table">
<table class="filter-table filter-table--hover">
<thead>
<th>
<strong>Name</strong>
</th>
<th>
<strong>Start url</strong>
</th>
<th><strong>Name</strong></th>
<th style="width: 100px"></th>
<th style="width: 78px"></th>
<th style="width: 78px"></th>
<th style="width: 25px"></th>
</thead>
<tr ng-repeat="playlist in ctrl.playlists">
<td>
<td class="link-td">
<a href="playlists/edit/{{playlist.id}}">{{playlist.name}}</a>
</td>
<td>
<a href="playlists/play/{{playlist.id}}">playlists/play/{{playlist.id}}</a>
<td class="dropdown">
<button class="btn btn-inverse btn-small" data-toggle="dropdown">
Start playlist
<i class="fa fa-caret-down"></i>
</button>
<ul class="dropdown-menu" role="menu">
<li>
<a href="{{playlist.startUrl}}">
<i class="fa fa-play"></i> In Normal mode</span>
</a>
<a href="{{playlist.startUrl}}?kiosk=tv">
<i class="fa fa-play"></i> In TV mode</span>
</a>
<a href="{{playlist.startUrl}}?kiosk=tv&autofitpanels">
<i class="fa fa-play"></i> In TV mode <span class="muted">(with auto fit panels)</span>
</a>
<a href="{{playlist.startUrl}}?kiosk">
<i class="fa fa-play"></i> In Kiosk mode</span>
</a>
<a ng-href="{{playlist.startUrl}}?kiosk&autofitpanels">
<i class="fa fa-play"></i> In Kiosk mode <span class="muted">(with auto fit panels)</span>
</a>
</li>
</ul>
</td>
<td class="text-center">
<a href="playlists/play/{{playlist.id}}" class="btn btn-inverse btn-small">
<i class="fa fa-play"></i>
Play
</a>
</td>
<td class="text-right">
<a href="playlists/edit/{{playlist.id}}" class="btn btn-inverse btn-small">
<i class="fa fa-edit"></i>
Edit
</a>
</td>
<td class="text-right">
<td class="text-right">
<a ng-click="ctrl.removePlaylist(playlist)" class="btn btn-danger btn-small">
<i class="fa fa-remove"></i>
</a>
@@ -49,18 +53,16 @@
</tr>
</table>
</div>
<div ng-if="ctrl.playlists.length === 0">
<empty-list-cta model="{
title: 'There are no playlists created yet',
buttonIcon: 'fa fa-plus',
buttonLink: 'playlists/create',
buttonTitle: ' Create Playlist',
proTip: 'You can run the playlist in Kiosk Mode.',
proTip: 'You can use playlists to remove control TVs',
proTipLink: 'http://docs.grafana.org/reference/playlist/',
proTipLinkTitle: 'Learn more',
proTipTarget: '_blank'
}" />
</div>
</div>

View File

@@ -19,29 +19,18 @@ export class PlaylistEditCtrl {
/** @ngInject */
constructor(private $scope, private backendSrv, private $location, $route, navModelSrv) {
this.navModel = navModelSrv.getNav('dashboards', 'playlists', 0);
this.isNew = $route.current.params.id;
this.isNew = !$route.current.params.id;
if ($route.current.params.id) {
const playlistId = $route.current.params.id;
backendSrv.get('/api/playlists/' + playlistId).then(result => {
this.playlist = result;
this.navModel.node = {
text: result.name,
icon: this.navModel.node.icon,
};
this.navModel.breadcrumbs.push(this.navModel.node);
});
backendSrv.get('/api/playlists/' + playlistId + '/items').then(result => {
this.playlistItems = result;
});
} else {
this.navModel.node = {
text: 'New playlist',
icon: this.navModel.node.icon,
};
this.navModel.breadcrumbs.push(this.navModel.node);
}
}

View File

@@ -19,9 +19,7 @@ function grafanaRoutes($routeProvider) {
controller: 'PlaylistEditCtrl',
})
.when('/playlists/play/:id', {
templateUrl: 'public/app/features/playlist/partials/playlists.html',
controllerAs: 'ctrl',
controller: 'PlaylistsCtrl',
template: '',
resolve: {
init: function(playlistSrv, $route) {
const playlistId = $route.current.params.id;

View File

@@ -8,7 +8,7 @@ export class PlaylistSearchCtrl {
/** @ngInject */
constructor($timeout, private backendSrv) {
this.query = { query: '', tag: [], starred: false, limit: 30 };
this.query = { query: '', tag: [], starred: false, limit: 20 };
$timeout(() => {
this.query.query = '';

View File

@@ -1,6 +1,8 @@
import coreModule from '../../core/core_module';
import kbn from 'app/core/utils/kbn';
import appEvents from 'app/core/app_events';
import _ from 'lodash';
import { toUrlParams } from 'app/core/utils/url';
class PlaylistSrv {
private cancelPromise: any;
@@ -11,42 +13,27 @@ class PlaylistSrv {
public isPlaying: boolean;
/** @ngInject */
constructor(private $location: any, private $timeout: any, private backendSrv: any, private $routeParams: any) {}
constructor(private $location: any, private $timeout: any, private backendSrv: any) {}
next() {
this.$timeout.cancel(this.cancelPromise);
const playedAllDashboards = this.index > this.dashboards.length - 1;
if (playedAllDashboards) {
window.location.href = this.getUrlWithKioskMode();
window.location.href = this.startUrl;
return;
}
const dash = this.dashboards[this.index];
this.$location.url('dashboard/' + dash.uri);
const queryParams = this.$location.search();
const filteredParams = _.pickBy(queryParams, value => value !== null);
this.$location.url('dashboard/' + dash.uri + '?' + toUrlParams(filteredParams));
this.index++;
this.cancelPromise = this.$timeout(() => this.next(), this.interval);
}
getUrlWithKioskMode() {
const inKioskMode = document.body.classList.contains('page-kiosk-mode');
// check if should add kiosk query param
if (inKioskMode && this.startUrl.indexOf('kiosk') === -1) {
return this.startUrl + '?kiosk=true';
}
// check if should remove kiosk query param
if (!inKioskMode) {
return this.startUrl.split('?')[0];
}
// already has kiosk query param, just return startUrl
return this.startUrl;
}
prev() {
this.index = Math.max(this.index - 2, 0);
this.next();
@@ -59,10 +46,6 @@ class PlaylistSrv {
this.index = 0;
this.isPlaying = true;
if (this.$routeParams.kiosk) {
appEvents.emit('toggle-kiosk-mode');
}
this.backendSrv.get(`/api/playlists/${playlistId}`).then(playlist => {
this.backendSrv.get(`/api/playlists/${playlistId}/dashboards`).then(dashboards => {
this.dashboards = dashboards;
@@ -73,6 +56,13 @@ class PlaylistSrv {
}
stop() {
if (this.isPlaying) {
const queryParams = this.$location.search();
if (queryParams.kiosk) {
appEvents.emit('toggle-kiosk-mode', { exit: true });
}
}
this.index = 0;
this.isPlaying = false;

View File

@@ -10,7 +10,10 @@ export class PlaylistsCtrl {
this.navModel = navModelSrv.getNav('dashboards', 'playlists', 0);
backendSrv.get('/api/playlists').then(result => {
this.playlists = result;
this.playlists = result.map(item => {
item.startUrl = `playlists/play/${item.id}`;
return item;
});
});
}