Commit 95f5c84a by Torkel Ödegaard

ux: making org visibile in profile view

parent 7f0f0eb6
.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;
}
}
...@@ -27,6 +27,7 @@ type CurrentUser struct { ...@@ -27,6 +27,7 @@ type CurrentUser struct {
Email string `json:"email"` Email string `json:"email"`
Name string `json:"name"` Name string `json:"name"`
LightTheme bool `json:"lightTheme"` LightTheme bool `json:"lightTheme"`
OrgCount int `json:"orgCount"`
OrgId int64 `json:"orgId"` OrgId int64 `json:"orgId"`
OrgName string `json:"orgName"` OrgName string `json:"orgName"`
OrgRole m.RoleType `json:"orgRole"` OrgRole m.RoleType `json:"orgRole"`
......
...@@ -141,7 +141,6 @@ func getFrontendSettingsMap(c *middleware.Context) (map[string]interface{}, erro ...@@ -141,7 +141,6 @@ func getFrontendSettingsMap(c *middleware.Context) (map[string]interface{}, erro
"alertingEnabled": setting.AlertingEnabled, "alertingEnabled": setting.AlertingEnabled,
"googleAnalyticsId": setting.GoogleAnalyticsId, "googleAnalyticsId": setting.GoogleAnalyticsId,
"disableLoginForm": setting.DisableLoginForm, "disableLoginForm": setting.DisableLoginForm,
"disableSignoutMenu": setting.DisableSignoutMenu,
"externalUserMngInfo": setting.ExternalUserMngInfo, "externalUserMngInfo": setting.ExternalUserMngInfo,
"externalUserMngLinkUrl": setting.ExternalUserMngLinkUrl, "externalUserMngLinkUrl": setting.ExternalUserMngLinkUrl,
"externalUserMngLinkName": setting.ExternalUserMngLinkName, "externalUserMngLinkName": setting.ExternalUserMngLinkName,
......
...@@ -50,6 +50,7 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) { ...@@ -50,6 +50,7 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
Login: c.Login, Login: c.Login,
Email: c.Email, Email: c.Email,
Name: c.Name, Name: c.Name,
OrgCount: c.OrgCount,
OrgId: c.OrgId, OrgId: c.OrgId,
OrgName: c.OrgName, OrgName: c.OrgName,
OrgRole: c.OrgRole, OrgRole: c.OrgRole,
...@@ -86,9 +87,9 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) { ...@@ -86,9 +87,9 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
if c.OrgRole == m.ROLE_ADMIN || c.OrgRole == m.ROLE_EDITOR { if c.OrgRole == m.ROLE_ADMIN || c.OrgRole == m.ROLE_EDITOR {
data.NavTree = append(data.NavTree, &dtos.NavLink{ data.NavTree = append(data.NavTree, &dtos.NavLink{
Text: "New", Text: "Create",
Icon: "fa fa-fw fa-plus", Icon: "fa fa-fw fa-plus",
Url: "", Url: "#",
Children: []*dtos.NavLink{ Children: []*dtos.NavLink{
{Text: "Dashboard", Icon: "fa fa-fw fa-plus", Url: setting.AppSubUrl + "/dashboard/new"}, {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"}, {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) { ...@@ -112,17 +113,33 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
}) })
if c.IsSignedIn { if c.IsSignedIn {
data.NavTree = append(data.NavTree, &dtos.NavLink{ profileNode := &dtos.NavLink{
Text: c.SignedInUser.Login, Text: c.SignedInUser.Login,
Id: "profile", Id: "profile",
Img: data.User.GravatarUrl, Img: data.User.GravatarUrl,
Url: setting.AppSubUrl + "/profile", Url: setting.AppSubUrl + "/profile",
HideFromMenu: true, HideFromMenu: true,
Children: []*dtos.NavLink{ 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: "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}, {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) { ...@@ -226,7 +243,7 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
}, },
}, },
{ {
Text: "User Management", Text: "Users",
Id: "users", Id: "users",
Description: "Manage users & user groups", Description: "Manage users & user groups",
Icon: "fa fa-fw fa-users", Icon: "fa fa-fw fa-users",
...@@ -258,21 +275,22 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) { ...@@ -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, 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 return &data, nil
} }
......
...@@ -160,6 +160,7 @@ type SignedInUser struct { ...@@ -160,6 +160,7 @@ type SignedInUser struct {
Name string Name string
Email string Email string
ApiKeyId int64 ApiKeyId int64
OrgCount int
IsGrafanaAdmin bool IsGrafanaAdmin bool
HelpFlags1 HelpFlags1 HelpFlags1 HelpFlags1
LastSeenAt time.Time LastSeenAt time.Time
......
...@@ -350,6 +350,7 @@ func GetSignedInUser(query *m.GetSignedInUserQuery) error { ...@@ -350,6 +350,7 @@ func GetSignedInUser(query *m.GetSignedInUserQuery) error {
u.name as name, u.name as name,
u.help_flags1 as help_flags1, u.help_flags1 as help_flags1,
u.last_seen_at as last_seen_at, 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.name as org_name,
org_user.role as org_role, org_user.role as org_role,
org.id as org_id org.id as org_id
......
...@@ -11,7 +11,6 @@ export class NavbarCtrl { ...@@ -11,7 +11,6 @@ export class NavbarCtrl {
/** @ngInject */ /** @ngInject */
constructor(private $scope, private $rootScope, private contextSrv) { constructor(private $scope, private $rootScope, private contextSrv) {
console.log(this.model);
} }
showSearch() { showSearch() {
......
...@@ -36,7 +36,16 @@ ...@@ -36,7 +36,16 @@
<img ng-src="{{::item.img}}" ng-show="::item.img"> <img ng-src="{{::item.img}}" ng-show="::item.img">
</span> </span>
</a> </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"> <li ng-repeat="child in ::item.children" ng-class="{divider: child.divider}" ng-hide="::child.hideFromMenu">
<a href="{{::child.url}}" target="{{::child.target}}"> <a href="{{::child.url}}" target="{{::child.target}}">
<i class="{{::child.icon}}" ng-show="::child.icon"></i> <i class="{{::child.icon}}" ng-show="::child.icon"></i>
......
...@@ -6,7 +6,6 @@ import $ from 'jquery'; ...@@ -6,7 +6,6 @@ import $ from 'jquery';
import coreModule from '../../core_module'; import coreModule from '../../core_module';
export class SideMenuCtrl { export class SideMenuCtrl {
isSignedIn: boolean;
user: any; user: any;
mainLinks: any; mainLinks: any;
bottomNav: any; bottomNav: any;
...@@ -19,7 +18,6 @@ export class SideMenuCtrl { ...@@ -19,7 +18,6 @@ export class SideMenuCtrl {
/** @ngInject */ /** @ngInject */
constructor(private $scope, private $rootScope, private $location, private contextSrv, private backendSrv, private $element) { constructor(private $scope, private $rootScope, private $location, private contextSrv, private backendSrv, private $element) {
this.isSignedIn = contextSrv.isSignedIn;
this.user = contextSrv.user; this.user = contextSrv.user;
this.appSubUrl = config.appSubUrl; this.appSubUrl = config.appSubUrl;
this.maxShownOrgs = 10; this.maxShownOrgs = 10;
...@@ -27,6 +25,13 @@ export class SideMenuCtrl { ...@@ -27,6 +25,13 @@ export class SideMenuCtrl {
this.bottomNav = _.filter(config.bootData.navTree, item => item.hideFromMenu); this.bottomNav = _.filter(config.bootData.navTree, item => item.hideFromMenu);
this.loginUrl = 'login?redirect=' + encodeURIComponent(this.$location.path()); 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', () => { this.$scope.$on('$routeChangeSuccess', () => {
if (!this.contextSrv.pinned) { if (!this.contextSrv.pinned) {
this.contextSrv.sidemenu = false; this.contextSrv.sidemenu = false;
...@@ -44,60 +49,6 @@ export class SideMenuCtrl { ...@@ -44,60 +49,6 @@ export class SideMenuCtrl {
search() { search() {
this.$rootScope.appEvent('show-dash-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() { export function sideMenuDirective() {
......
...@@ -6,7 +6,6 @@ ...@@ -6,7 +6,6 @@
</div> </div>
<section class="card-section card-list-layout-grid"> <section class="card-section card-list-layout-grid">
<ol class="card-list" > <ol class="card-list" >
<li class="card-item-wrapper" ng-repeat="navItem in ctrl.navModel.node.children"> <li class="card-item-wrapper" ng-repeat="navItem in ctrl.navModel.node.children">
<a class="card-item" ng-href="{{::navItem.url}}"> <a class="card-item" ng-href="{{::navItem.url}}">
......
...@@ -120,6 +120,14 @@ ...@@ -120,6 +120,14 @@
} }
} }
} }
&--sidemenu {
li.sidemenu-org-switcher {
> a {
padding: 8px 10px 8px 15px;
}
}
}
} }
.dropdown-item-text { .dropdown-item-text {
......
...@@ -38,7 +38,7 @@ ...@@ -38,7 +38,7 @@
// important to overlap it otherwise it can be hidden // important to overlap it otherwise it can be hidden
// again by the mouse getting outside the hover space // again by the mouse getting outside the hover space
left: $side-menu-width - 2px; 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; z-index: 1;
} }
} }
...@@ -68,11 +68,11 @@ ...@@ -68,11 +68,11 @@
@include keyframes(dropdown-anim) { @include keyframes(dropdown-anim) {
0% { 0% {
opacity: 0; opacity: 0;
transform: translate3d(-5%,0,0); //transform: translate3d(-5%,0,0);
} }
100% { 100% {
opacity: 1; opacity: 1;
transform: translate3d(0,0,0); //transform: translate3d(0,0,0);
} }
} }
...@@ -109,30 +109,28 @@ ...@@ -109,30 +109,28 @@
font-size: 17px; font-size: 17px;
} }
.sidemenu .fa-caret-right { li.sidemenu-org-switcher {
position: absolute; border-bottom: 1px solid $dropdownDividerBottom;
top: 38%;
right: 6px;
font-size: 14px;
color: $text-color-faint;
} }
.sidemenu-org-avatar { .sidemenu-org-switcher__org-name {
>img { font-size: $font-size-base;
position: absolute;
width: 30px;
height: 30px;
border-radius: 50%;
left: 14px;
top: 6px;
z-index: 10;
}
} }
.sidemenu-org-avatar--missing { .sidemenu-org-switcher__org-current {
color: $gray-4; font-size: $font-size-xs;
text-shadow: 0 1px 0 $dark-1; color: $text-color-weak;
line-height: 28px; position: relative;
font-size: $font-size-lg; 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;
}
}
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