Commit 74ceb76e by Torkel Ödegaard

ux: work on page header

parent 645f49ed
...@@ -24,6 +24,7 @@ type NavLink struct { ...@@ -24,6 +24,7 @@ type NavLink struct {
Id string `json:"id,omitempty"` Id string `json:"id,omitempty"`
Text string `json:"text,omitempty"` Text string `json:"text,omitempty"`
Description string `json:"description,omitempty"` Description string `json:"description,omitempty"`
SubTitle string `json:"subTitle,omitempty"`
Icon string `json:"icon,omitempty"` Icon string `json:"icon,omitempty"`
Img string `json:"img,omitempty"` Img string `json:"img,omitempty"`
Url string `json:"url,omitempty"` Url string `json:"url,omitempty"`
......
...@@ -104,7 +104,7 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) { ...@@ -104,7 +104,7 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
{Text: "Home", Url: setting.AppSubUrl + "/", Icon: "fa fa-fw fa-home"}, {Text: "Home", Url: setting.AppSubUrl + "/", Icon: "fa fa-fw fa-home"},
{Text: "Playlists", Id: "playlists", Url: setting.AppSubUrl + "/playlists", Icon: "fa fa-fw fa-film"}, {Text: "Playlists", Id: "playlists", Url: setting.AppSubUrl + "/playlists", Icon: "fa fa-fw fa-film"},
{Text: "Snapshots", Id: "snapshots", Url: setting.AppSubUrl + "/dashboard/snapshots", Icon: "icon-gf icon-gf-fw icon-gf-snapshot"}, {Text: "Snapshots", Id: "snapshots", Url: setting.AppSubUrl + "/dashboard/snapshots", Icon: "icon-gf icon-gf-fw icon-gf-snapshot"},
{Text: "Dashboard List", Description: "Manage Dashboards And Folders", Id: "dashboards", Url: setting.AppSubUrl + "/dashboards", Icon: "fa fa-fw fa-bars"}, {Text: "Dashboard List", Id: "dashboards", Url: setting.AppSubUrl + "/dashboards", Icon: "fa fa-fw fa-bars"},
} }
data.NavTree = append(data.NavTree, &dtos.NavLink{ data.NavTree = append(data.NavTree, &dtos.NavLink{
...@@ -146,6 +146,7 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) { ...@@ -146,6 +146,7 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
data.NavTree = append(data.NavTree, &dtos.NavLink{ data.NavTree = append(data.NavTree, &dtos.NavLink{
Text: "Alerting", Text: "Alerting",
SubTitle: "Alert rules & notifications",
Id: "alerting", Id: "alerting",
Icon: "gicon gicon-alert", Icon: "gicon gicon-alert",
Url: setting.AppSubUrl + "/alerting/list", Url: setting.AppSubUrl + "/alerting/list",
...@@ -202,10 +203,11 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) { ...@@ -202,10 +203,11 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
if c.OrgRole == m.ROLE_ADMIN { if c.OrgRole == m.ROLE_ADMIN {
cfgNode := &dtos.NavLink{ cfgNode := &dtos.NavLink{
Id: "cfg", Id: "cfg",
Text: "Configuration", Text: "Configuration",
Icon: "fa fa-fw fa-cogs", SubTitle: "Organization: " + c.OrgName,
Url: setting.AppSubUrl + "/configuration", Icon: "fa fa-fw fa-cog",
Url: setting.AppSubUrl + "/datasources",
Children: []*dtos.NavLink{ Children: []*dtos.NavLink{
{ {
Text: "Data Sources", Text: "Data Sources",
...@@ -219,25 +221,6 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) { ...@@ -219,25 +221,6 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
}, },
}, },
{ {
Text: "Preferences",
Id: "org",
Description: "Organization preferences",
Icon: "fa fa-fw fa-sliders",
Url: setting.AppSubUrl + "/org",
},
{
Text: "Plugins",
Id: "plugins",
Description: "View and configure plugins",
Icon: "icon-gf icon-gf-fw 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"},
},
},
{
Text: "Members", Text: "Members",
Id: "users", Id: "users",
Description: "Manage org members", Description: "Manage org members",
...@@ -245,13 +228,33 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) { ...@@ -245,13 +228,33 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
Url: setting.AppSubUrl + "/org/users", Url: setting.AppSubUrl + "/org/users",
}, },
{ {
Text: "Groups", Text: "Teams",
Id: "users", Id: "users",
Description: "Manage org groups", Description: "Manage org groups",
Icon: "fa fa-fw fa-users", Icon: "gicon gicon-user-group",
Url: setting.AppSubUrl + "/org/user-groups", Url: setting.AppSubUrl + "/org/user-groups",
}, },
{ {
Text: "Plugins",
Id: "plugins",
Description: "View and configure plugins",
Icon: "icon-gf icon-gf-fw 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"},
// },
},
{
Text: "Preferences",
Id: "org-settings",
Description: "Organization preferences",
Icon: "fa fa-fw fa-sliders",
Url: setting.AppSubUrl + "/org",
},
{
Text: "API Keys", Text: "API Keys",
Id: "apikeys", Id: "apikeys",
Description: "Create & manage API keys", Description: "Create & manage API keys",
...@@ -261,21 +264,21 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) { ...@@ -261,21 +264,21 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
}, },
} }
if c.IsGrafanaAdmin { // if c.IsGrafanaAdmin {
cfgNode.Children = append(cfgNode.Children, &dtos.NavLink{ // cfgNode.Children = append(cfgNode.Children, &dtos.NavLink{
Text: "Server Admin", // Text: "Server Admin",
Id: "admin", // Id: "admin",
Icon: "fa fa-fw fa-shield", // Icon: "fa fa-fw fa-shield",
Url: setting.AppSubUrl + "/admin", // Url: setting.AppSubUrl + "/admin",
Children: []*dtos.NavLink{ // Children: []*dtos.NavLink{
{Text: "Users", Id: "global-users", Url: setting.AppSubUrl + "/admin/users"}, // {Text: "Users", Id: "global-users", Url: setting.AppSubUrl + "/admin/users"},
{Text: "Orgs", Id: "global-orgs", Url: setting.AppSubUrl + "/admin/orgs"}, // {Text: "Orgs", Id: "global-orgs", Url: setting.AppSubUrl + "/admin/orgs"},
{Text: "Server Settings", Id: "server-settings", Url: setting.AppSubUrl + "/admin/settings"}, // {Text: "Server Settings", Id: "server-settings", Url: setting.AppSubUrl + "/admin/settings"},
{Text: "Server Stats", Id: "server-stats", Url: setting.AppSubUrl + "/admin/stats"}, // {Text: "Server Stats", Id: "server-stats", Url: setting.AppSubUrl + "/admin/stats"},
{Text: "Style Guide", Id: "styleguide", Url: setting.AppSubUrl + "/styleguide"}, // {Text: "Style Guide", Id: "styleguide", Url: setting.AppSubUrl + "/styleguide"},
}, // },
}) // })
} // }
data.NavTree = append(data.NavTree, cfgNode) data.NavTree = append(data.NavTree, cfgNode)
} }
......
import { react2AngularDirective } from 'app/core/utils/react2angular'; import { react2AngularDirective } from 'app/core/utils/react2angular';
import { PasswordStrength } from './components/PasswordStrength'; import { PasswordStrength } from './components/PasswordStrength';
import PageHeader from './components/PageHeader';
export function registerAngularDirectives() { export function registerAngularDirectives() {
react2AngularDirective('passwordStrength', PasswordStrength, ['password']); react2AngularDirective('passwordStrength', PasswordStrength, ['password']);
react2AngularDirective('pageHeader', PageHeader, ['model', "noTabs"]);
} }
import React from 'react';
import { NavModel } from '../nav_model_srv';
import classNames from 'classnames';
export interface IProps {
model: NavModel;
}
export default class PageHeader extends React.Component<IProps, any> {
constructor(props) {
super(props);
}
renderBreadcrumb(breadcrumb) {
return (
<a className="breadcrumb-item" href={breadcrumb.url} key={breadcrumb.id}>
{breadcrumb.text}
</a>
);
}
renderTab(tab) {
let tabClasses = classNames({
'gf-tabs-link': true,
'active': tab.active,
});
console.log(tab.active);
return (
<li className="gf-tabs-item" key={tab.url}>
<a className={tabClasses} href={tab.url}>
<i className={tab.icon} />
{tab.text}
</a>
</li>
);
}
renderHeaderTitle(main) {
return (
<div className="page-header__inner">
<span className="page-header__logo">
{main.icon && <i className={`page-header__icon ${main.icon}`} />}
{main.img && <img className="page-header__img" src={main.img} />}
</span>
<div className="page-header__info-block">
<h1 className="page-header__title">{main.text}</h1>
{main.subTitle && <div className="page-header__sub-title">{main.subTitle}</div>}
{main.subType && (
<div className="page-header__stamps">
<i className={main.subType.icon}></i>
{main.subType.text}
</div>
)}
</div>
</div>
);
}
render() {
return (
<div className="page-header-canvas">
<div className="page-container">
<div className="page-nav">
<div className="page-breadcrumbs">
<a className="breadcrumb-item active" href="/">
<i className="fa fa-home" />
</a>
{this.props.model.breadcrumbs.map(this.renderBreadcrumb)}
</div>
</div>
<div className="page-header">
{this.renderHeaderTitle(this.props.model.main)}
{this.props.model.main.children && (
<ul className="gf-tabs">
{this.props.model.main.children.map(this.renderTab)}
</ul>
)}
</div>
</div>
</div>
);
}
}
///<reference path="../headers/common.d.ts" />
import coreModule from 'app/core/core_module'; import coreModule from 'app/core/core_module';
import config from 'app/core/config'; import config from 'app/core/config';
import _ from 'lodash'; import _ from 'lodash';
...@@ -9,20 +7,18 @@ export interface NavModelItem { ...@@ -9,20 +7,18 @@ export interface NavModelItem {
url: string; url: string;
icon?: string; icon?: string;
img?: string; img?: string;
active?: boolean;
children: NavModelItem[];
} }
export class NavModel { export class NavModel {
breadcrumbs: NavModelItem[]; breadcrumbs: NavModelItem[];
header: NavModelItem; main: NavModelItem;
node: NavModelItem; node: NavModelItem;
constructor() { constructor() {
this.breadcrumbs = []; this.breadcrumbs = [];
} }
setPageHeaderIndex(index: number) {
this.header = this.breadcrumbs[index];
}
} }
export class NavModelSrv { export class NavModelSrv {
...@@ -43,13 +39,29 @@ export class NavModelSrv { ...@@ -43,13 +39,29 @@ export class NavModelSrv {
var nav = new NavModel(); var nav = new NavModel();
for (let id of args) { for (let id of args) {
// if its a number then it's the index to use for main
if (_.isNumber(id)) {
nav.main = nav.breadcrumbs[id];
break;
}
let node = _.find(children, {id: id}); let node = _.find(children, {id: id});
nav.breadcrumbs.push(node); nav.breadcrumbs.push(node);
nav.node = node; nav.node = node;
nav.header = node; nav.main = node;
children = node.children; children = node.children;
} }
if (nav.main.children) {
for (let item of nav.main.children) {
item.active = false;
if (item.url === nav.node.url) {
item.active = true;
}
}
}
return nav; return nav;
} }
......
<div class="page-header-canvas"> <page-header model="ctrl.navModel" no-tabs="true"></page-header>
<div class="page-container">
<navbar model="ctrl.navModel"></navbar>
<div class="page-header">
<page-h1 model="ctrl.navModel"></page-h1>
</div>
</div>
</div>
<div class="page-container page-body"> <div class="page-container page-body">
<section class="card-section card-list-layout-grid"> <section class="card-section card-list-layout-grid">
......
...@@ -23,8 +23,7 @@ export class AlertListCtrl { ...@@ -23,8 +23,7 @@ export class AlertListCtrl {
/** @ngInject */ /** @ngInject */
constructor(private backendSrv, private $location, navModelSrv) { constructor(private backendSrv, private $location, navModelSrv) {
this.navModel = navModelSrv.getNav('alerting', 'alert-list'); this.navModel = navModelSrv.getNav('alerting', 'alert-list', 0);
this.navModel.setPageHeaderIndex(0);
var params = $location.search(); var params = $location.search();
this.filters.state = params.state || null; this.filters.state = params.state || null;
......
...@@ -9,8 +9,7 @@ export class AlertNotificationsListCtrl { ...@@ -9,8 +9,7 @@ export class AlertNotificationsListCtrl {
/** @ngInject */ /** @ngInject */
constructor(private backendSrv, navModelSrv) { constructor(private backendSrv, navModelSrv) {
this.loadNotifications(); this.loadNotifications();
this.navModel = navModelSrv.getNav('alerting', 'channels'); this.navModel = navModelSrv.getNav('alerting', 'channels', 0);
this.navModel.setPageHeaderIndex(0);
} }
loadNotifications() { loadNotifications() {
......
<div class="page-header-canvas"> <page-header model="ctrl.navModel"></page-header>
<div class="page-container" >
<navbar model="ctrl.navModel"></navbar>
<div class="page-header">
<page-h1 model="ctrl.navModel" class="page-header__heading"></page-h1>
<ul class="gf-tabs">
<li class="gf-tabs-item">
<a class="gf-tabs-link active" href="alerting">
<i class="fa fa-fw fa-list-ul"></i>
Alert Rules
</a>
</li>
<li class="gf-tabs-item">
<a class="gf-tabs-link" href="alerting/notifications">
<i class="gicon gicon-alert-notification-channel"></i>
Notification channels
</a>
</li>
</ul>
</div>
</div>
</div>
<div class="page-container page-body"> <div class="page-container page-body">
......
<div class="page-header-canvas"> <page-header model="ctrl.navModel"></page-header>
<div class="page-container" >
<navbar model="ctrl.navModel"></navbar>
<div class="page-header">
<page-h1 model="ctrl.navModel" class="page-header__heading"></page-h1>
<ul class="gf-tabs">
<li class="gf-tabs-item">
<a class="gf-tabs-link" href="alerting">
<i class="fa fa-fw fa-list-ul"></i>
Alert Rules
</a>
</li>
<li class="gf-tabs-item">
<a class="gf-tabs-link active" href="alerting/notifications">
<i class="gicon gicon-alert-notification-channel"></i>
Notification channels
</a>
</li>
</ul>
</div>
</div>
</div>
<div class="page-container page-body"> <div class="page-container page-body">
<div class="page-action-bar"> <div class="page-action-bar">
......
...@@ -6,7 +6,7 @@ export class OrgDetailsCtrl { ...@@ -6,7 +6,7 @@ export class OrgDetailsCtrl {
constructor($scope, $http, backendSrv, contextSrv, navModelSrv) { constructor($scope, $http, backendSrv, contextSrv, navModelSrv) {
$scope.init = function() { $scope.init = function() {
$scope.getOrgInfo(); $scope.getOrgInfo();
$scope.navModel = navModelSrv.getNav('cfg', 'org'); $scope.navModel = navModelSrv.getNav('cfg', 'org-settings', 0);
}; };
$scope.getOrgInfo = function() { $scope.getOrgInfo = function() {
......
<navbar model="navModel"></navbar> <!-- <div class="page&#45;header&#45;canvas"> -->
<!-- <div class="page&#45;container" > -->
<!-- <navbar model="navModel"></navbar> -->
<!-- -->
<!-- <div class="page&#45;header"> -->
<!-- <page&#45;h1 model="navModel"></page&#45;h1> -->
<!-- -->
<!-- <ul class="gf&#45;tabs"> -->
<!-- <li class="gf&#45;tabs&#45;item"> -->
<!-- <a class="gf&#45;tabs&#45;link active" href="org"> -->
<!-- <i class="fa fa&#45;fw fa&#45;sliders"></i> -->
<!-- Preferences -->
<!-- </a> -->
<!-- </li> -->
<!-- <li class="gf&#45;tabs&#45;item"> -->
<!-- <a class="gf&#45;tabs&#45;link" href="org/users"> -->
<!-- <i class="icon&#45;gf icon&#45;gf&#45;fw icon&#45;gf&#45;users"></i> -->
<!-- Members -->
<!-- </a> -->
<!-- </li> -->
<!-- <li class="gf&#45;tabs&#45;item"> -->
<!-- <a class="gf&#45;tabs&#45;link" href="org/user&#45;groups"> -->
<!-- <i class="gicon gicon&#45;user&#45;group"></i> -->
<!-- Teams -->
<!-- </a> -->
<!-- </li> -->
<!-- </ul> -->
<!-- </div> -->
<!-- </div> -->
<!-- </div> -->
<div class="page-container"> <page-header model="navModel"></page-header>
<div class="page-header">
<page-h1 model="navModel"></page-h1>
</div>
<h3 class="page-heading">General</h3> <div class="page-container page-body">
<form name="orgForm" class="gf-form-group"> <h3 class="page-heading">General</h3>
<div class="gf-form-inline"> <form name="orgForm" class="gf-form-group">
<div class="gf-form max-width-28"> <div class="gf-form-inline">
<span class="gf-form-label">Organization name</span> <div class="gf-form max-width-28">
<input class="gf-form-input" type="text" required ng-model="org.name"> <span class="gf-form-label">Organization name</span>
</div> <input class="gf-form-input" type="text" required ng-model="org.name">
</div> </div>
</div>
<div class="gf-form-button-row"> <div class="gf-form-button-row">
<button type="submit" class="btn btn-success" ng-click="update()">Save</button> <button type="submit" class="btn btn-success" ng-click="update()">Save</button>
</div> </div>
</form> </form>
<prefs-control mode="org"></prefs-control>
<prefs-control mode="org"></prefs-control>
</div> </div>
...@@ -13,7 +13,7 @@ export class DataSourcesCtrl { ...@@ -13,7 +13,7 @@ export class DataSourcesCtrl {
private datasourceSrv, private datasourceSrv,
private navModelSrv) { private navModelSrv) {
this.navModel = this.navModelSrv.getNav('cfg', 'datasources'); this.navModel = this.navModelSrv.getNav('cfg', 'datasources', 0);
backendSrv.get('/api/datasources').then(result => { backendSrv.get('/api/datasources').then(result => {
this.datasources = result; this.datasources = result;
......
<div class="page-header-canvas"> <page-header model="ctrl.navModel"></page-header>
<div class="page-container">
<navbar model="ctrl.navModel"></navbar>
<div class="page-header">
<page-h1 model="ctrl.navModel"></page-h1>
</div>
</div>
</div>
<div class="page-container page-body"> <div class="page-container page-body">
<div class="page-action-bar"> <div class="page-action-bar">
......
...@@ -52,6 +52,7 @@ $critical: #ed2e18; ...@@ -52,6 +52,7 @@ $critical: #ed2e18;
// ------------------------- // -------------------------
$body-bg: rgb(23,24,25); $body-bg: rgb(23,24,25);
$page-bg: rgb(22, 23, 25); $page-bg: rgb(22, 23, 25);
$body-color: $gray-4; $body-color: $gray-4;
$text-color: $gray-4; $text-color: $gray-4;
$text-color-strong: $white; $text-color-strong: $white;
......
...@@ -47,5 +47,9 @@ ...@@ -47,5 +47,9 @@
background-image: url('../img/icons_#{$theme-name}_theme/icon_notification_channels.svg'); background-image: url('../img/icons_#{$theme-name}_theme/icon_notification_channels.svg');
} }
.gicon-user-group {
background-image: url('../img/icons_#{$theme-name}_theme/icon_user_group.svg');
}
...@@ -20,27 +20,30 @@ ...@@ -20,27 +20,30 @@
} }
} }
.page-header__title { .page-header__inner {
font-size: $font-size-h2;
flex-grow: 1; flex-grow: 1;
display: flex;
margin-bottom: 2.5rem; margin-bottom: 2.5rem;
line-height: 50px; }
.page-header__title {
font-size: $font-size-h2;
margin-bottom: 1px;
padding-top: $spacer;
} }
.page-header__img { .page-header__img {
border-radius: 50%; border-radius: 50%;
margin-right: 0.5rem;
position: relative; position: relative;
top: -3px; top: -3px;
width: 50px; width: 65px;
height: 50px; height: 65px;
} }
.page-header__icon { .page-header__icon {
font-size: 150%; font-size: 70px;
margin-right: 0.5rem; width: 65;
width: 50px; height: 65;
height: 50px;
position: relative; position: relative;
&.fa { &.fa {
...@@ -52,10 +55,19 @@ ...@@ -52,10 +55,19 @@
} }
} }
.page-heading { .page-header__logo {
font-size: 1.25rem; margin-right: $spacer/2;
margin-top: 0;
margin-bottom: $spacer * 0.7;
} }
.page-header-info-block {
}
.page-header__sub-title {
color: $text-muted;
}
.page-header-stamps-type {
color: $link-color-disabled;
text-transform: uppercase;
}
...@@ -42,6 +42,12 @@ ...@@ -42,6 +42,12 @@
} }
} }
.page-heading {
font-size: 1.25rem;
margin-top: 0;
margin-bottom: $spacer * 0.7;
}
.page-action-bar { .page-action-bar {
margin-bottom: $spacer * 2; margin-bottom: $spacer * 2;
display: flex; display: flex;
......
.plugin-header {
@include clearfix();
padding: $spacer 0 $spacer/2 0;
margin-bottom: 2rem;
}
.plugin-header-logo {
float: left;
width: 7rem;
img {
width: 7rem;
}
margin-right: $spacer;
}
.plugin-header-info-block {
float: left;
}
.plugin-header-author {
}
.plugin-header-stamps-type {
color: $link-color-disabled;
text-transform: uppercase;
}
.plugin-info-list-item {
img {
width: 16px;
}
white-space: nowrap;
max-width: $page-sidebar-width;
text-overflow: ellipsis;
overflow: hidden;
}
.get-more-plugins-link { .get-more-plugins-link {
color: $gray-3; color: $gray-3;
...@@ -55,3 +17,15 @@ ...@@ -55,3 +17,15 @@
display: none; display: none;
} }
} }
.plugin-info-list-item {
img {
width: 16px;
}
white-space: nowrap;
max-width: $page-sidebar-width;
text-overflow: ellipsis;
overflow: hidden;
}
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