소스 검색

ux: making org visibile in profile view

Torkel Ödegaard 8 년 전
부모
커밋
95f5c84a57

+ 134 - 0
'

@@ -0,0 +1,134 @@
+
+
+.sidemenu {
+  display: flex;
+  flex-flow: column;
+  flex-direction: column;
+  width: $side-menu-width;
+  background-color: $side-menu-bg;
+  z-index: 1;
+
+  a:focus {
+    text-decoration: none;
+  }
+}
+
+.sidemenu__top {
+  flex-grow: 1;
+}
+
+.sidemenu__bottom {
+  padding-bottom: $spacer;
+}
+
+.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 100ms ease-in-out 100ms 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;
+    //transform: translate3d(-5%,0,0);
+  }
+  100% {
+    opacity: 1;
+    //transform: translate3d(0,0,0);
+  }
+}
+
+.icon-circle {
+  width: 35px;
+  height: 35px;
+  display: inline-block;
+  i {
+    color: $link-color;
+    opacity: .7;
+    position: relative;
+    left: 3px;
+    font-size: 130%;
+  }
+
+  .fa {
+    top: 2px;
+  }
+
+  .icon-gf {
+    top: 5px;
+  }
+
+  img {
+    left: 3px;
+    position: relative;
+  }
+}
+
+.side-menu-header {
+  padding: 10px 10px 10px 20px;
+  white-space: nowrap;
+  background-color: $side-menu-item-hover-bg;
+  font-size: 17px;
+}
+
+li.sidemenu-org-switcher {
+  border-bottom: 1px solid $dropdownDividerBottom;
+}
+
+.sidemenu-org-switcher__org-name {
+  font-size: $font-size-base;
+}
+
+.sidemenu-org-switcher__org-current {
+  font-size: $font-size-xs;
+  color: $text-color-weak;
+}
+
+.sidemenu-org-switcher__switch {
+  font-size: $font-size-sm;
+  padding-left: 1.5rem;
+  display: flex;
+  align-items: center;
+  i.fa > {
+    margin-right: 5px;
+    top: 0;
+  }
+}

+ 1 - 0
pkg/api/dtos/models.go

