ux: more nav work

This commit is contained in:
Torkel Ödegaard
2017-08-15 20:24:16 +02:00
parent 1eab771231
commit 0bc226d760
25 changed files with 95 additions and 187 deletions
+10 -8
View File
@@ -20,12 +20,14 @@ type PluginCss struct {
} }
type NavLink struct { type NavLink struct {
Id string `json:"id,omitempty"` Id string `json:"id,omitempty"`
Text string `json:"text,omitempty"` Text string `json:"text,omitempty"`
Description string `json:"description,omitempty"` Description string `json:"description,omitempty"`
Icon string `json:"icon,omitempty"` Icon string `json:"icon,omitempty"`
Img string `json:"img,omitempty"` Img string `json:"img,omitempty"`
Url string `json:"url,omitempty"` Url string `json:"url,omitempty"`
Divider bool `json:"divider,omitempty"` Target string `json:"target,omitempty"`
Children []*NavLink `json:"children,omitempty"` Divider bool `json:"divider,omitempty"`
HideFromMenu bool `json:"hideFromMenu,omitempty"`
Children []*NavLink `json:"children,omitempty"`
} }
+21 -2
View File
@@ -105,19 +105,35 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
data.NavTree = append(data.NavTree, &dtos.NavLink{ data.NavTree = append(data.NavTree, &dtos.NavLink{
Text: "Dashboards", Text: "Dashboards",
Id: "dashboards",
Icon: "icon-gf icon-gf-dashboard", Icon: "icon-gf icon-gf-dashboard",
Url: setting.AppSubUrl + "/", Url: setting.AppSubUrl + "/",
Children: dashboardChildNavs, Children: dashboardChildNavs,
}) })
if c.IsSignedIn {
data.NavTree = append(data.NavTree, &dtos.NavLink{
Text: "Your Profile",
Id: "profile",
Icon: "fa fa-fw fa-user",
Url: setting.AppSubUrl + "/profile",
Children: []*dtos.NavLink{
{Text: "Signout", Url: setting.AppSubUrl + "/logout", Icon: "fa fa-fw fa-sign-out", Target: "_self"},
{Text: "Your profile", Url: setting.AppSubUrl + "/profile", Icon: "fa fa-fw fa-sliders"},
{Text: "Change Password", Id: "change-password", Url: setting.AppSubUrl + "/profile/password", Icon: "fa fa-fw fa-lock", HideFromMenu: true},
},
})
}
if setting.AlertingEnabled && (c.OrgRole == m.ROLE_ADMIN || c.OrgRole == m.ROLE_EDITOR) { if setting.AlertingEnabled && (c.OrgRole == m.ROLE_ADMIN || c.OrgRole == m.ROLE_EDITOR) {
alertChildNavs := []*dtos.NavLink{ alertChildNavs := []*dtos.NavLink{
{Text: "Alert List", Url: setting.AppSubUrl + "/alerting/list", Icon: "fa fa-fw fa-list-ul"}, {Text: "Alert List", Id: "alert-list", Url: setting.AppSubUrl + "/alerting/list", Icon: "fa fa-fw fa-list-ul"},
{Text: "Notification channels", Url: setting.AppSubUrl + "/alerting/notifications", Icon: "fa fa-fw fa-bell-o"}, {Text: "Notification channels", Id: "channels", Url: setting.AppSubUrl + "/alerting/notifications", Icon: "fa fa-fw fa-bell-o"},
} }
data.NavTree = append(data.NavTree, &dtos.NavLink{ data.NavTree = append(data.NavTree, &dtos.NavLink{
Text: "Alerting", Text: "Alerting",
Id: "alerting",
Icon: "icon-gf icon-gf-alert", Icon: "icon-gf icon-gf-alert",
Url: setting.AppSubUrl + "/alerting/list", Url: setting.AppSubUrl + "/alerting/list",
Children: alertChildNavs, Children: alertChildNavs,
@@ -133,6 +149,7 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
if plugin.Pinned { if plugin.Pinned {
appLink := &dtos.NavLink{ appLink := &dtos.NavLink{
Text: plugin.Name, Text: plugin.Name,
Id: "plugin-page-" + plugin.Id,
Url: plugin.DefaultNavUrl, Url: plugin.DefaultNavUrl,
Img: plugin.Info.Logos.Small, Img: plugin.Info.Logos.Small,
} }
@@ -216,6 +233,7 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
}, },
{ {
Text: "API Keys", Text: "API Keys",
Id: "apikeys",
Description: "Create & manage API keys", Description: "Create & manage API keys",
Icon: "fa fa-fw fa-key", Icon: "fa fa-fw fa-key",
Url: setting.AppSubUrl + "/org/apikeys", Url: setting.AppSubUrl + "/org/apikeys",
@@ -226,6 +244,7 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
if c.IsGrafanaAdmin { if c.IsGrafanaAdmin {
cfgNode.Children = append(cfgNode.Children, &dtos.NavLink{ cfgNode.Children = append(cfgNode.Children, &dtos.NavLink{
Text: "Server Admin", Text: "Server Admin",
Id: "admin",
Icon: "fa fa-fw fa-shield", Icon: "fa fa-fw fa-shield",
Url: setting.AppSubUrl + "/admin", Url: setting.AppSubUrl + "/admin",
Children: []*dtos.NavLink{ Children: []*dtos.NavLink{
@@ -39,7 +39,6 @@
</ul> </ul>
</div> </div>
<div class="sidemenu-item dropup dropdown" ng-if="::ctrl.isSignedIn"> <div class="sidemenu-item dropup dropdown" ng-if="::ctrl.isSignedIn">
<a class="sidemenu-link" href="profile"> <a class="sidemenu-link" href="profile">
<span class="icon-circle sidemenu-icon sidemenu-org-avatar"> <span class="icon-circle sidemenu-icon sidemenu-org-avatar">
@@ -59,7 +58,7 @@
{{::orgitem.text}} {{::orgitem.text}}
</a> </a>
</li> </li>
<li ng-repeat="menuItem in ctrl.userMenu" ng-class="::menuItem.cssClass"> <li ng-repeat="menuItem in ctrl.profileNav.children" ng-class="::menuItem.cssClass" ng-hide="menuItem.hideFromMenu">
<a href="{{::menuItem.url}}" ng-show="::menuItem.url" target="{{::menuItem.target}}"> <a href="{{::menuItem.url}}" ng-show="::menuItem.url" target="{{::menuItem.target}}">
<i class="{{::menuItem.icon}}" ng-show="::menuItem.icon"></i> <i class="{{::menuItem.icon}}" ng-show="::menuItem.icon"></i>
{{::menuItem.text}} {{::menuItem.text}}
@@ -7,10 +7,9 @@ import coreModule from '../../core_module';
export class SideMenuCtrl { export class SideMenuCtrl {
isSignedIn: boolean; isSignedIn: boolean;
showSignout: boolean;
user: any; user: any;
mainLinks: any; mainLinks: any;
userMenu: any; profileNav: any;
appSubUrl: string; appSubUrl: string;
loginUrl: string; loginUrl: string;
orgFilter: string; orgFilter: string;
@@ -23,11 +22,9 @@ export class SideMenuCtrl {
this.isSignedIn = contextSrv.isSignedIn; this.isSignedIn = contextSrv.isSignedIn;
this.user = contextSrv.user; this.user = contextSrv.user;
this.appSubUrl = config.appSubUrl; this.appSubUrl = config.appSubUrl;
this.showSignout = this.contextSrv.isSignedIn && !config['disableSignoutMenu'];
this.maxShownOrgs = 10; this.maxShownOrgs = 10;
this.mainLinks = _.filter(config.bootData.navTree, item => item.id !== 'profile');
this.mainLinks = config.bootData.navTree; this.profileNav = _.find(config.bootData.navTree, {id: 'profile'});
this.openUserDropdown();
this.loginUrl = 'login?redirect=' + encodeURIComponent(this.$location.path()); this.loginUrl = 'login?redirect=' + encodeURIComponent(this.$location.path());
this.$scope.$on('$routeChangeSuccess', () => { this.$scope.$on('$routeChangeSuccess', () => {
@@ -49,13 +46,6 @@ export class SideMenuCtrl {
} }
openUserDropdown() { openUserDropdown() {
this.userMenu = [ ];
if (this.showSignout) {
this.userMenu.push({text: "Sign out", url: this.getUrl("/logout"), target: "_self", icon: 'fa fa-sign-out'});
}
this.userMenu.push({text: 'Profile', url: this.getUrl('/profile'), icon: 'fa fa-user'});
// if (this.contextSrv.hasRole('Admin')) { // if (this.contextSrv.hasRole('Admin')) {
// this.orgMenu.push({section: this.user.orgName, cssClass: 'dropdown-menu-title'}); // this.orgMenu.push({section: this.user.orgName, cssClass: 'dropdown-menu-title'});
+15 -100
View File
@@ -29,114 +29,29 @@ export class NavModelSrv {
return _.find(this.navItems, {id: 'cfg'}); return _.find(this.navItems, {id: 'cfg'});
} }
getConfigurationNav() { getNav(...args) {
let cfg = this.getCfgNode(); var children = this.navItems;
return { var nav = {breadcrumbs: [], node: null};
breadcrumbs: [cfg],
node: cfg,
};
}
getAlertingNav(subPage) { for (let id of args) {
return { let node = _.find(children, {id: id});
section: { nav.breadcrumbs.push(node);
title: 'Alerting', nav.node = node;
url: 'plugins', children = node.children;
icon: 'icon-gf icon-gf-alert' }
},
menu: [
{title: 'Alert List', active: subPage === 0, url: 'alerting/list', icon: 'fa fa-list-ul'},
{title: 'Notification channels', active: subPage === 1, url: 'alerting/notifications', icon: 'fa fa-bell-o'},
]
};
}
getDatasourceNav(subPage) { return nav;
let cfg = this.getCfgNode();
let ds = _.find(cfg.children, {id: 'datasources'});
return {
breadcrumbs: [cfg, ds],
node: ds
};
}
getPlaylistsNav(subPage) {
return {
section: {
title: 'Playlists',
url: 'playlists',
icon: 'fa fa-fw fa-film'
},
menu: [
{title: 'List view', active: subPage === 0, url: 'playlists', icon: 'fa fa-list-ul'},
{title: 'Add Playlist', active: subPage === 1, url: 'playlists/create', icon: 'fa fa-plus'},
]
};
}
getProfileNav() {
return {
section: {
title: 'User Profile',
url: 'profile',
icon: 'fa fa-fw fa-user'
},
menu: []
};
} }
getNotFoundNav() { getNotFoundNav() {
return { var node = {
section: { text: "Page not found ",
title: 'Page', icon: "fa fa-fw fa-warning",
url: '',
icon: 'fa fa-fw fa-warning'
},
menu: []
}; };
}
getOrgNav(subPage) {
let cfg = this.getCfgNode();
let org = _.find(cfg.children, {id: 'org'});
return { return {
breadcrumbs: [this.getCfgNode(), org], breadcrumbs: [node],
node: org node: node
};
}
getAdminNav(subPage) {
return {
section: {
title: 'Admin',
url: 'admin',
icon: 'fa fa-fw fa-cogs'
},
menu: [
{title: 'Users', active: subPage === 0, url: 'admin/users', icon: 'fa fa-fw fa-user'},
{title: 'Orgs', active: subPage === 1, url: 'admin/orgs', icon: 'fa fa-fw fa-users'},
{title: 'Server Settings', active: subPage === 2, url: 'admin/settings', icon: 'fa fa-fw fa-cogs'},
{title: 'Server Stats', active: subPage === 2, url: 'admin/stats', icon: 'fa fa-fw fa-line-chart'},
{title: 'Style Guide', active: subPage === 2, url: 'styleguide', icon: 'fa fa-fw fa-key'},
]
};
}
getPluginsNav() {
let cfg = this.getCfgNode();
let plugins = _.find(cfg.children, {id: 'plugins'});
return {
breadcrumbs: [this.getCfgNode(), plugins],
node: plugins
};
}
getUserManNav() {
let cfg = this.getCfgNode();
let users = _.find(cfg.children, {id: 'users'});
return {
breadcrumbs: [cfg, users],
node: users,
}; };
} }
+2 -2
View File
@@ -24,7 +24,7 @@ class AdminHomeCtrl {
/** @ngInject **/ /** @ngInject **/
constructor(navModelSrv) { constructor(navModelSrv) {
this.navModel = navModelSrv.getAdminNav(); this.navModel = navModelSrv.getNav('cfg', 'admin');
} }
} }
@@ -47,7 +47,7 @@ export class ConfigurationHomeCtrl {
/** @ngInject */ /** @ngInject */
constructor(private $scope, private backendSrv, private navModelSrv) { constructor(private $scope, private backendSrv, private navModelSrv) {
this.navModel = navModelSrv.getConfigurationNav(); this.navModel = navModelSrv.getNav('cfg');
} }
} }
@@ -23,7 +23,7 @@ export class AlertListCtrl {
/** @ngInject */ /** @ngInject */
constructor(private backendSrv, private $location, private $scope, navModelSrv) { constructor(private backendSrv, private $location, private $scope, navModelSrv) {
this.navModel = navModelSrv.getAlertingNav(0); this.navModel = navModelSrv.getNav('alerting');
var params = $location.search(); var params = $location.search();
this.filters.state = params.state || null; this.filters.state = params.state || null;
@@ -25,7 +25,7 @@ export class AlertNotificationEditCtrl {
/** @ngInject */ /** @ngInject */
constructor(private $routeParams, private backendSrv, private $location, private $templateCache, navModelSrv) { constructor(private $routeParams, private backendSrv, private $location, private $templateCache, navModelSrv) {
this.navModel = navModelSrv.getAlertingNav(); this.navModel = navModelSrv.getNav('alerting', 'channels');
this.backendSrv.get(`/api/alert-notifiers`).then(notifiers => { this.backendSrv.get(`/api/alert-notifiers`).then(notifiers => {
this.notifiers = notifiers; this.notifiers = notifiers;
@@ -36,10 +36,12 @@ export class AlertNotificationEditCtrl {
} }
if (!this.$routeParams.id) { if (!this.$routeParams.id) {
this.navModel.breadcrumbs.push({text: 'New'});
return _.defaults(this.model, this.defaults); return _.defaults(this.model, this.defaults);
} }
return this.backendSrv.get(`/api/alert-notifications/${this.$routeParams.id}`).then(result => { return this.backendSrv.get(`/api/alert-notifications/${this.$routeParams.id}`).then(result => {
this.navModel.breadcrumbs.push({text: result.name});
return result; return result;
}); });
}).then(model => { }).then(model => {
@@ -14,7 +14,7 @@ export class AlertNotificationsListCtrl {
/** @ngInject */ /** @ngInject */
constructor(private backendSrv, private $scope, navModelSrv) { constructor(private backendSrv, private $scope, navModelSrv) {
this.loadNotifications(); this.loadNotifications();
this.navModel = navModelSrv.getAlertingNav(1); this.navModel = navModelSrv.getNav('alerting', 'channels');
} }
loadNotifications() { loadNotifications() {
@@ -2,11 +2,18 @@
<div class="page-container" > <div class="page-container" >
<div class="page-header"> <div class="page-header">
<h1>Alert List</h1> <h1>
<i class="{{ctrl.navModel.node.icon}}"></i>
{{ctrl.navModel.node.text}}
</h1>
<a class="btn btn-inverse" ng-click="ctrl.openHowTo()"> <a class="btn btn-inverse" ng-click="ctrl.openHowTo()">
<i class="fa fa-info-circle"></i> <i class="fa fa-info-circle"></i>
How to add an alert How to add an alert
</a> </a>
<a class="btn btn-inverse" href="alerting/notifications" >
<i class="fa fa-bell"></i>
Notification channels
</a>
</div> </div>
<div class="gf-form-group"> <div class="gf-form-group">
@@ -2,7 +2,11 @@
<div class="page-container" > <div class="page-container" >
<div class="page-header"> <div class="page-header">
<h1>Notification channels</h1> <h1>
<i class="{{ctrl.navModel.node.icon}}"></i>
{{ctrl.navModel.node.text}}
</h1>
<a href="alerting/notification/new" class="btn btn-success pull-right"> <a href="alerting/notification/new" class="btn btn-success pull-right">
<i class="fa fa-plus"></i> <i class="fa fa-plus"></i>
New Channel New Channel
@@ -12,7 +12,7 @@ function (angular, config) {
$scope.command = {}; $scope.command = {};
$scope.authProxyEnabled = config.authProxyEnabled; $scope.authProxyEnabled = config.authProxyEnabled;
$scope.ldapEnabled = config.ldapEnabled; $scope.ldapEnabled = config.ldapEnabled;
$scope.navModel = navModelSrv.getProfileNav(); $scope.navModel = navModelSrv.getNav('profile', 'change-password');
$scope.changePassword = function() { $scope.changePassword = function() {
if (!$scope.userForm.$valid) { return; } if (!$scope.userForm.$valid) { return; }
+1 -1
View File
@@ -8,7 +8,7 @@ function (angular) {
module.controller('OrgApiKeysCtrl', function($scope, $http, backendSrv, navModelSrv) { module.controller('OrgApiKeysCtrl', function($scope, $http, backendSrv, navModelSrv) {
$scope.navModel = navModelSrv.getOrgNav(0); $scope.navModel = navModelSrv.getNav('cfg', 'apikeys');
$scope.roleTypes = ['Viewer', 'Editor', 'Admin']; $scope.roleTypes = ['Viewer', 'Editor', 'Admin'];
$scope.token = { role: 'Viewer' }; $scope.token = { role: 'Viewer' };
+1 -1
View File
@@ -24,7 +24,7 @@ export class OrgUsersCtrl {
role: 'Viewer', role: 'Viewer',
}; };
this.navModel = navModelSrv.getUserManNav(0); this.navModel = navModelSrv.getNav('cfg', 'users');
this.get(); this.get();
this.editor = { index: 0 }; this.editor = { index: 0 };
@@ -2,7 +2,10 @@
<div class="page-container"> <div class="page-container">
<div class="page-header"> <div class="page-header">
<h1>Change password</h1> <h1>
<i class="{{navModel.node.icon}}"></i>
{{navModel.node.text}}
</h1>
</div> </div>
<div ng-if="ldapEnabled || authProxyEnabled"> <div ng-if="ldapEnabled || authProxyEnabled">
@@ -2,7 +2,10 @@
<div class="page-container"> <div class="page-container">
<div class="page-header"> <div class="page-header">
<h1>User Profile</h1> <h1>
<i class="{{ctrl.navModel.node.icon}}"></i>
{{ctrl.navModel.node.text}}
</h1>
</div> </div>
<form name="ctrl.userForm" class="gf-form-group"> <form name="ctrl.userForm" class="gf-form-group">
+1 -1
View File
@@ -17,7 +17,7 @@ export class ProfileCtrl {
constructor(private backendSrv, private contextSrv, private $location, navModelSrv) { constructor(private backendSrv, private contextSrv, private $location, navModelSrv) {
this.getUser(); this.getUser();
this.getUserOrgs(); this.getUserOrgs();
this.navModel = navModelSrv.getProfileNav(); this.navModel = navModelSrv.getNav('profile');
} }
getUser() { getUser() {
+1 -1
View File
@@ -43,7 +43,7 @@ export class DataSourceEditCtrl {
private navModelSrv, private navModelSrv,
) { ) {
this.navModel = navModelSrv.getDatasourceNav(0); this.navModel = navModelSrv.getNav('cfg', 'datasources');
this.datasources = []; this.datasources = [];
this.tabIndex = 0; this.tabIndex = 0;
+1 -1
View File
@@ -17,7 +17,7 @@ export class DataSourcesCtrl {
private datasourceSrv, private datasourceSrv,
private navModelSrv) { private navModelSrv) {
this.navModel = this.navModelSrv.getDatasourceNav(0); this.navModel = this.navModelSrv.getNav('cfg', 'datasources');
backendSrv.get('/api/datasources').then(result => { backendSrv.get('/api/datasources').then(result => {
this.datasources = result; this.datasources = result;
@@ -28,7 +28,7 @@ export class PluginEditCtrl {
private $http, private $http,
private navModelSrv, private navModelSrv,
) { ) {
this.navModel = navModelSrv.getPluginsNav(); this.navModel = navModelSrv.getNav('cfg', 'plugins');
this.model = {}; this.model = {};
this.pluginId = $routeParams.pluginId; this.pluginId = $routeParams.pluginId;
this.tabIndex = 0; this.tabIndex = 0;
@@ -10,7 +10,7 @@ export class PluginListCtrl {
/** @ngInject */ /** @ngInject */
constructor(private backendSrv: any, $location, navModelSrv) { constructor(private backendSrv: any, $location, navModelSrv) {
this.tabIndex = 0; this.tabIndex = 0;
this.navModel = navModelSrv.getPluginsNav(); this.navModel = navModelSrv.getNav('cfg', 'plugins');
var pluginType = $location.search().type || 'panel'; var pluginType = $location.search().type || 'panel';
switch (pluginType) { switch (pluginType) {
@@ -2,7 +2,6 @@
import angular from 'angular'; import angular from 'angular';
import _ from 'lodash'; import _ from 'lodash';
import {NavModel} from 'app/core/core';
var pluginInfoCache = {}; var pluginInfoCache = {};
@@ -10,10 +9,10 @@ export class AppPageCtrl {
page: any; page: any;
pluginId: any; pluginId: any;
appModel: any; appModel: any;
navModel: NavModel; navModel: any;
/** @ngInject */ /** @ngInject */
constructor(private backendSrv, private $routeParams: any, private $rootScope) { constructor(private backendSrv, private $routeParams: any, private $rootScope, private navModelSrv) {
this.pluginId = $routeParams.pluginId; this.pluginId = $routeParams.pluginId;
if (pluginInfoCache[this.pluginId]) { if (pluginInfoCache[this.pluginId]) {
@@ -32,47 +31,12 @@ export class AppPageCtrl {
if (!this.page) { if (!this.page) {
this.$rootScope.appEvent('alert-error', ['App Page Not Found', '']); this.$rootScope.appEvent('alert-error', ['App Page Not Found', '']);
this.navModel = { this.navModel = this.navModelSrv.getNotFoundNav();
section: {
title: "Page not found",
url: app.defaultNavUrl,
icon: 'icon-gf icon-gf-sadface',
},
menu: [],
};
return; return;
} }
let menu = []; this.navModel = this.navModelSrv.getNav('plugin-page-' + app.id);
this.navModel.breadcrumbs.push({text: this.page.name});
for (let item of app.includes) {
if (item.addToNav) {
if (item.type === 'dashboard') {
menu.push({
title: item.name,
url: 'dashboard/db/' + item.slug,
icon: 'fa fa-fw fa-dot-circle-o',
});
}
if (item.type === 'page') {
menu.push({
title: item.name,
url: `plugins/${app.id}/page/${item.slug}`,
icon: 'fa fa-fw fa-dot-circle-o',
});
}
}
}
this.navModel = {
section: {
title: app.name,
url: app.defaultNavUrl,
iconUrl: app.info.logos.small,
},
menu: menu,
};
} }
loadPluginInfo() { loadPluginInfo() {
+1 -1
View File
@@ -54,7 +54,7 @@ $gf-form-margin: 0.1rem;
flex-shrink: 0; flex-shrink: 0;
font-weight: $font-weight-semi-bold; font-weight: $font-weight-semi-bold;
background-color: #23252d; //$input-label-bg; background-color: #292a2d; //$input-label-bg;
display: block; display: block;
font-size: $font-size-sm; font-size: $font-size-sm;
margin-right: $gf-form-margin; margin-right: $gf-form-margin;
+1 -1
View File
@@ -1,6 +1,6 @@
.query-part { .query-part {
background-color: $input-bg !important; background-color: lighten($input-label-bg, 5%);
&.show-function-controls { &.show-function-controls {
padding-top: 5px; padding-top: 5px;
+2 -2
View File
@@ -49,7 +49,7 @@
} }
.page-header { .page-header {
padding: $spacer 0 0 0; padding: 2rem 0 0 0;
margin-bottom: 2rem; margin-bottom: 2rem;
@include brand-bottom-border(); @include brand-bottom-border();
@include clearfix(); @include clearfix();
@@ -58,7 +58,7 @@
font-size: $font-size-h2; font-size: $font-size-h2;
flex-grow: 1; flex-grow: 1;
display: inline-block; display: inline-block;
margin-bottom: $spacer*1.5; margin-bottom: 2rem;
} }
a, button { a, button {