Commit 74ceb76e by Torkel Ödegaard

ux: work on page header

parent 645f49ed
......@@ -24,6 +24,7 @@ type NavLink struct {
Id string `json:"id,omitempty"`
Text string `json:"text,omitempty"`
Description string `json:"description,omitempty"`
SubTitle string `json:"subTitle,omitempty"`
Icon string `json:"icon,omitempty"`
Img string `json:"img,omitempty"`
Url string `json:"url,omitempty"`
......
......@@ -104,7 +104,7 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
{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: "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{
......@@ -146,6 +146,7 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
data.NavTree = append(data.NavTree, &dtos.NavLink{
Text: "Alerting",
SubTitle: "Alert rules & notifications",
Id: "alerting",
Icon: "gicon gicon-alert",
Url: setting.AppSubUrl + "/alerting/list",
......@@ -202,10 +203,11 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
if c.OrgRole == m.ROLE_ADMIN {
cfgNode := &dtos.NavLink{
Id: "cfg",
Text: "Configuration",
Icon: "fa fa-fw fa-cogs",
Url: setting.AppSubUrl + "/configuration",
Id: "cfg",
Text: "Configuration",
SubTitle: "Organization: " + c.OrgName,
Icon: "fa fa-fw fa-cog",
Url: setting.AppSubUrl + "/datasources",
Children: []*dtos.NavLink{
{
Text: "Data Sources",
......@@ -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",
Id: "users",
Description: "Manage org members",
......@@ -245,13 +228,33 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
Url: setting.AppSubUrl + "/org/users",
},
{
Text: "Groups",
Text: "Teams",
Id: "users",
Description: "Manage org groups",
Icon: "fa fa-fw fa-users",
Icon: "gicon gicon-user-group",
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",
Id: "apikeys",
Description: "Create & manage API keys",
......@@ -261,21 +264,21 @@ 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{
{Text: "Users", Id: "global-users", Url: setting.AppSubUrl + "/admin/users"},
{Text: "Orgs", Id: "global-orgs", Url: setting.AppSubUrl + "/admin/orgs"},
{Text: "Server Settings", Id: "server-settings", Url: setting.AppSubUrl + "/admin/settings"},
{Text: "Server Stats", Id: "server-stats", Url: setting.AppSubUrl + "/admin/stats"},
{Text: "Style Guide", Id: "styleguide", Url: setting.AppSubUrl + "/styleguide"},
},
})
}
// 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{
// {Text: "Users", Id: "global-users", Url: setting.AppSubUrl + "/admin/users"},
// {Text: "Orgs", Id: "global-orgs", Url: setting.AppSubUrl + "/admin/orgs"},
// {Text: "Server Settings", Id: "server-settings", Url: setting.AppSubUrl + "/admin/settings"},
// {Text: "Server Stats", Id: "server-stats", Url: setting.AppSubUrl + "/admin/stats"},
// {Text: "Style Guide", Id: "styleguide", Url: setting.AppSubUrl + "/styleguide"},
// },
// })
// }
data.NavTree = append(data.NavTree, cfgNode)
}
......
import { react2AngularDirective } from 'app/core/utils/react2angular';
import { PasswordStrength } from './components/PasswordStrength';
import PageHeader from './components/PageHeader';
export function registerAngularDirectives() {
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 config from 'app/core/config';
import _ from 'lodash';
......@@ -9,20 +7,18 @@ export interface NavModelItem {
url: string;
icon?: string;
img?: string;
active?: boolean;
children: NavModelItem[];
}
export class NavModel {
breadcrumbs: NavModelItem[];
header: NavModelItem;
main: NavModelItem;
node: NavModelItem;
constructor() {
this.breadcrumbs = [];
}
setPageHeaderIndex(index: number) {
this.header = this.breadcrumbs[index];
}
}
export class NavModelSrv {
......@@ -43,13 +39,29 @@ export class NavModelSrv {
var nav = new NavModel();
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});
nav.breadcrumbs.push(node);
nav.node = node;
nav.header = node;
nav.main = node;
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;
}
......
<div class="page-header-canvas">
<div class="page-container">
<navbar model="ctrl.navModel"></navbar>
<div class="page-header">
<page-h1 model="ctrl.navModel"></page-h1>
</div>
</div>
</div>
<page-header model="ctrl.navModel" no-tabs="true"></page-header>
<div class="page-container page-body">
<section class="card-section card-list-layout-grid">
......
......@@ -23,8 +23,7 @@ export class AlertListCtrl {
/** @ngInject */
constructor(private backendSrv, private $location, navModelSrv) {
this.navModel = navModelSrv.getNav('alerting', 'alert-list');
this.navModel.setPageHeaderIndex(0);
this.navModel = navModelSrv.getNav('alerting', 'alert-list', 0);
var params = $location.search();
this.filters.state = params.state || null;
......
......@@ -9,8 +9,7 @@ export class AlertNotificationsListCtrl {
/** @ngInject */
constructor(private backendSrv, navModelSrv) {
this.loadNotifications();
this.navModel = navModelSrv.getNav('alerting', 'channels');
this.navModel.setPageHeaderIndex(0);
this.navModel = navModelSrv.getNav('alerting', 'channels', 0);
}
loadNotifications() {
......
<div class="page-header-canvas">
<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>
<page-header model="ctrl.navModel"></page-header>
<div class="page-container page-body">
......
<div class="page-header-canvas">
<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>
<page-header model="ctrl.navModel"></page-header>
<div class="page-container page-body">
<div class="page-action-bar">
......
......@@ -6,7 +6,7 @@ export class OrgDetailsCtrl {
constructor($scope, $http, backendSrv, contextSrv, navModelSrv) {
$scope.init = function() {
$scope.getOrgInfo();
$scope.navModel = navModelSrv.getNav('cfg', 'org');
$scope.navModel = navModelSrv.getNav('cfg', 'org-settings', 0);
};
$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">
<div class="page-header">
<page-h1 model="navModel"></page-h1>
</div>
<page-header model="navModel"></page-header>
<h3 class="page-heading">General</h3>
<form name="orgForm" class="gf-form-group">
<div class="gf-form-inline">
<div class="gf-form max-width-28">
<span class="gf-form-label">Organization name</span>
<input class="gf-form-input" type="text" required ng-model="org.name">
</div>
</div>
<div class="page-container page-body">
<h3 class="page-heading">General</h3>
<form name="orgForm" class="gf-form-group">
<div class="gf-form-inline">
<div class="gf-form max-width-28">
<span class="gf-form-label">Organization name</span>
<input class="gf-form-input" type="text" required ng-model="org.name">
</div>
</div>
<div class="gf-form-button-row">
<button type="submit" class="btn btn-success" ng-click="update()">Save</button>
</div>
</form>
<prefs-control mode="org"></prefs-control>
</form>
<prefs-control mode="org"></prefs-control>
</div>
......@@ -13,7 +13,7 @@ export class DataSourcesCtrl {
private datasourceSrv,
private navModelSrv) {
this.navModel = this.navModelSrv.getNav('cfg', 'datasources');
this.navModel = this.navModelSrv.getNav('cfg', 'datasources', 0);
backendSrv.get('/api/datasources').then(result => {
this.datasources = result;
......
<div class="page-header-canvas">
<div class="page-container">
<navbar model="ctrl.navModel"></navbar>
<div class="page-header">
<page-h1 model="ctrl.navModel"></page-h1>
</div>
</div>
</div>
<page-header model="ctrl.navModel"></page-header>
<div class="page-container page-body">
<div class="page-action-bar">
......
......@@ -52,6 +52,7 @@ $critical: #ed2e18;
// -------------------------
$body-bg: rgb(23,24,25);
$page-bg: rgb(22, 23, 25);
$body-color: $gray-4;
$text-color: $gray-4;
$text-color-strong: $white;
......
......@@ -47,5 +47,9 @@
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 @@
}
}
.page-header__title {
font-size: $font-size-h2;
.page-header__inner {
flex-grow: 1;
display: flex;
margin-bottom: 2.5rem;
line-height: 50px;
}
.page-header__title {
font-size: $font-size-h2;
margin-bottom: 1px;
padding-top: $spacer;
}
.page-header__img {
border-radius: 50%;
margin-right: 0.5rem;
position: relative;
top: -3px;
width: 50px;
height: 50px;
width: 65px;
height: 65px;
}
.page-header__icon {
font-size: 150%;
margin-right: 0.5rem;
width: 50px;
height: 50px;
font-size: 70px;
width: 65;
height: 65;
position: relative;
&.fa {
......@@ -52,10 +55,19 @@
}
}
.page-heading {
font-size: 1.25rem;
margin-top: 0;
margin-bottom: $spacer * 0.7;
.page-header__logo {
margin-right: $spacer/2;
}
.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 @@
}
}
.page-heading {
font-size: 1.25rem;
margin-top: 0;
margin-bottom: $spacer * 0.7;
}
.page-action-bar {
margin-bottom: $spacer * 2;
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 {
color: $gray-3;
......@@ -55,3 +17,15 @@
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