@@ -27,6 +27,7 @@ type CurrentUser struct {
 	Email          string       `json:"email"`
 	Name           string       `json:"name"`
 	LightTheme     bool         `json:"lightTheme"`
+	OrgCount       int          `json:"orgCount"`
 	OrgId          int64        `json:"orgId"`
 	OrgName        string       `json:"orgName"`
 	OrgRole        m.RoleType   `json:"orgRole"`

+ 0 - 1
pkg/api/frontendsettings.go

@@ -141,7 +141,6 @@ func getFrontendSettingsMap(c *middleware.Context) (map[string]interface{}, erro
 		"alertingEnabled":         setting.AlertingEnabled,
 		"googleAnalyticsId":       setting.GoogleAnalyticsId,
 		"disableLoginForm":        setting.DisableLoginForm,
-		"disableSignoutMenu":      setting.DisableSignoutMenu,
 		"externalUserMngInfo":     setting.ExternalUserMngInfo,
 		"externalUserMngLinkUrl":  setting.ExternalUserMngLinkUrl,
 		"externalUserMngLinkName": setting.ExternalUserMngLinkName,

+ 35 - 17
pkg/api/index.go

@@ -50,6 +50,7 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
 			Login:          c.Login,
 			Email:          c.Email,
 			Name:           c.Name,
+			OrgCount:       c.OrgCount,
 			OrgId:          c.OrgId,
 			OrgName:        c.OrgName,
 			OrgRole:        c.OrgRole,
@@ -86,9 +87,9 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
 
 	if c.OrgRole == m.ROLE_ADMIN || c.OrgRole == m.ROLE_EDITOR {
 		data.NavTree = append(data.NavTree, &dtos.NavLink{
-			Text: "New",
+			Text: "Create",
 			Icon: "fa fa-fw fa-plus",
-			Url:  "",
+			Url:  "#",
 			Children: []*dtos.NavLink{
 				{Text: "Dashboard", Icon: "fa fa-fw fa-plus", Url: setting.AppSubUrl + "/dashboard/new"},
 				{Text: "Folder", Icon: "fa fa-fw fa-plus", Url: setting.AppSubUrl + "/dashboard/new/?editview=new-folder"},
@@ -112,17 +113,33 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
 	})
 
 	if c.IsSignedIn {
-		data.NavTree = append(data.NavTree, &dtos.NavLink{
+		profileNode := &dtos.NavLink{
 			Text:         c.SignedInUser.Login,
 			Id:           "profile",
 			Img:          data.User.GravatarUrl,
 			Url:          setting.AppSubUrl + "/profile",
 			HideFromMenu: true,
 			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.DisableSignoutMenu {
+			// add sign out first
+			profileNode.Children = append([]*dtos.NavLink{
+				{Text: "Sign out", Url: setting.AppSubUrl + "/logout", Icon: "fa fa-fw fa-sign-out", Target: "_self"},
+			}, profileNode.Children...)
+		}
+
+		data.NavTree = append(data.NavTree, profileNode)
+	} else {
+		data.NavTree = append(data.NavTree, &dtos.NavLink{
+			Text:         "Sign in",
+			Id:           "sign-in",
+			Icon:         "fa fa-fw fa-sign-in",
+			Url:          setting.AppSubUrl + "/login",
+			HideFromMenu: true,
 		})
 	}
 
@@ -226,7 +243,7 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
 					},
 				},
 				{
-					Text:        "User Management",
+					Text:        "Users",
 					Id:          "users",
 					Description: "Manage users & user groups",
 					Icon:        "fa fa-fw fa-users",
@@ -258,21 +275,22 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
 			})
 		}
 
-		data.NavTree = append(data.NavTree, &dtos.NavLink{
-			Text:         "Help",
-			Id:           "help",
-			Url:          "/help",
-			Icon:         "fa fa-fw fa-question",
-			HideFromMenu: true,
-			Children: []*dtos.NavLink{
-				{Text: "Shortcuts", Url: "/shortcuts", Icon: "fa fa-fw fa-keyboard-o", Target: "_self"},
-				{Text: "Community site", Url: "http://community.grafana.com", Icon: "fa fa-fw fa-comment", Target: "_blank"},
-				{Text: "Documentation", Url: "http://docs.grafana.org", Icon: "fa fa-fw fa-file", Target: "_blank"},
-			},
-		})
 		data.NavTree = append(data.NavTree, cfgNode)
 	}
 
+	data.NavTree = append(data.NavTree, &dtos.NavLink{
+		Text:         "Help",
+		Id:           "help",
+		Url:          "#",
+		Icon:         "fa fa-fw fa-question",
+		HideFromMenu: true,
+		Children: []*dtos.NavLink{
+			{Text: "Keyboard shortcuts", Url: "/shortcuts", Icon: "fa fa-fw fa-keyboard-o", Target: "_self"},
+			{Text: "Community site", Url: "http://community.grafana.com", Icon: "fa fa-fw fa-comment", Target: "_blank"},
+			{Text: "Documentation", Url: "http://docs.grafana.org", Icon: "fa fa-fw fa-file", Target: "_blank"},
+		},
+	})
+
 	return &data, nil
 }
 

+ 1 - 0
pkg/models/user.go

@@ -160,6 +160,7 @@ type SignedInUser struct {
 	Name           string
 	Email          string
 	ApiKeyId       int64
+	OrgCount       int
 	IsGrafanaAdmin bool
 	HelpFlags1     HelpFlags1
 	LastSeenAt     time.Time

+ 1 - 0
pkg/services/sqlstore/user.go

@@ -350,6 +350,7 @@ func GetSignedInUser(query *m.GetSignedInUserQuery) error {
 		u.name           as name,
 		u.help_flags1    as help_flags1,
 		u.last_seen_at   as last_seen_at,
+		(SELECT COUNT(*) FROM org_user where org_user.user_id = u.id) as org_count,
 		org.name         as org_name,
 		org_user.role    as org_role,
 		org.id           as org_id

+ 0 - 1
public/app/core/components/navbar/navbar.ts

@@ -11,7 +11,6 @@ export class NavbarCtrl {
 
   /** @ngInject */
   constructor(private $scope, private $rootScope, private contextSrv) {
-    console.log(this.model);
   }
 
   showSearch() {

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

@@ -36,7 +36,16 @@
         <img ng-src="{{::item.img}}" ng-show="::item.img">
       </span>
     </a>
-    <ul class="dropdown-menu dropdown-menu--sidemenu" role="menu" ng-if="::item.children">
+    <ul class="dropdown-menu dropdown-menu--sidemenu" role="menu">
+			<li ng-if="item.showOrgSwitcher" class="sidemenu-org-switcher">
+				<a ng-click="ctrl.switchOrg()">
+					<div>
+						<div class="sidemenu-org-switcher__org-name">{{ctrl.contextSrv.user.orgName}}</div>
+						<div class="sidemenu-org-switcher__org-current">Current Org:</div>
+					</div>
+					<div class="sidemenu-org-switcher__switch"><i class="fa fa-fw fa-random"></i>Switch</div>
+				</a>
+			</li>
       <li ng-repeat="child in ::item.children" ng-class="{divider: child.divider}" ng-hide="::child.hideFromMenu">
         <a href="{{::child.url}}" target="{{::child.target}}">
           <i class="{{::child.icon}}" ng-show="::child.icon"></i>

+ 7 - 56
public/app/core/components/sidemenu/sidemenu.ts

@@ -6,7 +6,6 @@ import $ from 'jquery';
 import coreModule from '../../core_module';
 
 export class SideMenuCtrl {
-  isSignedIn: boolean;
   user: any;
   mainLinks: any;
   bottomNav: any;
@@ -19,7 +18,6 @@ export class SideMenuCtrl {
 
   /** @ngInject */
   constructor(private $scope, private $rootScope, private $location, private contextSrv, private backendSrv, private $element) {
-    this.isSignedIn = contextSrv.isSignedIn;
     this.user = contextSrv.user;
     this.appSubUrl = config.appSubUrl;
     this.maxShownOrgs = 10;
@@ -27,6 +25,13 @@ export class SideMenuCtrl {
     this.bottomNav = _.filter(config.bootData.navTree, item => item.hideFromMenu);
     this.loginUrl = 'login?redirect=' + encodeURIComponent(this.$location.path());
 
+    if (contextSrv.user.orgCount > 1) {
+      let profileNode = _.find(this.bottomNav, {id: 'profile'});
+      if (profileNode) {
+        profileNode.showOrgSwitcher = true;
+      }
+    }
+
     this.$scope.$on('$routeChangeSuccess', () => {
       if (!this.contextSrv.pinned) {
         this.contextSrv.sidemenu = false;
@@ -44,60 +49,6 @@ export class SideMenuCtrl {
  search() {
    this.$rootScope.appEvent('show-dash-search');
  }
-
- openUserDropdown() {
-
-   // 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(){
-   this.orgItems = [];
-   this.orgs.forEach(org => {
-     if (org.orgId === this.contextSrv.user.orgId) {
-       return;
-     }
-
-     if (this.orgItems.length === this.maxShownOrgs) {
-       return;
-     }
-
-     if (this.orgFilter === '' || (org.name.toLowerCase().indexOf(this.orgFilter.toLowerCase()) !== -1)) {
-       this.orgItems.push({
-         text: "Switch to " + org.name,
-         icon: "fa fa-fw fa-random",
-         url: this.getUrl('/profile/switch-org/' + org.orgId),
-         target: '_self'
-       });
-     }
-   });
-   if (config.allowOrgCreate) {
-     this.orgItems.push({text: "New organization", icon: "fa fa-fw fa-plus", url: this.getUrl('/org/new')});
-   }
- }
 }
 
 export function sideMenuDirective() {

+ 0 - 1
public/app/features/admin/partials/configuration_home.html

@@ -6,7 +6,6 @@
 		</div>
 
 		<section class="card-section card-list-layout-grid">
-
 			<ol class="card-list" >
 				<li class="card-item-wrapper" ng-repeat="navItem in ctrl.navModel.node.children">
 					<a class="card-item" ng-href="{{::navItem.url}}">

+ 8 - 0
public/sass/components/_dropdown.scss

@@ -120,6 +120,14 @@
       }
     }
   }
+
+  &--sidemenu {
+    li.sidemenu-org-switcher {
+      > a {
+        padding: 8px 10px 8px 15px;
+      }
+    }
+  }
 }
 
 .dropdown-item-text {

+ 22 - 24
public/sass/components/_sidemenu.scss

@@ -38,7 +38,7 @@
       // 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');
+      @include animation('dropdown-anim 100ms ease-in-out 100ms forwards');
       z-index: 1;
     }
   }
@@ -68,11 +68,11 @@
 @include keyframes(dropdown-anim) {
   0% {
     opacity: 0;
-    transform: translate3d(-5%,0,0);
+    //transform: translate3d(-5%,0,0);
   }
   100% {
     opacity: 1;
-    transform: translate3d(0,0,0);
+    //transform: translate3d(0,0,0);
   }
 }
 
@@ -109,30 +109,28 @@
   font-size: 17px;
 }
 
-.sidemenu .fa-caret-right {
-  position: absolute;
-  top: 38%;
-  right: 6px;
-  font-size: 14px;
-  color: $text-color-faint;
+li.sidemenu-org-switcher {
+  border-bottom: 1px solid $dropdownDividerBottom;
 }
 
-.sidemenu-org-avatar {
-  >img {
-    position: absolute;
-    width: 30px;
-    height: 30px;
-    border-radius: 50%;
-    left: 14px;
-    top: 6px;
-    z-index: 10;
-  }
+.sidemenu-org-switcher__org-name {
+  font-size: $font-size-base;
 }
 
-.sidemenu-org-avatar--missing {
-  color: $gray-4;
-  text-shadow: 0 1px 0 $dark-1;
-  line-height: 28px;
-  font-size: $font-size-lg;
+.sidemenu-org-switcher__org-current {
+  font-size: $font-size-xs;
+  color: $text-color-weak;
+  position: relative;
+  top: -2px;
 }
 
+.sidemenu-org-switcher__switch {
+  font-size: $font-size-sm;
+  padding-left: 1.5rem;
+  display: flex;
+  align-items: center;
+  > i.fa.fa-random {
+    margin-right: 4px;
+    top: 1px;
+  }
+}