From 63424f9866df40bdd0643ac8be9a9a6366a8afdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 15 Aug 2017 14:49:12 +0200 Subject: [PATCH] ux: more work on sidemenu --- pkg/api/api.go | 1 + pkg/api/avatar/avatar.go | 2 +- pkg/api/index.go | 71 ++++---- .../core/components/sidemenu/sidemenu.html | 152 +++++++++-------- .../app/core/components/sidemenu/sidemenu.ts | 59 ++++--- public/app/core/routes/routes.ts | 6 + public/app/features/admin/admin.ts | 11 ++ .../admin/partials/configuration_home.html | 48 ++++++ public/sass/_variables.scss | 2 +- public/sass/components/_dropdown.scss | 2 +- public/sass/components/_navbar.scss | 2 +- public/sass/components/_scrollbar.scss | 4 +- public/sass/components/_sidemenu.scss | 156 +++++++----------- public/views/index.html | 2 +- 14 files changed, 280 insertions(+), 238 deletions(-) create mode 100644 public/app/features/admin/partials/configuration_home.html diff --git a/pkg/api/api.go b/pkg/api/api.go index c91779cb193..f16f85e7c8f 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -41,6 +41,7 @@ func (hs *HttpServer) registerRoutes() { r.Get("/org/users/", reqSignedIn, Index) r.Get("/org/apikeys/", reqSignedIn, Index) r.Get("/dashboard/import/", reqSignedIn, Index) + r.Get("/configuration", reqGrafanaAdmin, Index) r.Get("/admin", reqGrafanaAdmin, Index) r.Get("/admin/settings", reqGrafanaAdmin, Index) r.Get("/admin/users", reqGrafanaAdmin, Index) diff --git a/pkg/api/avatar/avatar.go b/pkg/api/avatar/avatar.go index 41ce857db4f..69926bce492 100644 --- a/pkg/api/avatar/avatar.go +++ b/pkg/api/avatar/avatar.go @@ -146,7 +146,7 @@ func CacheServer() http.Handler { } func newNotFound() *Avatar { - avatar := &Avatar{} + avatar := &Avatar{notFound: true} // load transparent png into buffer path := filepath.Join(setting.StaticRootPath, "img", "transparent.png") diff --git a/pkg/api/index.go b/pkg/api/index.go index db8e51db9af..3afff3bb144 100644 --- a/pkg/api/index.go +++ b/pkg/api/index.go @@ -124,28 +124,6 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) { }) } - if c.OrgRole == m.ROLE_ADMIN { - data.MainNavLinks = append(data.MainNavLinks, &dtos.NavLink{ - Text: "Data Sources", - Icon: "icon-gf icon-gf-datasources", - Url: setting.AppSubUrl + "/datasources", - Children: []*dtos.NavLink{ - {Text: "List", Url: setting.AppSubUrl + "/datasources", Icon: "icon-gf icon-gf-datasources"}, - {Text: "New", Url: setting.AppSubUrl + "/datasources", Icon: "fa fa-fw fa-plus"}, - }, - }) - data.MainNavLinks = append(data.MainNavLinks, &dtos.NavLink{ - Text: "Plugins", - Icon: "icon-gf icon-gf-apps", - Url: setting.AppSubUrl + "/plugins", - Children: []*dtos.NavLink{ - {Text: "Panels", Url: setting.AppSubUrl + "/plugins?type=panel", Icon: "fa fa-fw fa-stop"}, - {Text: "Data sources", Url: setting.AppSubUrl + "/plugins?type=datasource", Icon: "icon-gf icon-gf-datasources"}, - {Text: "Apps", Url: setting.AppSubUrl + "/plugins?type=app", Icon: "icon-gf icon-gf-apps"}, - }, - }) - } - enabledPlugins, err := plugins.GetEnabledPlugins(c.OrgId) if err != nil { return nil, err @@ -192,18 +170,49 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) { } } - if c.IsGrafanaAdmin { - data.MainNavLinks = append(data.MainNavLinks, &dtos.NavLink{ - Text: "Admin", + if c.OrgRole == m.ROLE_ADMIN { + cfgNode := &dtos.NavLink{ + Text: "Configuration", Icon: "fa fa-fw fa-cogs", - Url: setting.AppSubUrl + "/admin", + Url: setting.AppSubUrl + "/configuration", Children: []*dtos.NavLink{ - {Text: "Global Users", Url: setting.AppSubUrl + "/admin/users"}, - {Text: "Global Orgs", Url: setting.AppSubUrl + "/admin/orgs"}, - {Text: "Server Settings", Url: setting.AppSubUrl + "/admin/settings"}, - {Text: "Server Stats", Url: setting.AppSubUrl + "/admin/stats"}, + { + Text: "Data Sources", + Icon: "icon-gf icon-gf-datasources", + Url: setting.AppSubUrl + "/datasources", + Children: []*dtos.NavLink{ + {Text: "List", Url: setting.AppSubUrl + "/datasources", Icon: "icon-gf icon-gf-datasources"}, + {Text: "New", Url: setting.AppSubUrl + "/datasources", Icon: "fa fa-fw fa-plus"}, + }, + }, + { + Text: "Plugins", + Icon: "icon-gf icon-gf-apps", + Url: setting.AppSubUrl + "/plugins", + Children: []*dtos.NavLink{ + {Text: "Panels", Url: setting.AppSubUrl + "/plugins?type=panel", Icon: "fa fa-fw fa-stop"}, + {Text: "Data sources", Url: setting.AppSubUrl + "/plugins?type=datasource", Icon: "icon-gf icon-gf-datasources"}, + {Text: "Apps", Url: setting.AppSubUrl + "/plugins?type=app", Icon: "icon-gf icon-gf-apps"}, + }, + }, }, - }) + } + + if c.IsGrafanaAdmin { + cfgNode.Children = append(cfgNode.Children, &dtos.NavLink{ + Text: "Server Admin", + Icon: "fa fa-fw fa-shield", + Url: setting.AppSubUrl + "/admin", + Children: []*dtos.NavLink{ + {Text: "Global Users", Url: setting.AppSubUrl + "/admin/users"}, + {Text: "Global Orgs", Url: setting.AppSubUrl + "/admin/orgs"}, + {Text: "Server Settings", Url: setting.AppSubUrl + "/admin/settings"}, + {Text: "Server Stats", Url: setting.AppSubUrl + "/admin/stats"}, + }, + }) + } + + data.MainNavLinks = append(data.MainNavLinks, cfgNode) } return &data, nil diff --git a/public/app/core/components/sidemenu/sidemenu.html b/public/app/core/components/sidemenu/sidemenu.html index bc22d6cfb4e..55611dab103 100644 --- a/public/app/core/components/sidemenu/sidemenu.html +++ b/public/app/core/components/sidemenu/sidemenu.html @@ -1,77 +1,89 @@ - - - - - +
-
-
  • - - - - - - - - - -
  • +
    + - + + + +
    + + + + + +
    +
    diff --git a/public/app/core/components/sidemenu/sidemenu.ts b/public/app/core/components/sidemenu/sidemenu.ts index 923b080ed92..e371429dc91 100644 --- a/public/app/core/components/sidemenu/sidemenu.ts +++ b/public/app/core/components/sidemenu/sidemenu.ts @@ -10,7 +10,7 @@ export class SideMenuCtrl { showSignout: boolean; user: any; mainLinks: any; - orgMenu: any; + userMenu: any; appSubUrl: string; loginUrl: string; orgFilter: string; @@ -49,40 +49,39 @@ export class SideMenuCtrl { } openUserDropdown() { - this.orgMenu = [ - {section: 'You', cssClass: 'dropdown-menu-title'}, - {text: 'Profile', url: this.getUrl('/profile')}, - ]; + this.userMenu = [ ]; if (this.showSignout) { - this.orgMenu.push({text: "Sign out", url: this.getUrl("/logout"), target: "_self"}); + this.userMenu.push({text: "Sign out", url: this.getUrl("/logout"), target: "_self", icon: 'fa fa-sign-out'}); } - if (this.contextSrv.hasRole('Admin')) { - this.orgMenu.push({section: this.user.orgName, cssClass: 'dropdown-menu-title'}); - this.orgMenu.push({ - text: "Preferences", - url: this.getUrl("/org") - }); - this.orgMenu.push({ - text: "Users", - url: this.getUrl("/org/users") - }); - this.orgMenu.push({ - text: "User Groups", - url: this.getUrl("/org/user-groups") - }); - this.orgMenu.push({ - text: "API Keys", - url: this.getUrl("/org/apikeys") - }); - } + this.userMenu.push({text: 'Profile', url: this.getUrl('/profile'), icon: 'fa fa-user'}); - this.orgMenu.push({cssClass: "divider"}); - this.backendSrv.get('/api/user/orgs').then(orgs => { - this.orgs = orgs; - this.loadOrgsItems(); - }); + // if (this.contextSrv.hasRole('Admin')) { + // this.orgMenu.push({section: this.user.orgName, cssClass: 'dropdown-menu-title'}); + // this.orgMenu.push({ + // text: "Preferences", + // url: this.getUrl("/org") + // }); + // this.orgMenu.push({ + // text: "Users", + // url: this.getUrl("/org/users") + // }); + // this.orgMenu.push({ + // text: "User Groups", + // url: this.getUrl("/org/user-groups") + // }); + // this.orgMenu.push({ + // text: "API Keys", + // url: this.getUrl("/org/apikeys") + // }); + // } + + // this.orgMenu.push({cssClass: "divider"}); + // this.backendSrv.get('/api/user/orgs').then(orgs => { + // this.orgs = orgs; + // this.loadOrgsItems(); + // }); } loadOrgsItems(){ diff --git a/public/app/core/routes/routes.ts b/public/app/core/routes/routes.ts index 0bf0a03bf7a..8999df32d7f 100644 --- a/public/app/core/routes/routes.ts +++ b/public/app/core/routes/routes.ts @@ -44,6 +44,12 @@ function setupAngularRoutes($routeProvider, $locationProvider) { templateUrl: 'public/app/features/dashboard/partials/dash_list.html', controller : 'DashListCtrl', }) + .when('/configuration', { + templateUrl: 'public/app/features/admin/partials/configuration_home.html', + controller : 'ConfigurationHomeCtrl', + controllerAs: 'ctrl', + resolve: loadAdminBundle, + }) .when('/datasources', { templateUrl: 'public/app/features/plugins/partials/ds_list.html', controller : 'DataSourcesCtrl', diff --git a/public/app/features/admin/admin.ts b/public/app/features/admin/admin.ts index 669e4a9c982..75af2f41506 100644 --- a/public/app/features/admin/admin.ts +++ b/public/app/features/admin/admin.ts @@ -2,6 +2,7 @@ import AdminListUsersCtrl from './admin_list_users_ctrl'; import './adminListOrgsCtrl'; import './adminEditOrgCtrl'; import './adminEditUserCtrl'; +import './configuration_home_ctrl'; import coreModule from 'app/core/core_module'; @@ -42,6 +43,16 @@ export class AdminStatsCtrl { } } +export class ConfigurationHomeCtrl { + navModel: any; + + /** @ngInject */ + constructor(private $scope, private backendSrv, private navModelSrv) { + this.navModel = navModelSrv.getAdminNav(); + } +} + +coreModule.controller('ConfigurationHomeCtrl', ConfigurationHomeCtrl); coreModule.controller('AdminSettingsCtrl', AdminSettingsCtrl); coreModule.controller('AdminHomeCtrl', AdminHomeCtrl); coreModule.controller('AdminStatsCtrl', AdminStatsCtrl); diff --git a/public/app/features/admin/partials/configuration_home.html b/public/app/features/admin/partials/configuration_home.html new file mode 100644 index 00000000000..6008ca6bda9 --- /dev/null +++ b/public/app/features/admin/partials/configuration_home.html @@ -0,0 +1,48 @@ + + +
    +
    + + +
    + + +
      +
    1. + +
      +
      + {{ds.type}} +
      +
      +
      +
      + +
      +
      +
      + {{ds.name}} + + default + +
      +
      + {{ds.url}} +
      +
      +
      +
      +
    2. +
    +
    + +
    + No data sources defined +
    +
    +
    diff --git a/public/sass/_variables.scss b/public/sass/_variables.scss index c80bbf9148f..204fcc5a8b1 100644 --- a/public/sass/_variables.scss +++ b/public/sass/_variables.scss @@ -219,7 +219,7 @@ $btn-padding-y-lg: .75rem !default; $btn-border-radius: 2px; // sidemenu -$side-menu-width: 68px; +$side-menu-width: 60px; // dashboard $panel-margin: 0.4rem; diff --git a/public/sass/components/_dropdown.scss b/public/sass/components/_dropdown.scss index 8dee2bcc2b9..3e4ed5cfd09 100644 --- a/public/sass/components/_dropdown.scss +++ b/public/sass/components/_dropdown.scss @@ -217,7 +217,7 @@ // Different positioning for bottom up menu .dropdown-menu { top: auto; - bottom: 100%; + bottom: 0; margin-bottom: 1px; } } diff --git a/public/sass/components/_navbar.scss b/public/sass/components/_navbar.scss index 357b2be3ce0..c4cb51cf6a4 100644 --- a/public/sass/components/_navbar.scss +++ b/public/sass/components/_navbar.scss @@ -59,7 +59,7 @@ display: block; margin-bottom: 3rem; background-color: $side-menu-bg; - padding: 0.4rem 1.0rem 0.4rem 1rem; + padding: 0.4rem 1.0rem 0.4rem 0.65rem; min-height: $navbarHeight; .fa-caret-down { diff --git a/public/sass/components/_scrollbar.scss b/public/sass/components/_scrollbar.scss index b16acb75475..78cbd6cb18a 100644 --- a/public/sass/components/_scrollbar.scss +++ b/public/sass/components/_scrollbar.scss @@ -107,9 +107,9 @@ } -// // Srollbars -// // +// Srollbars // + // ::-webkit-scrollbar { // width: 8px; // height: 8px; diff --git a/public/sass/components/_sidemenu.scss b/public/sass/components/_sidemenu.scss index 65e3e8d52da..8fb6845881f 100644 --- a/public/sass/components/_sidemenu.scss +++ b/public/sass/components/_sidemenu.scss @@ -12,34 +12,62 @@ } .sidemenu { - list-style: none; - margin: 0; - padding: 0; + display: flex; + flex-flow: column; +} - > li { - position: relative; - @include left-brand-border(); +.sidemenu__top { + flex-grow: 1; +} - &.active, - &:hover { - background-color: $side-menu-item-hover-bg; - @include left-brand-border-gradient(); +.sidemenu__bottom { + padding-bottom: $spacer; +} - .dropdown-menu { - margin: 0; - display: block; - opacity: 0; - top: 0px; - // important to overlap it otherwise it can be hidden - // again by the mouse getting outside the hover space - left: $side-menu-width - 2px; - @include animation('dropdown-anim 0ms ease-in-out 0ms forwards'); - z-index: -9999; - } +.sidemenu-item { + position: relative; + @include left-brand-border(); + + &.active, + &:hover { + background-color: $side-menu-item-hover-bg; + @include left-brand-border-gradient(); + + .dropdown-menu { + margin: 0; + display: block; + opacity: 0; + top: 0px; + // important to overlap it otherwise it can be hidden + // again by the mouse getting outside the hover space + left: $side-menu-width - 2px; + @include animation('dropdown-anim 0ms ease-in-out 0ms forwards'); + z-index: 1; } } } +.dropup.sidemenu-item:hover .dropdown-menu { + top: auto !important; +} + +.sidemenu-link { + color: $link-color; + line-height: 42px; + padding: 0px 10px 0px 10px; + display: block; + position: relative; + font-size: 16px; + border: 1px solid transparent; + + img { + border-radius: 50%; + width: 28px; + height: 28px; + box-shadow: 0 0 14px 2px rgba(255,255,255, 0.05); + } +} + @include keyframes(dropdown-anim) { 0% { opacity: 0; @@ -59,7 +87,7 @@ color: $link-color; opacity: .7; position: relative; - left: 7px; + left: 3px; font-size: 130%; } @@ -72,67 +100,17 @@ } img { - left: 7px; + left: 3px; position: relative; } } -.sidemenu-item { - color: $link-color; - line-height: 42px; - padding: 0px 10px 0px 10px; - display: block; - position: relative; - font-size: 16px; - border: 1px solid transparent; - - img { - border-radius: 50%; - width: 28px; - height: 28px; - box-shadow: 0 0 14px 2px rgba(255,255,255, 0.05); - } -} - .side-menu-header { padding: 10px 10px 10px 20px; white-space: nowrap; background-color: $side-menu-item-hover-bg; - font-size: 18px; -} - -.sidemenu-org-section .dropdown-menu-title { - margin: 0 10px 0 6px; - padding: 7px 0 7px; - overflow: hidden; - color: $dropdownTitle; -} - -.sidemenu-org-section .dropdown-menu-title > span { - display: inline-block; - position: relative; - - &::after { - display: block; - position: absolute; - top: 50%; - right: 0; - left: 100%; - width: 200px; - height: 1px; - margin-left: 5px; - background: $dropdownDivider; - content: ''; - } -} - -.sidemenu-org { - padding: 17px 10px 15px 14px; - box-sizing: border-box; - cursor: pointer; - display: table; - position: relative; - width: 100%; + font-size: 17px; + font-weight: normal; } .sidemenu .fa-caret-right { @@ -143,20 +121,14 @@ color: $text-color-faint; } -.sidemenu-org-avatar, -.sidemenu-org-details { - display: table-cell; - vertical-align: top; -} - .sidemenu-org-avatar { >img { position: absolute; - width: 35px; - height: 35px; + width: 30px; + height: 30px; border-radius: 50%; left: 14px; - top: 12px; + top: 6px; z-index: 10; } } @@ -168,19 +140,3 @@ font-size: $font-size-lg; } -.sidemenu-org-details { - padding-left: 10px; - color: $link-color; - display: none; -} - -.sidemenu-org-name { - display: block; - font-size: 13px; - color: $link-color-disabled; -} - -.sidemenu-org-user { - display: block; -} - diff --git a/public/views/index.html b/public/views/index.html index ad47baa3465..7fa2f0fa1e2 100644 --- a/public/views/index.html +++ b/public/views/index.html @@ -25,7 +25,7 @@ - +