Explorar o código

ux: more nav work

Torkel Ödegaard %!s(int64=8) %!d(string=hai) anos
pai
achega
0bc226d760

+ 10 - 8
pkg/api/dtos/index.go

@@ -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"`
 }

+ 21 - 2
pkg/api/index.go

@@ -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{

+ 1 - 2
public/app/core/components/sidemenu/sidemenu.html

@@ -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}}

+ 3 - 13
public/app/core/components/sidemenu/sidemenu.ts

@@ -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'});

+ 16 - 101
public/app/core/nav_model_srv.ts

@@ -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
     };
   }
 

+ 2 - 2
public/app/features/admin/admin.ts

@@ -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');
   }
 }
 

+ 1 - 1
public/app/features/alerting/alert_list_ctrl.ts

@@ -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;

+ 3 - 1
public/app/features/alerting/notification_edit_ctrl.ts

@@ -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 => {

+ 1 - 1
public/app/features/alerting/notifications_list_ctrl.ts

@@ -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() {

+ 8 - 1
public/app/features/alerting/partials/alert_list.html

@@ -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">

+ 5 - 1
public/app/features/alerting/partials/notifications_list.html

@@ -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

+ 1 - 1
public/app/features/org/change_password_ctrl.js

@@ -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; }

+ 1 - 1
public/app/features/org/orgApiKeysCtrl.js

@@ -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' };
 

+ 1 - 1
public/app/features/org/org_users_ctrl.ts

@@ -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 };

+ 4 - 1
public/app/features/org/partials/change_password.html

@@ -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">

+ 4 - 1
public/app/features/org/partials/profile.html

@@ -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">

+ 1 - 1
public/app/features/org/profile_ctrl.ts

@@ -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() {

+ 1 - 1
public/app/features/plugins/ds_edit_ctrl.ts

@@ -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;
 

+ 1 - 1
public/app/features/plugins/ds_list_ctrl.ts

@@ -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;

+ 1 - 1
public/app/features/plugins/plugin_edit_ctrl.ts

@@ -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;

+ 1 - 1
public/app/features/plugins/plugin_list_ctrl.ts

@@ -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) {

+ 5 - 41
public/app/features/plugins/plugin_page_ctrl.ts

@@ -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() {

+ 1 - 1
public/sass/components/_gf-form.scss

@@ -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;

+ 1 - 1
public/sass/components/_query_part.scss

@@ -1,6 +1,6 @@
 
 .query-part {
-  background-color: $input-bg !important;
+  background-color: lighten($input-label-bg, 5%);
 
   &.show-function-controls {
     padding-top: 5px;

+ 2 - 2
public/sass/layout/_page.scss

@@ -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 {