Commit 0bc226d7 by Torkel Ödegaard

ux: more nav work

parent 1eab7712
......@@ -20,12 +20,14 @@ type PluginCss struct {
}
type NavLink struct {
Id string `json:"id,omitempty"`
Text string `json:"text,omitempty"`
Description string `json:"description,omitempty"`
Icon string `json:"icon,omitempty"`
Img string `json:"img,omitempty"`
Url string `json:"url,omitempty"`
Divider bool `json:"divider,omitempty"`
Children []*NavLink `json:"children,omitempty"`
Id string `json:"id,omitempty"`
Text string `json:"text,omitempty"`
Description string `json:"description,omitempty"`
Icon string `json:"icon,omitempty"`
Img string `json:"img,omitempty"`
Url string `json:"url,omitempty"`
Target string `json:"target,omitempty"`
Divider bool `json:"divider,omitempty"`
HideFromMenu bool `json:"hideFromMenu,omitempty"`
Children []*NavLink `json:"children,omitempty"`
}
......@@ -105,19 +105,35 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
data.NavTree = append(data.NavTree, &dtos.NavLink{
Text: "Dashboards",
Id: "dashboards",
Icon: "icon-gf icon-gf-dashboard",
Url: setting.AppSubUrl + "/",
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) {
alertChildNavs := []*dtos.NavLink{
{Text: "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: "Alert List", Id: "alert-list", Url: setting.AppSubUrl + "/alerting/list", Icon: "fa fa-fw fa-list-ul"},
{Text: "Notification channels", Id: "channels", Url: setting.AppSubUrl + "/alerting/notifications", Icon: "fa fa-fw fa-bell-o"},
}
data.NavTree = append(data.NavTree, &dtos.NavLink{
Text: "Alerting",
Id: "alerting",
Icon: "icon-gf icon-gf-alert",
Url: setting.AppSubUrl + "/alerting/list",
Children: alertChildNavs,
......@@ -133,6 +149,7 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
if plugin.Pinned {
appLink := &dtos.NavLink{
Text: plugin.Name,
Id: "plugin-page-" + plugin.Id,
Url: plugin.DefaultNavUrl,
Img: plugin.Info.Logos.Small,
}
......@@ -216,6 +233,7 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
},
{
Text: "API Keys",
Id: "apikeys",
Description: "Create & manage API keys",
Icon: "fa fa-fw fa-key",
Url: setting.AppSubUrl + "/org/apikeys",
......@@ -226,6 +244,7 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
if c.IsGrafanaAdmin {
cfgNode.Children = append(cfgNode.Children, &dtos.NavLink{
Text: "Server Admin",
Id: "admin",
Icon: "fa fa-fw fa-shield",
Url: setting.AppSubUrl + "/admin",
Children: []*dtos.NavLink{
......
......@@ -39,7 +39,6 @@
</ul>
</div>
<div class="sidemenu-item dropup dropdown" ng-if="::ctrl.isSignedIn">
<a class="sidemenu-link" href="profile">
<span class="icon-circle sidemenu-icon sidemenu-org-avatar">
......@@ -59,7 +58,7 @@
{{::orgitem.text}}
</a>
</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}}">
<i class="{{::menuItem.icon}}" ng-show="::menuItem.icon"></i>
{{::menuItem.text}}
......
......@@ -7,10 +7,9 @@ import coreModule from '../../core_module';
export class SideMenuCtrl {
isSignedIn: boolean;
showSignout: boolean;
user: any;
mainLinks: any;
userMenu: any;
profileNav: any;
appSubUrl: string;
loginUrl: string;
orgFilter: string;
......@@ -23,11 +22,9 @@ export class SideMenuCtrl {
this.isSignedIn = contextSrv.isSignedIn;
this.user = contextSrv.user;
this.appSubUrl = config.appSubUrl;
this.showSignout = this.contextSrv.isSignedIn && !config['disableSignoutMenu'];
this.maxShownOrgs = 10;
this.mainLinks = config.bootData.navTree;
this.openUserDropdown();
this.mainLinks = _.filter(config.bootData.navTree, item => item.id !== 'profile');
this.profileNav = _.find(config.bootData.navTree, {id: 'profile'});
this.loginUrl = 'login?redirect=' + encodeURIComponent(this.$location.path());
this.$scope.$on('$routeChangeSuccess', () => {
......@@ -49,13 +46,6 @@ export class SideMenuCtrl {
}
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')) {
// this.orgMenu.push({section: this.user.orgName, cssClass: 'dropdown-menu-title'});
......
......@@ -29,114 +29,29 @@ export class NavModelSrv {
return _.find(this.navItems, {id: 'cfg'});
}
getConfigurationNav() {
let cfg = this.getCfgNode();
return {
breadcrumbs: [cfg],
node: cfg,
};
}
getAlertingNav(subPage) {
return {
section: {
title: 'Alerting',
url: 'plugins',
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) {
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'},
]
};
}
getNav(...args) {
var children = this.navItems;
var nav = {breadcrumbs: [], node: null};
for (let id of args) {
let node = _.find(children, {id: id});
nav.breadcrumbs.push(node);
nav.node = node;
children = node.children;
}
getProfileNav() {
return {
section: {
title: 'User Profile',
url: 'profile',
icon: 'fa fa-fw fa-user'
},
menu: []
};
return nav;
}
getNotFoundNav() {
return {
section: {
title: 'Page',
url: '',
icon: 'fa fa-fw fa-warning'
},
menu: []
var node = {
text: "Page not found ",
icon: "fa fa-fw fa-warning",
};
}
getOrgNav(subPage) {
let cfg = this.getCfgNode();
let org = _.find(cfg.children, {id: 'org'});
return {
breadcrumbs: [this.getCfgNode(), org],
node: org
};
}
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,
breadcrumbs: [node],
node: node
};
}
......
......@@ -24,7 +24,7 @@ class AdminHomeCtrl {
/** @ngInject **/
constructor(navModelSrv) {
this.navModel = navModelSrv.getAdminNav();
this.navModel = navModelSrv.getNav('cfg', 'admin');
}
}
......@@ -47,7 +47,7 @@ export class ConfigurationHomeCtrl {
/** @ngInject */
constructor(private $scope, private backendSrv, private navModelSrv) {
this.navModel = navModelSrv.getConfigurationNav();
this.navModel = navModelSrv.getNav('cfg');
}
}
......
......@@ -23,7 +23,7 @@ export class AlertListCtrl {
/** @ngInject */
constructor(private backendSrv, private $location, private $scope, navModelSrv) {
this.navModel = navModelSrv.getAlertingNav(0);
this.navModel = navModelSrv.getNav('alerting');
var params = $location.search();
this.filters.state = params.state || null;
......
......@@ -25,7 +25,7 @@ export class AlertNotificationEditCtrl {
/** @ngInject */
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.notifiers = notifiers;
......@@ -36,10 +36,12 @@ export class AlertNotificationEditCtrl {
}
if (!this.$routeParams.id) {
this.navModel.breadcrumbs.push({text: 'New'});
return _.defaults(this.model, this.defaults);
}
return this.backendSrv.get(`/api/alert-notifications/${this.$routeParams.id}`).then(result => {
this.navModel.breadcrumbs.push({text: result.name});
return result;
});
}).then(model => {
......
......@@ -14,7 +14,7 @@ export class AlertNotificationsListCtrl {
/** @ngInject */
constructor(private backendSrv, private $scope, navModelSrv) {
this.loadNotifications();
this.navModel = navModelSrv.getAlertingNav(1);
this.navModel = navModelSrv.getNav('alerting', 'channels');
}
loadNotifications() {
......
......@@ -2,11 +2,18 @@
<div class="page-container" >
<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()">
<i class="fa fa-info-circle"></i>
How to add an alert
</a>
<a class="btn btn-inverse" href="alerting/notifications" >
<i class="fa fa-bell"></i>
Notification channels
</a>
</div>
<div class="gf-form-group">
......
......@@ -2,7 +2,11 @@
<div class="page-container" >
<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">
<i class="fa fa-plus"></i>
New Channel
......
......@@ -12,7 +12,7 @@ function (angular, config) {
$scope.command = {};
$scope.authProxyEnabled = config.authProxyEnabled;
$scope.ldapEnabled = config.ldapEnabled;
$scope.navModel = navModelSrv.getProfileNav();
$scope.navModel = navModelSrv.getNav('profile', 'change-password');
$scope.changePassword = function() {
if (!$scope.userForm.$valid) { return; }
......
......@@ -8,7 +8,7 @@ function (angular) {
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.token = { role: 'Viewer' };
......
......@@ -24,7 +24,7 @@ export class OrgUsersCtrl {
role: 'Viewer',
};
this.navModel = navModelSrv.getUserManNav(0);
this.navModel = navModelSrv.getNav('cfg', 'users');
this.get();
this.editor = { index: 0 };
......
......@@ -2,7 +2,10 @@
<div class="page-container">
<div class="page-header">
<h1>Change password</h1>
<h1>
<i class="{{navModel.node.icon}}"></i>
{{navModel.node.text}}
</h1>
</div>
<div ng-if="ldapEnabled || authProxyEnabled">
......
......@@ -2,7 +2,10 @@
<div class="page-container">
<div class="page-header">
<h1>User Profile</h1>
<h1>
<i class="{{ctrl.navModel.node.icon}}"></i>
{{ctrl.navModel.node.text}}
</h1>
</div>
<form name="ctrl.userForm" class="gf-form-group">
......
......@@ -17,7 +17,7 @@ export class ProfileCtrl {
constructor(private backendSrv, private contextSrv, private $location, navModelSrv) {
this.getUser();
this.getUserOrgs();
this.navModel = navModelSrv.getProfileNav();
this.navModel = navModelSrv.getNav('profile');
}
getUser() {
......
......@@ -43,7 +43,7 @@ export class DataSourceEditCtrl {
private navModelSrv,
) {
this.navModel = navModelSrv.getDatasourceNav(0);
this.navModel = navModelSrv.getNav('cfg', 'datasources');
this.datasources = [];
this.tabIndex = 0;
......
......@@ -17,7 +17,7 @@ export class DataSourcesCtrl {
private datasourceSrv,
private navModelSrv) {
this.navModel = this.navModelSrv.getDatasourceNav(0);
this.navModel = this.navModelSrv.getNav('cfg', 'datasources');
backendSrv.get('/api/datasources').then(result => {
this.datasources = result;
......
......@@ -28,7 +28,7 @@ export class PluginEditCtrl {
private $http,
private navModelSrv,
) {
this.navModel = navModelSrv.getPluginsNav();
this.navModel = navModelSrv.getNav('cfg', 'plugins');
this.model = {};
this.pluginId = $routeParams.pluginId;
this.tabIndex = 0;
......
......@@ -10,7 +10,7 @@ export class PluginListCtrl {
/** @ngInject */
constructor(private backendSrv: any, $location, navModelSrv) {
this.tabIndex = 0;
this.navModel = navModelSrv.getPluginsNav();
this.navModel = navModelSrv.getNav('cfg', 'plugins');
var pluginType = $location.search().type || 'panel';
switch (pluginType) {
......
......@@ -2,7 +2,6 @@
import angular from 'angular';
import _ from 'lodash';
import {NavModel} from 'app/core/core';
var pluginInfoCache = {};
......@@ -10,10 +9,10 @@ export class AppPageCtrl {
page: any;
pluginId: any;
appModel: any;
navModel: NavModel;
navModel: any;
/** @ngInject */
constructor(private backendSrv, private $routeParams: any, private $rootScope) {
constructor(private backendSrv, private $routeParams: any, private $rootScope, private navModelSrv) {
this.pluginId = $routeParams.pluginId;
if (pluginInfoCache[this.pluginId]) {
......@@ -32,47 +31,12 @@ export class AppPageCtrl {
if (!this.page) {
this.$rootScope.appEvent('alert-error', ['App Page Not Found', '']);
this.navModel = {
section: {
title: "Page not found",
url: app.defaultNavUrl,
icon: 'icon-gf icon-gf-sadface',
},
menu: [],
};
this.navModel = this.navModelSrv.getNotFoundNav();
return;
}
let menu = [];
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,
};
this.navModel = this.navModelSrv.getNav('plugin-page-' + app.id);
this.navModel.breadcrumbs.push({text: this.page.name});
}
loadPluginInfo() {
......
......@@ -54,7 +54,7 @@ $gf-form-margin: 0.1rem;
flex-shrink: 0;
font-weight: $font-weight-semi-bold;
background-color: #23252d; //$input-label-bg;
background-color: #292a2d; //$input-label-bg;
display: block;
font-size: $font-size-sm;
margin-right: $gf-form-margin;
......
.query-part {
background-color: $input-bg !important;
background-color: lighten($input-label-bg, 5%);
&.show-function-controls {
padding-top: 5px;
......
......@@ -49,7 +49,7 @@
}
.page-header {
padding: $spacer 0 0 0;
padding: 2rem 0 0 0;
margin-bottom: 2rem;
@include brand-bottom-border();
@include clearfix();
......@@ -58,7 +58,7 @@
font-size: $font-size-h2;
flex-grow: 1;
display: inline-block;
margin-bottom: $spacer*1.5;
margin-bottom: 2rem;
}
a, button {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment