Merge branch 'develop' into develop-light-theme

This commit is contained in:
Patrick O'Carroll 2017-12-04 13:17:19 +01:00
commit a16cfb4b3b
105 changed files with 1649 additions and 1758 deletions

View File

@ -24,11 +24,13 @@ 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"`
Target string `json:"target,omitempty"`
Divider bool `json:"divider,omitempty"`
HideFromMenu bool `json:"hideFromMenu,omitempty"`
HideFromTabs bool `json:"hideFromTabs,omitempty"`
Children []*NavLink `json:"children,omitempty"`
}

View File

@ -101,15 +101,17 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
}
dashboardChildNavs := []*dtos.NavLink{
{Text: "Home", Url: setting.AppSubUrl + "/", Icon: "fa fa-fw fa-home"},
{Text: "Home", Url: setting.AppSubUrl + "/", Icon: "fa fa-fw fa-home", HideFromTabs: true},
{Divider: true, HideFromTabs: true},
{Text: "Manage", Id: "dashboards", Url: setting.AppSubUrl + "/dashboards", Icon: "fa fa-fw fa-sitemap"},
{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"},
}
data.NavTree = append(data.NavTree, &dtos.NavLink{
Text: "Dashboards",
Id: "dashboards",
SubTitle: "Manage dashboards & folders",
Icon: "gicon gicon-dashboard",
Url: setting.AppSubUrl + "/",
Children: dashboardChildNavs,
@ -117,22 +119,23 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
if c.IsSignedIn {
profileNode := &dtos.NavLink{
Text: c.SignedInUser.Login,
Text: c.SignedInUser.Name,
SubTitle: c.SignedInUser.Login,
Id: "profile",
Img: data.User.GravatarUrl,
Url: setting.AppSubUrl + "/profile",
HideFromMenu: true,
Children: []*dtos.NavLink{
{Text: "Your profile", Url: setting.AppSubUrl + "/profile", Icon: "fa fa-fw fa-sliders"},
{Text: "Preferences", Id: "profile-settings", 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...)
profileNode.Children = append(profileNode.Children, &dtos.NavLink{
Text: "Sign out", Id: "sign-out", Url: setting.AppSubUrl + "/logout", Icon: "fa fa-fw fa-sign-out", Target: "_self",
})
}
data.NavTree = append(data.NavTree, profileNode)
@ -140,12 +143,13 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
if setting.AlertingEnabled && (c.OrgRole == m.ROLE_ADMIN || c.OrgRole == m.ROLE_EDITOR) {
alertChildNavs := []*dtos.NavLink{
{Text: "Alert List", Id: "alert-list", Url: setting.AppSubUrl + "/alerting/list", Icon: "fa fa-fw fa-list-ul"},
{Text: "Notification channels", Id: "channels", Url: setting.AppSubUrl + "/alerting/notifications", Icon: "fa fa-fw fa-bell-o"},
{Text: "Alert Rules", Id: "alert-list", Url: setting.AppSubUrl + "/alerting/list", Icon: "fa fa-fw fa-list-ul"},
{Text: "Notification channels", Id: "channels", Url: setting.AppSubUrl + "/alerting/notifications", Icon: "gicon gicon-alert-notification-channel"},
}
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 +206,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",
@ -213,29 +218,6 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
Description: "Add and configure data sources",
Id: "datasources",
Url: setting.AppSubUrl + "/datasources",
Children: []*dtos.NavLink{
{Text: "List", Url: setting.AppSubUrl + "/datasources", Icon: "gicon gicon-datasources"},
{Text: "New", Url: setting.AppSubUrl + "/datasources", Icon: "fa fa-fw fa-plus"},
},
},
{
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",
@ -245,12 +227,27 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
Url: setting.AppSubUrl + "/org/users",
},
{
Text: "Groups",
Id: "users",
Text: "Teams",
Id: "teams",
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",
},
{
Text: "Preferences",
Id: "org-settings",
Description: "Organization preferences",
Icon: "fa fa-fw fa-sliders",
Url: setting.AppSubUrl + "/org",
},
{
Text: "API Keys",
Id: "apikeys",
@ -263,16 +260,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",
Divider: true, HideFromTabs: true,
})
cfgNode.Children = append(cfgNode.Children, &dtos.NavLink{
Text: "Server Admin",
HideFromTabs: true,
SubTitle: "Manage all users & orgs",
Id: "admin",
Icon: "fa fa-fw fa-shield",
Url: setting.AppSubUrl + "/admin/users",
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"},
{Text: "Users", Id: "global-users", Url: setting.AppSubUrl + "/admin/users", Icon: "icon-gf icon-gf-fw icon-gf-users"},
{Text: "Orgs", Id: "global-orgs", Url: setting.AppSubUrl + "/admin/orgs", Icon: "gicon gicon-org"},
{Text: "Settings", Id: "server-settings", Url: setting.AppSubUrl + "/admin/settings", Icon: "fa fa-fw fa-sliders"},
{Text: "Stats", Id: "server-stats", Url: setting.AppSubUrl + "/admin/stats", Icon: "fa fa-fw fa-bar-chart"},
{Text: "Style Guide", Id: "styleguide", Url: setting.AppSubUrl + "/styleguide", Icon: "fa fa-fw fa-eyedropper"},
},
})
}

View File

@ -68,6 +68,15 @@ func NewDashboard(title string) *Dashboard {
return dash
}
// NewDashboardFolder creates a new dashboard folder
func NewDashboardFolder(title string) *Dashboard {
folder := NewDashboard(title)
folder.Data.Set("schemaVersion", 16)
folder.Data.Set("editable", true)
folder.Data.Set("hideControls", true)
return folder
}
// GetTags turns the tags in data json into go string array
func (dash *Dashboard) GetTags() []string {
return dash.Data.Get("tags").MustStringArray()

View File

@ -49,10 +49,6 @@ func ImportDashboard(cmd *ImportDashboardCommand) error {
if dashboard, err = loadPluginDashboard(cmd.PluginId, cmd.Path); err != nil {
return err
}
if err = createDashboardFolderForPlugin(cmd, dashboard); err != nil {
return err
}
} else {
dashboard = m.NewDashboardFromJson(cmd.Dashboard)
}
@ -93,63 +89,6 @@ func ImportDashboard(cmd *ImportDashboardCommand) error {
return nil
}
func createDashboardFolderForPlugin(cmd *ImportDashboardCommand, dashboard *m.Dashboard) error {
var err error
var plugin *PluginBase
if plugin, err = getPlugin(cmd.PluginId); err != nil {
return err
}
var pluginType string
if plugin.Type == "datasource" {
pluginType = "Datasource"
} else if plugin.Type == "app" {
pluginType = "App"
}
folderTitle := fmt.Sprint(pluginType, ": ", plugin.Name)
folderDash := simplejson.NewFromAny(map[string]interface{}{
"schemaVersion": 16,
"title": folderTitle,
"editable": true,
"hideControls": true,
})
saveCmd := m.SaveDashboardCommand{
Dashboard: folderDash,
OrgId: cmd.OrgId,
UserId: cmd.UserId,
PluginId: cmd.PluginId,
IsFolder: true,
}
dashModel := saveCmd.GetDashboardModel()
getDashboardQuery := m.GetDashboardQuery{
OrgId: cmd.OrgId,
Slug: dashModel.Slug,
}
if err := bus.Dispatch(&getDashboardQuery); err != nil {
return err
}
if getDashboardQuery.Result != nil {
dashboard.FolderId = getDashboardQuery.Result.Id
return nil
}
if err := bus.Dispatch(&saveCmd); err != nil {
return err
}
dashboard.FolderId = saveCmd.Result.Id
return nil
}
type DashTemplateEvaluator struct {
template *simplejson.Json
inputs []ImportDashboardInput

View File

@ -13,32 +13,12 @@ import (
)
func TestDashboardImport(t *testing.T) {
Convey("When importing plugin dashboard", t, func() {
setting.Cfg = ini.Empty()
sec, _ := setting.Cfg.NewSection("plugin.test-app")
sec.NewKey("path", "../../tests/test-app")
err := Init()
So(err, ShouldBeNil)
folderId := int64(1000)
pluginScenario("When importing a plugin dashboard", t, func() {
var importedDash *m.Dashboard
var createdFolder *m.Dashboard
bus.AddHandler("test", func(cmd *m.SaveDashboardCommand) error {
if cmd.IsFolder {
createdFolder = cmd.GetDashboardModel()
createdFolder.Id = folderId
cmd.Result = createdFolder
} else {
importedDash = cmd.GetDashboardModel()
cmd.Result = importedDash
}
return nil
})
bus.AddHandler("test", func(cmd *m.GetDashboardQuery) error {
importedDash = cmd.GetDashboardModel()
cmd.Result = importedDash
return nil
})
@ -52,7 +32,7 @@ func TestDashboardImport(t *testing.T) {
},
}
err = ImportDashboard(&cmd)
err := ImportDashboard(&cmd)
So(err, ShouldBeNil)
Convey("should install dashboard", func() {
@ -67,78 +47,6 @@ func TestDashboardImport(t *testing.T) {
panel := importedDash.Data.Get("rows").GetIndex(0).Get("panels").GetIndex(0)
So(panel.Get("datasource").MustString(), ShouldEqual, "graphite")
So(importedDash.FolderId, ShouldEqual, folderId)
})
Convey("should create app folder", func() {
So(createdFolder.Title, ShouldEqual, "App: Test App")
So(createdFolder.Id, ShouldEqual, folderId)
})
})
Convey("When re-importing plugin dashboard", t, func() {
setting.Cfg = ini.Empty()
sec, _ := setting.Cfg.NewSection("plugin.test-app")
sec.NewKey("path", "../../tests/test-app")
err := Init()
So(err, ShouldBeNil)
folderId := int64(1000)
var importedDash *m.Dashboard
var createdFolder *m.Dashboard
bus.AddHandler("test", func(cmd *m.SaveDashboardCommand) error {
if cmd.IsFolder {
cmd.Result = cmd.GetDashboardModel()
} else {
importedDash = cmd.GetDashboardModel()
cmd.Result = importedDash
}
return nil
})
bus.AddHandler("test", func(cmd *m.GetDashboardQuery) error {
cmd.Result = &m.Dashboard{
Id: 1000,
Title: "Something",
}
return nil
})
cmd := ImportDashboardCommand{
PluginId: "test-app",
Path: "dashboards/connections.json",
OrgId: 1,
UserId: 1,
Inputs: []ImportDashboardInput{
{Name: "*", Type: "datasource", Value: "graphite"},
},
}
err = ImportDashboard(&cmd)
So(err, ShouldBeNil)
Convey("should install dashboard", func() {
So(importedDash, ShouldNotBeNil)
resultStr, _ := importedDash.Data.EncodePretty()
expectedBytes, _ := ioutil.ReadFile("../../tests/test-app/dashboards/connections_result.json")
expectedJson, _ := simplejson.NewJson(expectedBytes)
expectedStr, _ := expectedJson.EncodePretty()
So(string(resultStr), ShouldEqual, string(expectedStr))
panel := importedDash.Data.Get("rows").GetIndex(0).Get("panels").GetIndex(0)
So(panel.Get("datasource").MustString(), ShouldEqual, "graphite")
So(importedDash.FolderId, ShouldEqual, folderId)
})
Convey("should not create app folder", func() {
So(createdFolder, ShouldBeNil)
})
})
@ -177,3 +85,16 @@ func TestDashboardImport(t *testing.T) {
})
}
func pluginScenario(desc string, t *testing.T, fn func()) {
Convey("Given a plugin", t, func() {
setting.Cfg = ini.Empty()
sec, _ := setting.Cfg.NewSection("plugin.test-app")
sec.NewKey("path", "../../tests/test-app")
err := Init()
So(err, ShouldBeNil)
Convey(desc, fn)
})
}

View File

@ -108,13 +108,3 @@ func loadPluginDashboard(pluginId, path string) (*m.Dashboard, error) {
return m.NewDashboardFromJson(data), nil
}
func getPlugin(pluginId string) (*PluginBase, error) {
plugin, exists := Plugins[pluginId]
if !exists {
return nil, PluginNotFoundError{pluginId}
}
return plugin, nil
}

View File

@ -1,8 +1,10 @@
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"]);
}

View File

@ -0,0 +1,72 @@
import React from 'react';
import { NavModel, NavModelItem } from '../nav_model_srv';
import classNames from 'classnames';
export interface IProps {
model: NavModel;
}
function TabItem(tab: NavModelItem) {
if (tab.hideFromTabs) {
return (null);
}
let tabClasses = classNames({
'gf-tabs-link': true,
active: 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>
);
}
function Tabs({main}: {main: NavModelItem}) {
return <ul className="gf-tabs">{main.children.map(TabItem)}</ul>;
}
export default class PageHeader extends React.Component<IProps, any> {
constructor(props) {
super(props);
}
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} />
{main.subType.text}
</div>
)}
</div>
</div>
);
}
render() {
return (
<div className="page-header-canvas">
<div className="page-container">
<div className="page-header">
{this.renderHeaderTitle(this.props.model.main)}
{this.props.model.main.children && <Tabs main={this.props.model.main} />}
</div>
</div>
</div>
);
}
}

View File

@ -85,6 +85,16 @@ export function grafanaAppDirective(playlistSrv, contextSrv, $timeout, $rootScop
}
});
let sidemenuOpenSmallBreakpoint = scope.contextSrv.sidemenuSmallBreakpoint;
body.toggleClass('sidemenu-open--xs', sidemenuOpenSmallBreakpoint);
scope.$watch('contextSrv.sidemenuSmallBreakpoint', newVal => {
if (sidemenuOpenSmallBreakpoint !== scope.contextSrv.sidemenuSmallBreakpoint) {
sidemenuOpenSmallBreakpoint = scope.contextSrv.sidemenuSmallBreakpoint;
body.toggleClass('sidemenu-open--xs', scope.contextSrv.sidemenuSmallBreakpoint);
}
});
// tooltip removal fix
// manage page classes
var pageClass;

View File

@ -1,13 +1,11 @@
<div class="page-nav">
<div class="container">
<div class="page-breadcrumbs">
<a class="breadcrumb-item active" href="/">
<i class="fa fa-home"></i>
</a>
<a class="breadcrumb-item" ng-href="{{::item.url}}" ng-repeat="item in ctrl.model.breadcrumbs">
{{::item.text}}
</a>
</div>
<div class="page-breadcrumbs">
<a class="breadcrumb-item active" href="/">
<i class="fa fa-home"></i>
</a>
<a class="breadcrumb-item" ng-href="{{::item.url}}" ng-repeat="item in ctrl.model.breadcrumbs">
{{::item.text}}
</a>
</div>
</div>

View File

@ -39,10 +39,10 @@ export function pageH1() {
return {
restrict: 'E',
template: `
<h1>
<i class="{{::model.node.icon}}" ng-if="::model.node.icon"></i>
<img ng-src="{{::model.node.img}}" ng-if="::model.node.img"></i>
{{model.node.text}}
<h1 class="page-header__title">
<i class="page-header__icon {{::model.header.icon}}" ng-if="::model.header.icon"></i>
<img class="page-header__img" ng-src="{{::model.header.img}}" ng-if="::model.header.img"></i>
{{model.header.text}}
</h1>
`,
scope: {

View File

@ -8,6 +8,14 @@ export function geminiScrollbar() {
let scrollbar = new PerfectScrollbar(elem[0]);
scope.$on('$routeChangeSuccess', () => {
elem[0].scrollTop = 0;
});
scope.$on('$routeUpdate', () => {
elem[0].scrollTop = 0;
});
scope.$on('$destroy', () => {
scrollbar.destroy();
});

View File

@ -15,84 +15,74 @@
ng-blur="ctrl.searchInputBlur()"
/>
<div class="search-switches">
<i class="fa fa-filter"></i>
<a class="pointer" href="javascript:void 0;" ng-click="ctrl.showStarred()" tabindex="2">
<i class="fa fa-remove" ng-show="ctrl.query.starred"></i>
starred
</a> |
<a class="pointer" href="javascript:void 0;" ng-click="ctrl.getTags()" tabindex="3">
<i class="fa fa-remove" ng-show="ctrl.tagsMode"></i>
tags
</a>
<span ng-if="ctrl.query.tag.length">
|
<span ng-repeat="tagName in ctrl.query.tag">
<a ng-click="ctrl.removeTag(tagName, $event)" tag-color-from-name="tagName" class="label label-tag">
<i class="fa fa-remove"></i>
{{tagName}}
</a>
</span>
</span>
</div>
<div class="search-field-spacer"></div>
</div>
<div class="search-dropdown" ng-class="{'search-dropdown--fade-in': ctrl.openCompleted}">
<div class="search-results-container" ng-if="ctrl.tagsMode">
<div ng-repeat="tag in ctrl.results" class="pointer" style="width: 180px; float: left;"
ng-class="{'selected': $index === ctrl.selectedIndex }"
ng-click="ctrl.filterByTag(tag.term, $event)">
<a class="search-result-tag label label-tag" tag-color-from-name="tag.term">
<i class="fa fa-tag"></i>
<span>{{tag.term}} &nbsp;({{tag.count}})</span>
</a>
</div>
</div>
<div class="search-dropdown">
<div class="search-dropdown__col_1">
<div class="search-results-container" grafana-scrollbar>
<h6 ng-show="!ctrl.isLoading && results.length">No dashboards matching your query were found.</h6>
<div class="search-results-container" ng-if="!ctrl.tagsMode" grafana-scrollbar>
<h6 ng-show="!ctrl.isLoading && results.length">No dashboards matching your query were found.</h6>
<div ng-repeat="section in ctrl.results" class="search-section">
<a class="search-section__header pointer" ng-hide="section.hideHeader" ng-click="ctrl.toggleFolder(section)">
<i class="search-section__header__icon" ng-class="section.icon"></i>
<span class="search-section__header__text">{{::section.title}}</span>
<i class="fa fa-minus search-section__header__toggle" ng-show="section.expanded"></i>
<i class="fa fa-plus search-section__header__toggle" ng-hide="section.expanded"></i>
</a>
<div ng-repeat="section in ctrl.results" class="search-section">
<a class="search-section__header pointer" ng-hide="section.hideHeader" ng-click="ctrl.toggleFolder(section)">
<i class="search-section__header__icon" ng-class="section.icon"></i>
<span class="search-section__header__text">{{::section.title}}</span>
<i class="fa fa-minus search-section__header__toggle" ng-show="section.expanded"></i>
<i class="fa fa-plus search-section__header__toggle" ng-hide="section.expanded"></i>
</a>
<div ng-if="section.expanded">
<a ng-repeat="item in section.items" class="search-item" ng-class="{'selected': item.selected}" ng-href="{{::item.url}}">
<span class="search-item__icon">
<i class="fa fa-th-large"></i>
</span>
<span class="search-item__body">
<div class="search-item__body-title">{{::item.title}}</div>
<div class="search-item__body-sub-title" ng-show="item.folderTitle && section.hideHeader">
{{::item.folderTitle}}
</div>
</span>
<span class="search-item__tags">
<span ng-click="ctrl.filterByTag(tag, $event)" ng-repeat="tag in item.tags" tag-color-from-name="tag" class="label label-tag">
{{tag}}
</span>
</span>
<span class="search-item__actions">
<span class="search-item__actions__item" ng-click="ctrl.toggleStar()">
<i class="fa" ng-class="{'fa-star': item.isStarred, 'fa-star-o': !item.isStarred}"></i>
<div ng-if="section.expanded">
<a ng-repeat="item in section.items" class="search-item" ng-class="{'selected': item.selected}" ng-href="{{::item.url}}">
<span class="search-item__icon">
<i class="fa fa-th-large"></i>
</span>
</span>
</a>
</div>
</div>
</div>
<span class="search-item__body">
<div class="search-item__body-title">{{::item.title}}</div>
<div class="search-item__body-sub-title" ng-show="item.folderTitle && section.hideHeader">
{{::item.folderTitle}}
</div>
</span>
<span class="search-item__tags">
<span ng-click="ctrl.filterByTag(tag, $event)" ng-repeat="tag in item.tags" tag-color-from-name="tag" class="label label-tag">
{{tag}}
</span>
</span>
</a>
</div>
</div>
</div>
</div>
<div class="search-button-row">
<a class="search-button-row-explore-link" target="_blank" href="https://grafana.com/dashboards?utm_source=grafana_search">
Find <img src="public/img/icn-dashboard-tiny.svg" width="14" /> dashboards on Grafana.com
</a>
</div>
</div>
<div class="search-dropdown__col_2">
<div class="search-filter-box">
<div class="search-filter-box__header">
<i class="fa fa-filter"></i>
Filter by:
<a class="pointer pull-right small">
<i class="fa fa-remove"></i> Clear
</a>
</div>
<div class="gf-form">
<folder-picker initial-title="ctrl.initialFolderFilterTitle"
on-change="ctrl.onFolderChange($folder)"
label-class="width-4">
</folder-picker>
</div>
<div class="gf-form">
<label class="gf-form-label width-4">Tags</label>
<bootstrap-tagsinput ng-model="ctrl.dashboard.tags" tagclass="label label-tag" placeholder="add tags">
</bootstrap-tagsinput>
</div>
</div>
<div class="search-filter-box">
<a class="search-button-row-explore-link" target="_blank" href="https://grafana.com/dashboards?utm_source=grafana_search">
<img src="public/img/icn-dashboard-tiny.svg" width="20" /> Find dashboards on Grafana.com
</a>
</div>
</div>
</div>
</div>

View File

@ -9,16 +9,18 @@ export class SearchCtrl {
selectedIndex: number;
results: any;
currentSearchId: number;
tagsMode: boolean;
showImport: boolean;
dismiss: any;
ignoreClose: any;
isLoading: boolean;
initialFolderFilterTitle: string;
/** @ngInject */
constructor($scope, private $location, private $timeout, private searchSrv: SearchSrv, $rootScope) {
$rootScope.onAppEvent('show-dash-search', this.openSearch.bind(this), $scope);
$rootScope.onAppEvent('hide-dash-search', this.closeSearch.bind(this), $scope);
this.initialFolderFilterTitle = "All";
}
closeSearch() {
@ -44,14 +46,6 @@ export class SearchCtrl {
this.query.starred = true;
}
if (payload && payload.tagsMode) {
return this.$timeout(() => {
this.ignoreClose = false;
this.giveSearchFocus = this.giveSearchFocus + 1;
this.getTags();
}, 100);
}
this.$timeout(() => {
this.ignoreClose = false;
this.giveSearchFocus = this.giveSearchFocus + 1;
@ -70,14 +64,6 @@ export class SearchCtrl {
this.moveSelection(-1);
}
if (evt.keyCode === 13) {
if (this.tagsMode) {
var tag = this.results[this.selectedIndex];
if (tag) {
this.filterByTag(tag.term, null);
}
return;
}
var selectedDash = this.results[this.selectedIndex];
if (selectedDash) {
this.$location.search({});
@ -93,7 +79,6 @@ export class SearchCtrl {
}
searchDashboards() {
this.tagsMode = false;
this.currentSearchId = this.currentSearchId + 1;
var localSearchId = this.currentSearchId;
@ -129,12 +114,8 @@ export class SearchCtrl {
getTags() {
return this.searchSrv.getDashboardTags().then((results) => {
this.tagsMode = !this.tagsMode;
this.results = results;
this.giveSearchFocus = this.giveSearchFocus + 1;
if ( !this.tagsMode ) {
this.search();
}
});
}

View File

@ -2,6 +2,12 @@
<img src="public/img/grafana_icon.svg"></img>
</a>
<a class="sidemenu__logo_small_breakpoint" ng-click="ctrl.toggleSideMenuSmallBreakpoint()">
<img src="public/img/grafana_icon.svg"></img>
<p class="sidemenu__close"><i class="fa fa-times"></i>&nbsp;Close</p>
</a>
<div class="sidemenu__top">
<div ng-repeat="item in ::ctrl.mainLinks" class="sidemenu-item dropdown">
<a href="{{::item.url}}" class="sidemenu-link" target="{{::item.target}}">

View File

@ -11,6 +11,7 @@ export class SideMenuCtrl {
bottomNav: any;
loginUrl: string;
isSignedIn: boolean;
smallBPSideMenuOpen = false;
/** @ngInject */
constructor(private $scope, private $rootScope, private $location, private contextSrv, private $timeout) {
@ -28,6 +29,10 @@ export class SideMenuCtrl {
}
this.$scope.$on('$routeChangeSuccess', () => {
if (this.smallBPSideMenuOpen) {
this.contextSrv.setSideMenuForSmallBreakpoint(false, true);
this.smallBPSideMenuOpen = false;
}
this.loginUrl = 'login?redirect=' + encodeURIComponent(this.$location.path());
});
}
@ -39,6 +44,11 @@ export class SideMenuCtrl {
});
}
toggleSideMenuSmallBreakpoint() {
this.smallBPSideMenuOpen = !this.smallBPSideMenuOpen;
this.contextSrv.setSideMenuForSmallBreakpoint(this.smallBPSideMenuOpen, false);
}
switchOrg() {
this.$rootScope.appEvent('show-modal', {
templateHtml: '<org-switcher dismiss="dismiss()"></org-switcher>',

View File

@ -1,19 +1,27 @@
///<reference path="../headers/common.d.ts" />
import coreModule from 'app/core/core_module';
import config from 'app/core/config';
import _ from 'lodash';
export interface NavModelItem {
title: string;
text: string;
url: string;
icon?: string;
iconUrl?: string;
img?: string;
id: string;
active?: boolean;
hideFromTabs?: boolean;
divider?: boolean;
children: NavModelItem[];
}
export interface NavModel {
section: NavModelItem;
menu: NavModelItem[];
export class NavModel {
breadcrumbs: NavModelItem[];
main: NavModelItem;
node: NavModelItem;
constructor() {
this.breadcrumbs = [];
}
}
export class NavModelSrv {
@ -31,15 +39,32 @@ export class NavModelSrv {
getNav(...args) {
var children = this.navItems;
var nav = {breadcrumbs: [], node: null};
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.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;
}

View File

@ -36,12 +36,11 @@ export class NewDashboardCtrl {
meta: { canStar: false, canShare: false, isNew: true },
dashboard: {
title: "New dashboard",
rows: [
panels: [
{
title: 'Dashboard Row',
height: '350px',
panels: [],
isNew: true,
type: 'add-panel',
gridPos: {x: 0, y: 0, w: 12, h: 9},
title: 'Panel Title',
}
]
},

View File

@ -48,12 +48,6 @@ function setupAngularRoutes($routeProvider, $locationProvider) {
reloadOnSearch: false,
pageClass: 'page-dashboard',
})
.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',

View File

@ -27,9 +27,10 @@ export class ContextSrv {
isGrafanaAdmin: any;
isEditor: any;
sidemenu: any;
sidemenuSmallBreakpoint = false;
constructor() {
this.sidemenu = store.getBool('grafana.sidemenu', false);
this.sidemenu = store.getBool('grafana.sidemenu', true);
if (!config.buildInfo) {
config.buildInfo = {};
@ -55,7 +56,11 @@ export class ContextSrv {
toggleSideMenu() {
this.sidemenu = !this.sidemenu;
store.set('grafana.sidemenu',this.sidemenu);
store.set('grafana.sidemenu', this.sidemenu);
}
setSideMenuForSmallBreakpoint(show: boolean, persistToggle: boolean) {
this.sidemenuSmallBreakpoint = show;
}
}

View File

@ -10,7 +10,7 @@ class AdminSettingsCtrl {
/** @ngInject **/
constructor($scope, backendSrv, navModelSrv) {
this.navModel = navModelSrv.getNav('cfg', 'admin', 'server-settings');
this.navModel = navModelSrv.getNav('cfg', 'admin', 'server-settings', 1);
backendSrv.get('/api/admin/settings').then(function(settings) {
$scope.settings = settings;
@ -24,7 +24,7 @@ class AdminHomeCtrl {
/** @ngInject **/
constructor(navModelSrv) {
this.navModel = navModelSrv.getNav('cfg', 'admin');
this.navModel = navModelSrv.getNav('cfg', 'admin', 1);
}
}
@ -34,7 +34,7 @@ export class AdminStatsCtrl {
/** @ngInject */
constructor(backendSrv: any, navModelSrv) {
this.navModel = navModelSrv.getNav('cfg', 'admin', 'server-stats');
this.navModel = navModelSrv.getNav('cfg', 'admin', 'server-stats', 1);
backendSrv.get('/api/admin/stats').then(stats => {
this.stats = stats;
@ -42,16 +42,6 @@ export class AdminStatsCtrl {
}
}
export class ConfigurationHomeCtrl {
navModel: any;
/** @ngInject */
constructor(navModelSrv) {
this.navModel = navModelSrv.getNav('cfg');
}
}
coreModule.controller('ConfigurationHomeCtrl', ConfigurationHomeCtrl);
coreModule.controller('AdminSettingsCtrl', AdminSettingsCtrl);
coreModule.controller('AdminHomeCtrl', AdminHomeCtrl);
coreModule.controller('AdminStatsCtrl', AdminStatsCtrl);

View File

@ -5,7 +5,7 @@ export class AdminEditOrgCtrl {
/** @ngInject */
constructor($scope, $routeParams, backendSrv, $location, navModelSrv) {
$scope.init = function() {
$scope.navModel = navModelSrv.getNav('cfg', 'admin', 'global-orgs');
$scope.navModel = navModelSrv.getNav('cfg', 'admin', 'global-orgs', 1);
if ($routeParams.id) {
$scope.getOrg($routeParams.id);

View File

@ -8,7 +8,7 @@ export class AdminEditUserCtrl {
$scope.user = {};
$scope.newOrg = { name: '', role: 'Editor' };
$scope.permissions = {};
$scope.navModel = navModelSrv.getNav('cfg', 'admin', 'global-users');
$scope.navModel = navModelSrv.getNav('cfg', 'admin', 'global-users', 1);
$scope.init = function() {
if ($routeParams.id) {

View File

@ -5,7 +5,7 @@ export class AdminListOrgsCtrl {
/** @ngInject */
constructor($scope, backendSrv, navModelSrv) {
$scope.init = function() {
$scope.navModel = navModelSrv.getNav('cfg', 'admin', 'global-orgs');
$scope.navModel = navModelSrv.getNav('cfg', 'admin', 'global-orgs', 1);
$scope.getOrgs();
};

View File

@ -10,7 +10,7 @@ export default class AdminListUsersCtrl {
/** @ngInject */
constructor(private $scope, private backendSrv, navModelSrv) {
this.navModel = navModelSrv.getNav('cfg', 'admin', 'global-users');
this.navModel = navModelSrv.getNav('cfg', 'admin', 'global-users', 1);
this.query = '';
this.getUsers();
}

View File

@ -1,35 +1,11 @@
<navbar model="ctrl.navModel"></navbar>
<page-header model="ctrl.navModel"></page-header>
<div class="page-container">
<div class="page-header">
<page-h1 model="ctrl.navModel"></page-h1>
</div>
<div class="page-container page-body">
<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}}">
<div class="card-item-header">
<div class="card-item-type">
</div>
</div>
<div class="card-item-body">
<figure class="card-item-figure">
<i class="{{navItem.icon}}"></i>
</figure>
<div class="card-item-details">
<div class="card-item-name">
{{navItem.text}}
</div>
<div class="card-item-sub-name">
{{navItem.description}}
</div>
</div>
</div>
</a>
</li>
</ol>
</section>
<div class="grafana-info-box span8">
Grafana is a multi-tenant system where most can be configured per organization. These
admin pages are for server admins where you can manage orgs, & all users across all orgs.
</div>
</div>

View File

@ -1,33 +0,0 @@
<div class="scroll-canvas" grafana-scrollbar>
<navbar model="ctrl.navModel"></navbar>
<div class="page-container">
<div class="page-header">
<page-h1 model="ctrl.navModel"></page-h1>
</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}}">
<div class="card-item-header">
<div class="card-item-type">
</div>
</div>
<div class="card-item-body">
<figure class="card-item-figure">
<i class="{{navItem.icon}}"></i>
</figure>
<div class="card-item-details">
<div class="card-item-name">
{{navItem.text}}
</div>
<div class="card-item-sub-name">
{{navItem.description}}
</div>
</div>
</div>
</a>
</li>
</ol>
</section>
</div>

View File

@ -1,9 +1,7 @@
<navbar model="navModel"></navbar>
<page-header model="navModel"></page-header>
<div class="page-container">
<div class="page-header">
<h1>Edit Organization</h1>
</div>
<div class="page-container page-body">
<h2 class="page-sub-heading">Edit Organization</h2>
<form name="orgDetailsForm" class="gf-form-group">
<div class="gf-form">

View File

@ -1,8 +1,9 @@
<navbar model="navModel"></navbar>
<page-header model="navModel"></page-header>
<div class="page-container">
<div class="page-header">
<h1>Edit User</h1>
<div class="page-container page-body">
<div class="page-sub-heading">
<h2>Edit User</h2>
</div>
<form name="userForm" class="gf-form-group">
@ -54,7 +55,7 @@
<form name="addOrgForm" class="gf-form-group">
<div class="gf-form-inline">
<div class="gf-form">
<span class="gf-form-label width-12">Add organization</span>
<span class="gf-form-label">Add</span>
<input type="text" ng-model="newOrg.name" bs-typeahead="searchOrgs" required class="gf-form-input max-width-20" placeholder="organization name">
</div>
<div class="gf-form">
@ -67,12 +68,14 @@
</div>
</form>
<table class="grafana-options-table">
<tr>
<th>Name</th>
<th>Role</th>
<th></th>
</tr>
<table class="filter-table">
<thead>
<tr>
<th>Name</th>
<th>Role</th>
<th></th>
</tr>
</thead>
<tr ng-repeat="org in orgs">
<td>
{{org.name}} <span class="label label-info" ng-show="org.orgId === user.orgId">Current</span>

View File

@ -1,7 +1,7 @@
<navbar model="navModel"></navbar>
<page-header model="navModel"></page-header>
<div class="page-container">
<div class="page-header">
<div class="page-container page-body">
<div class="page-sub-heading">
<h1>Add new user</h1>
</div>

View File

@ -1,8 +1,12 @@
<navbar model="navModel"></navbar>
<page-header model="navModel"></page-header>
<div class="page-container">
<div class="page-header">
<h1>Organizations</h1>
<div class="page-container page-body">
<div class="page-action-bar">
<div class="page-action-bar__spacer"></div>
<a class="page-header__cta btn btn-success" href="org/new">
<i class="fa fa-plus"></i>
New Org
</a>
</div>
<table class="filter-table form-inline">

View File

@ -1,25 +1,22 @@
<navbar model="ctrl.navModel"></navbar>
<page-header model="ctrl.navModel"></page-header>
<div class="page-container">
<div class="page-header">
<h1>Server settings</h1>
<div class="page-container page-body">
<div class="grafana-info-box span8" style="margin: 20px 0 25px 0">
These system settings are defined in grafana.ini or custom.ini (or overridden in ENV variables).
To change these you currently need to restart grafana.
</div>
<div class="grafana-info-box span8" style="margin: 20px 0 25px 0">
These system settings are defined in grafana.ini or custom.ini (or overridden in ENV variables).
To change these you currently need to restart grafana.
</div>
<table class="grafana-options-table">
<tr ng-repeat-start="(secName, secValue) in settings">
<td class="admin-settings-section">{{secName}}</td>
<td></td>
</tr>
<tr ng-repeat="(keyName, keyValue) in secValue" ng-repeat-end>
<td style="padding-left: 25px;">{{keyName}}</td>
<td>{{keyValue}}</td>
</tr>
</table>
<table class="filter-table">
<tr ng-repeat-start="(secName, secValue) in settings">
<td class="admin-settings-section">{{secName}}</td>
<td></td>
</tr>
<tr ng-repeat="(keyName, keyValue) in secValue" ng-repeat-end>
<td style="padding-left: 25px;">{{keyName}}</td>
<td>{{keyValue}}</td>
</tr>
</table>
</div>

View File

@ -1,10 +1,6 @@
<navbar model="ctrl.navModel"></navbar>
<div class="page-container">
<div class="page-header">
<h1>Stats</h1>
</div>
<page-header model="ctrl.navModel"></page-header>
<div class="page-container page-body">
<table class="filter-table form-inline">
<thead>
<tr>

View File

@ -1,22 +1,18 @@
<navbar model="ctrl.navModel"></navbar>
<div class="page-container">
<div class="page-header">
<h1>Users</h1>
<page-header model="ctrl.navModel"></page-header>
<div class="page-container page-body">
<div class="page-action-bar">
<div class="gf-form">
<label class="gf-form-label">Search</label>
<input class="gf-form-input width-15" type="text" placeholder="Find user by name/login/email" tabindex="1" give-focus="true" ng-model="ctrl.query" ng-model-options="{ debounce: 500 }" spellcheck='false' ng-change="ctrl.getUsers()" />
</div>
<div class="page-action-bar__spacer"></div>
<a class="btn btn-success" href="admin/users/create">
<i class="fa fa-plus"></i>
Add new user
</a>
</div>
<div class="gf-form pull-right gf-form-group">
<label class="gf-form-label">Search</label>
<span style="position: relative;">
<input class="gf-form-input width-15" type="text" placeholder="Find user by name/login/email" tabindex="1" give-focus="true" ng-model="ctrl.query" ng-model-options="{ debounce: 500 }" spellcheck='false' ng-change="ctrl.getUsers()" />
</span>
</div>
<div class="admin-list-table">
<table class="filter-table form-inline">
<thead>

View File

@ -23,7 +23,7 @@ export class AlertListCtrl {
/** @ngInject */
constructor(private backendSrv, private $location, navModelSrv) {
this.navModel = navModelSrv.getNav('alerting');
this.navModel = navModelSrv.getNav('alerting', 'alert-list', 0);
var params = $location.search();
this.filters.state = params.state || null;

View File

@ -9,7 +9,7 @@ export class AlertNotificationEditCtrl {
testSeverity = "critical";
notifiers: any;
notifierTemplateId: string;
isNew: boolean;
model: any;
defaults: any = {
type: 'email',
@ -23,7 +23,8 @@ export class AlertNotificationEditCtrl {
/** @ngInject */
constructor(private $routeParams, private backendSrv, private $location, private $templateCache, navModelSrv) {
this.navModel = navModelSrv.getNav('alerting', 'channels');
this.navModel = navModelSrv.getNav('alerting', 'channels', 0);
this.isNew = !this.$routeParams.id;
this.backendSrv.get(`/api/alert-notifiers`).then(notifiers => {
this.notifiers = notifiers;

View File

@ -9,7 +9,7 @@ export class AlertNotificationsListCtrl {
/** @ngInject */
constructor(private backendSrv, navModelSrv) {
this.loadNotifications();
this.navModel = navModelSrv.getNav('alerting', 'channels');
this.navModel = navModelSrv.getNav('alerting', 'channels', 0);
}
loadNotifications() {

View File

@ -1,29 +1,23 @@
<navbar model="ctrl.navModel"></navbar>
<page-header model="ctrl.navModel"></page-header>
<div class="page-container" >
<div class="page-header">
<page-h1 model="ctrl.navModel"></page-h1>
<div class="page-container page-body">
<a class="btn btn-secondary" ng-click="ctrl.openHowTo()">
<i class="fa fa-info-circle"></i>
How to add an alert
</a>
<a class="btn btn-inverse" href="alerting/notifications" >
<i class="fa fa-bell"></i>
Notification channels
</a>
</div>
<div class="gf-form-group">
<div class="gf-form-inline">
<div class="gf-form">
<label class="gf-form-label">Filter by state</label>
<div class="gf-form-select-wrapper width-13">
<select class="gf-form-input" ng-model="ctrl.filters.state" ng-options="f.value as f.text for f in ctrl.stateFilters" ng-change="ctrl.filtersChanged()">
</select>
</div>
<div class="page-action-bar">
<div class="gf-form">
<label class="gf-form-label">Filter by state</label>
<div class="gf-form-select-wrapper width-13">
<select class="gf-form-input" ng-model="ctrl.filters.state" ng-options="f.value as f.text for f in ctrl.stateFilters" ng-change="ctrl.filtersChanged()">
</select>
</div>
</div>
<div class="page-action-bar__spacer">
</div>
<a class="btn btn-secondary" ng-click="ctrl.openHowTo()">
<i class="fa fa-info-circle"></i>
How to add an alert
</a>
</div>
<section class="card-section card-list-layout-list">

View File

@ -1,9 +1,9 @@
<navbar model="ctrl.navModel"></navbar>
<page-header model="ctrl.navModel"></page-header>
<div class="page-container">
<div class="page-header">
<page-h1 model="ctrl.navModel"></page-h1>
</div>
<div class="page-container page-body">
<h3 class="page-sub-heading" ng-hide="ctrl.isNew">Edit Notification Channel</h3>
<h3 class="page-sub-heading" ng-show="ctrl.isNew">New Notification Channel</h3>
<form name="ctrl.theForm" ng-if="ctrl.notifiers">
<div class="gf-form-group">

View File

@ -1,10 +1,12 @@
<navbar model="ctrl.navModel"></navbar>
<page-header model="ctrl.navModel"></page-header>
<div class="page-container" >
<div class="page-header">
<page-h1 model="ctrl.navModel"></page-h1>
<div class="page-container page-body">
<div class="page-action-bar">
<div class="page-action-bar__spacer">
</div>
<a href="alerting/notification/new" class="btn btn-success pull-right">
<a href="alerting/notification/new" class="btn btn-success">
<i class="fa fa-plus"></i>
New Channel
</a>
</div>

View File

@ -17,7 +17,7 @@ export class DashboardListCtrl {
/** @ngInject */
constructor(private backendSrv, navModelSrv, private $q, private searchSrv: SearchSrv) {
this.navModel = navModelSrv.getNav('dashboards', 'dashboards');
this.navModel = navModelSrv.getNav('dashboards', 'dashboards', 0);
this.query = {query: '', mode: 'tree', tag: [], starred: false};
this.selectedStarredFilter = this.starredFilterOptions[0];

View File

@ -72,8 +72,18 @@ export class AddPanelPanel extends React.Component<AddPanelPanelProps, AddPanelP
render() {
return (
<div className="panel-container">
<div className="add-panel">{this.state.panelPlugins.map(this.renderPanelItem.bind(this))}</div>
<div className="add-panel">
<div className="add-panel__header">
<i className="gicon gicon-add-panel"></i>
<span className="add-panel__title">New Panel</span>
<span className="add-panel__sub-title">Select a visualization</span>
</div>
<div className="add-panel__items">
{this.state.panelPlugins.map(this.renderPanelItem.bind(this))}
</div>
</div>
</div>
);
}
}

View File

@ -1,95 +1,66 @@
<div class="navbar">
<div class="navbar-inner">
<div>
<a class="navbar-page-btn" ng-click="ctrl.showSearch()">
<i class="gicon gicon-dashboard"></i>
{{ctrl.dashboard.title}}
<i class="fa fa-caret-down"></i>
</a>
<ul class="nav dash-playlist-actions" ng-if="ctrl.playlistSrv.isPlaying">
<li>
<a ng-click="ctrl.playlistSrv.prev()"><i class="fa fa-step-backward"></i></a>
</li>
<li>
<a ng-click="ctrl.playlistSrv.stop()"><i class="fa fa-stop"></i></a>
</li>
<li>
<a ng-click="ctrl.playlistSrv.next()"><i class="fa fa-step-forward"></i></a>
</li>
</ul>
<ul class="nav dashnav-action-icons">
<li ng-show="::ctrl.dashboard.meta.canStar">
<a class="pointer" ng-click="ctrl.starDashboard()">
<i class="fa" ng-class="{'fa-star-o': !ctrl.dashboard.meta.isStarred, 'fa-star': ctrl.dashboard.meta.isStarred}" style="color: orange;"></i>
</a>
</li>
<li ng-show="::ctrl.dashboard.meta.canShare" class="dropdown">
<a class="pointer" ng-click="ctrl.hideTooltip($event)" bs-tooltip="'Share dashboard'" data-placement="bottom" data-toggle="dropdown"><i class="fa fa-share-square-o"></i></a>
<ul class="dropdown-menu">
<li>
<a class="pointer" ng-click="ctrl.shareDashboard(0)">
<i class="fa fa-link"></i> Link to Dashboard
<div class="dropdown-desc">Share an internal link to the current dashboard. Some configuration options available.</div>
</a>
</li>
<li>
<a class="pointer" ng-click="ctrl.shareDashboard(1)">
<i class="icon-gf icon-gf-snapshot"></i>Snapshot
<div class="dropdown-desc">Interactive, publically accessible dashboard. Sensitive data is stripped out.</div>
</a>
</li>
<li>
<a class="pointer" ng-click="ctrl.shareDashboard(2)">
<i class="fa fa-cloud-upload"></i>Export
<div class="dropdown-desc">Export the dashboard to a JSON file for others and to share on Grafana.com</div>
</a>
</li>
</ul>
</li>
<li ng-show="::ctrl.dashboard.meta.canSave">
<a ng-click="ctrl.saveDashboard()" bs-tooltip="'Save dashboard <br> CTRL+S'" data-placement="bottom"><i class="fa fa-save"></i></a>
</li>
<li ng-if="::ctrl.dashboard.snapshot.originalUrl">
<a ng-href="{{ctrl.dashboard.snapshot.originalUrl}}" bs-tooltip="'Open original dashboard'" data-placement="bottom"><i class="fa fa-link"></i></a>
</li>
<li class="dropdown">
<a class="pointer" data-toggle="dropdown">
<i class="fa fa-cog"></i>
</a>
<ul class="dropdown-menu dropdown-menu--navbar">
<li ng-repeat="navItem in ::ctrl.navModel.menu" ng-class="{active: navItem.active}">
<a class="pointer" ng-href="{{::navItem.url}}" ng-click="ctrl.navItemClicked(navItem, $event)">
<i class="{{::navItem.icon}}" ng-show="::navItem.icon"></i>
{{::navItem.title}}
</a>
</li>
</ul>
</li>
<li class="navbar-mini-btn-wrapper" ng-show="::ctrl.dashboard.meta.canSave">
<button class="btn btn-secondary btn-mini" ng-click="ctrl.addPanel()">
<i class="fa fa-plus-circle"></i> Add Panel
</button>
</li>
</ul>
<div class="nav nav--grow">
</div>
<ul class="nav pull-right">
<li ng-show="ctrl.dashboard.meta.fullscreen" class="dashnav-back-to-dashboard">
<a ng-click="ctrl.exitFullscreen()">
Back to dashboard
</a>
</li>
<li ng-if="!ctrl.dashboard.timepicker.hidden">
<gf-time-picker dashboard="ctrl.dashboard"></gf-time-picker>
</li>
</ul>
</div>
<ul class="nav dash-playlist-actions" ng-if="ctrl.playlistSrv.isPlaying">
<li>
<a ng-click="ctrl.playlistSrv.prev()"><i class="fa fa-step-backward"></i></a>
</li>
<li>
<a ng-click="ctrl.playlistSrv.stop()"><i class="fa fa-stop"></i></a>
</li>
<li>
<a ng-click="ctrl.playlistSrv.next()"><i class="fa fa-step-forward"></i></a>
</li>
</ul>
<div class="navbar__spacer"></div>
<div class="navbar-buttons navbar-buttons--actions">
<button class="btn navbar-button navbar-button--add-panel" ng-show="::ctrl.dashboard.meta.canSave" bs-tooltip="'Add panel'" data-placement="bottom" ng-click="ctrl.addPanel()">
<i class="gicon gicon-add-panel"></i>
</button>
<button class="btn navbar-button" ng-show="::ctrl.dashboard.meta.canStar" ng-click="ctrl.starDashboard()" bs-tooltip="'Mark as favorite'" data-placement="bottom">
<i class="fa" ng-class="{'fa-star-o': !ctrl.dashboard.meta.isStarred, 'fa-star': ctrl.dashboard.meta.isStarred}"></i>
</button>
<button class="btn navbar-button" ng-show="::ctrl.dashboard.meta.canShare" ng-click="ctrl.shareDashboard(0)" bs-tooltip="'Share dashboard'" data-placement="bottom">
<i class="fa fa-share-square-o"></i></a>
</button>
<button class="btn navbar-button" ng-show="::ctrl.dashboard.meta.canSave" ng-click="ctrl.saveDashboard()" bs-tooltip="'Save dashboard <br> CTRL+S'" data-placement="bottom">
<i class="fa fa-save"></i>
</button>
<button class="btn navbar-button" ng-if="::ctrl.dashboard.snapshot.originalUrl" ng-href="{{ctrl.dashboard.snapshot.originalUrl}}" bs-tooltip="'Open original dashboard'" data-placement="bottom">
<i class="fa fa-link"></i>
</button>
<div class="dropdown">
<button class="btn navbar-button" data-toggle="dropdown" bs-tooltip="'Settings'" data-placement="bottom">
<i class="fa fa-cog"></i>
</button>
<ul class="dropdown-menu dropdown-menu--navbar">
<li ng-repeat="navItem in ::ctrl.navModel.menu" ng-class="{active: navItem.active}">
<a class="pointer" ng-href="{{::navItem.url}}" ng-click="ctrl.navItemClicked(navItem, $event)">
<i class="{{::navItem.icon}}" ng-show="::navItem.icon"></i>
{{::navItem.title}}
</a>
</li>
</ul>
</div>
</div>
<gf-time-picker class="gf-timepicker-nav" dashboard="ctrl.dashboard" ng-if="!ctrl.dashboard.timepicker.hidden"></gf-time-picker>
</div>
<dashboard-search></dashboard-search>

View File

@ -150,7 +150,7 @@ export class DashNavCtrl {
this.dashboard.addPanel({
type: 'add-panel',
gridPos: {x: 0, y: 0, w: 12, h: 9},
title: 'New Graph',
title: 'Panel Title',
});
}

View File

@ -1,163 +1,131 @@
<div class="scroll-canvas" grafana-scrollbar>
<navbar model="ctrl.navModel"></navbar>
<div class="page-container" style="height: 95%">
<div class="page-header">
<h1>Dashboards</h1>
<page-header model="ctrl.navModel"></page-header>
<a class="btn btn-inverse" href="/dashboard/new">
<i class="gicon gicon-dashboard-new"></i>
Dashboard
</a>
<a class="btn btn-inverse" href="/dashboard/new/?editview=new-folder">
<i class="gicon gicon-folder-new"></i>
Folder
</a>
<div class="page-container page-body">
<div class="page-action-bar">
<div class="gf-form gf-form--grow">
<label class="gf-form-label">Search</label>
<input type="text" class="gf-form-input max-width-30" placeholder="Find Dashboard by name" tabindex="1" give-focus="true" ng-model="ctrl.query.query" ng-model-options="{ debounce: 500 }" spellcheck='false' ng-change="ctrl.onQueryChange()" />
</div>
<div class="gf-form-group">
<div class="gf-form width-15">
<span style="position: relative;">
<input type="text" class="gf-form-input" placeholder="Find Dashboard by name" tabindex="1" give-focus="true"
ng-model="ctrl.query.query" ng-model-options="{ debounce: 500 }" spellcheck='false' ng-change="ctrl.onQueryChange()" />
</span>
<div class="page-action-bar__spacer"></div>
<a class="btn btn-success" href="/dashboard/new">
<i class="fa fa-plus"></i>
Dashboard
</a>
<a class="btn btn-success" href="/dashboard/new/?editview=new-folder">
<i class="fa fa-plus"></i>
Folder
</a>
</div>
<div class="gf-form" ng-if="ctrl.query.tag.length">
Filters:
<span ng-repeat="tagName in ctrl.query.tag">
<a ng-click="ctrl.removeTag(tagName, $event)" tag-color-from-name="tagName" class="label label-tag">
<i class="fa fa-remove"></i>
{{tagName}}
</a>
</span>
</div>
<div class="gf-form">
<div class="gf-form-button-row"
ng-show="ctrl.hasFilters">
<button
type="button"
class="btn gf-form-button btn-inverse btn-small"
ng-click="ctrl.clearFilters()">
<i class="fa fa-close"></i> Clear current search query and filters
</button>
</div>
</div>
<div class="gf-form-group">
<div class="gf-form-button-row">
<button type="button"
class="btn gf-form-button btn-secondary"
ng-disabled="!ctrl.canMove"
ng-click="ctrl.moveTo()"
bs-tooltip="ctrl.canMove ? '' : 'Select a dashboard to move (cannot move folders)'" data-placement="bottom">
<i class="fa fa-exchange"></i>&nbsp;&nbsp;Move to...
</button>
<button type="button"
class="btn gf-form-button btn-inverse"
ng-click="ctrl.delete()"
ng-disabled="!ctrl.canDelete">
<i class="fa fa-trash"></i>&nbsp;&nbsp;Delete
</button>
</div>
</div>
<div class="dashboard-list">
<div class="search-results-filter-row">
<gf-form-switch
on-change="ctrl.onSelectAllChanged()"
checked="ctrl.selectAllChecked"
/>
<div class="search-results-filter-row__filters">
<select
class="search-results-filter-row__filters-item gf-form-input"
ng-model="ctrl.selectedStarredFilter"
ng-options="t.text disable when t.disabled for t in ctrl.starredFilterOptions"
ng-change="ctrl.onStarredFilterChange()"
/>
<select
class="search-results-filter-row__filters-item gf-form-input"
ng-model="ctrl.selectedTagFilter"
ng-options="t.term disable when t.disabled for t in ctrl.tagFilterOptions"
ng-change="ctrl.onTagFilterChange()"
/>
</div>
</div>
<div class="search-results-container" ng-show="ctrl.sections.length > 0" grafana-scrollbar>
<div ng-repeat="section in ctrl.sections" class="search-section">
<div class="gf-form" ng-if="ctrl.query.tag.length">
Filters:
<span ng-repeat="tagName in ctrl.query.tag">
<a ng-click="ctrl.removeTag(tagName, $event)" tag-color-from-name="tagName" class="label label-tag">
<i class="fa fa-remove"></i>
{{tagName}}
</a>
</span>
</div>
<div class="gf-form">
<div class="gf-form-button-row"
ng-show="ctrl.hasFilters">
<button
type="button"
class="btn gf-form-button btn-inverse btn-small"
ng-click="ctrl.clearFilters()">
<i class="fa fa-close"></i> Clear current search query and filters
</button>
<div class="search-section__header__with-checkbox" ng-hide="section.hideHeader">
<gf-form-switch
on-change="ctrl.selectionChanged()"
checked="section.checked">
</gf-form-switch>
<a class="search-section__header pointer" ng-click="ctrl.toggleFolder(section)" ng-hide="section.hideHeader">
<i class="search-section__header__icon" ng-class="section.icon"></i>
<span class="search-section__header__text">{{::section.title}}</span>
<i class="fa fa-minus search-section__header__toggle" ng-show="section.expanded"></i>
<i class="fa fa-plus search-section__header__toggle" ng-hide="section.expanded"></i>
</a>
</div>
</div>
<div class="gf-form-group">
<div class="gf-form-button-row">
<button type="button"
class="btn gf-form-button btn-secondary"
ng-disabled="!ctrl.canMove"
ng-click="ctrl.moveTo()"
bs-tooltip="ctrl.canMove ? '' : 'Select a dashboard to move (cannot move folders)'" data-placement="bottom">
<i class="fa fa-exchange"></i>&nbsp;&nbsp;Move to...
</button>
<button type="button"
class="btn gf-form-button btn-inverse"
ng-click="ctrl.delete()"
ng-disabled="!ctrl.canDelete">
<i class="fa fa-trash"></i>&nbsp;&nbsp;Delete
</button>
</div>
</div>
<div class="dashboard-list">
<div class="search-results-filter-row">
<div ng-if="section.expanded">
<div ng-repeat="item in section.items" class="search-item__with-checkbox" ng-class="{'selected': item.selected}">
<gf-form-switch
on-change="ctrl.onSelectAllChanged()"
checked="ctrl.selectAllChecked"
/>
<div class="search-results-filter-row__filters">
<select
class="search-results-filter-row__filters-item gf-form-input"
ng-model="ctrl.selectedStarredFilter"
ng-options="t.text disable when t.disabled for t in ctrl.starredFilterOptions"
ng-change="ctrl.onStarredFilterChange()"
/>
<select
class="search-results-filter-row__filters-item gf-form-input"
ng-model="ctrl.selectedTagFilter"
ng-options="t.term disable when t.disabled for t in ctrl.tagFilterOptions"
ng-change="ctrl.onTagFilterChange()"
/>
</div>
</div>
<div class="search-results-container" ng-show="ctrl.sections.length > 0" grafana-scrollbar>
<div ng-repeat="section in ctrl.sections" class="search-section">
<div class="search-section__header__with-checkbox" ng-hide="section.hideHeader">
<gf-form-switch
on-change="ctrl.selectionChanged()"
checked="section.checked">
</gf-form-switch>
<a class="search-section__header pointer" ng-click="ctrl.toggleFolder(section)" ng-hide="section.hideHeader">
<i class="search-section__header__icon" ng-class="section.icon"></i>
<span class="search-section__header__text">{{::section.title}}</span>
<i class="fa fa-minus search-section__header__toggle" ng-show="section.expanded"></i>
<i class="fa fa-plus search-section__header__toggle" ng-hide="section.expanded"></i>
</a>
</div>
<div ng-if="section.expanded">
<div ng-repeat="item in section.items" class="search-item__with-checkbox" ng-class="{'selected': item.selected}">
<gf-form-switch
on-change="ctrl.selectionChanged()"
checked="item.checked" />
<a ng-href="{{::item.url}}" class="search-item">
<span class="search-item__icon">
<i class="fa fa-th-large"></i>
</span>
<span class="search-item__body">
<div class="search-item__body-title">{{::item.title}}</div>
<div class="search-item__body-sub-title" ng-show="item.folderTitle && section.hideHeader">
<i class="fa fa-folder-o"></i>
{{::item.folderTitle}}
</div>
</span>
<span class="search-item__tags">
<span ng-click="ctrl.filterByTag(tag, $event)" ng-repeat="tag in item.tags" tag-color-from-name="tag" class="label label-tag">
{{tag}}
</span>
</span>
<span class="search-item__actions">
<i class="fa" ng-class="{'fa-star': item.isStarred, 'fa-star-o': !item.isStarred}"></i>
</span>
</a>
</div>
</div>
</div>
<!-- <div bindonce class="search-section" ng-repeat="dashboard in ctrl.dashboards">
<gf-form-switch
switch-class="gf-form-switch--table-cell"
on-change="ctrl.selectionChanged()"
checked="dashboard.checked">
</gf-form-switch>
<a class="search-item pointer "
bo-href-i="{{dashboard.url}}">
<span class="search-item__icon">
<i class="fa fa-th-large"></i>
</span>
<span class="search-result-tags">
<span ng-click="ctrl.filterByTag(tag, $event)" bindonce ng-repeat="tag in dashboard.tags" tag-color-from-name="tag" class="label label-tag">
{{tag}}
</span>
<i class="fa" bo-class="{'fa-star': dashboard.isStarred, 'fa-star-o': !dashboard.isStarred}"></i>
</span>
<span class="search-result-link">
<i class="fa search-result-icon"></i>
<span bo-text="dashboard.title" />
</span>
</a>
</div> -->
on-change="ctrl.selectionChanged()"
checked="item.checked" />
<a ng-href="{{::item.url}}" class="search-item">
<span class="search-item__icon">
<i class="fa fa-th-large"></i>
</span>
<span class="search-item__body">
<div class="search-item__body-title">{{::item.title}}</div>
<div class="search-item__body-sub-title" ng-show="item.folderTitle && section.hideHeader">
<i class="fa fa-folder-o"></i>
{{::item.folderTitle}}
</div>
</span>
<span class="search-item__tags">
<span ng-click="ctrl.filterByTag(tag, $event)" ng-repeat="tag in item.tags" tag-color-from-name="tag" class="label label-tag">
{{tag}}
</span>
</span>
<span class="search-item__actions">
<i class="fa" ng-class="{'fa-star': item.isStarred, 'fa-star-o': !item.isStarred}"></i>
</span>
</a>
</div>
</div>
</div>
<em class="muted" ng-hide="ctrl.sections.length > 0">
No Dashboards or Folders found.
</em>
</div>
</div>
</div>
<em class="muted" ng-hide="ctrl.sections.length > 0">
No Dashboards or Folders found.
</em>

View File

@ -1,38 +1,26 @@
<ul class="nav gf-timepicker-nav">
<div class="navbar-buttons navbar-buttons--zoom">
<button class="btn navbar-button navbar-button--tight" ng-click='ctrl.move(-1)'>
<i class="fa fa-chevron-left"></i>
</button>
<li class="dashnav-move-timeframe gf-timepicker-time-control" bs-tooltip="'Shift time backward <br> (left arrow key)'" data-placement="bottom">
<a ng-click='ctrl.move(-1)'>
<i class="fa fa-chevron-left"></i>
</a>
</li>
<li class="dashnav-zoom-out gf-timepicker-time-control" bs-tooltip="'Time range zoom out <br> CTRL+Z'" data-placement="bottom">
<a ng-click='ctrl.zoom(2)'>Zoom Out</a>
</li>
</li>
<li class="dashnav-move-timeframe gf-timepicker-time-control" bs-tooltip="'Shift time forward <br> (right arrow key)'" data-placement="bottom">
<a ng-click='ctrl.move(1)'>
<i class="fa fa-chevron-right"></i>
</a>
</li>
<button class="btn navbar-button" bs-tooltip="'Time range zoom out <br> CTRL+Z'" data-placement="bottom" ng-click='ctrl.zoom(2)'>
<i class="fa fa-search-minus"></i>
</button>
<li>
<a bs-tooltip="ctrl.tooltip" data-placement="bottom" ng-click="ctrl.openDropdown()" class="gf-timepicker-nav-btn">
<i class="fa fa-clock-o"></i>
<span ng-bind="ctrl.rangeString"></span>
<span ng-show="ctrl.isUtc" class="gf-timepicker-utc">
UTC
</span>
<button class="btn navbar-button navbar-button--tight" ng-click='ctrl.move(1)'>
<i class="fa fa-chevron-right"></i>
</button>
</div>
<span ng-show="ctrl.dashboard.refresh" class="text-warning">
&nbsp; Refresh every {{ctrl.dashboard.refresh}}
</span>
</a>
</li>
<div class="navbar-buttons">
<button bs-tooltip="ctrl.tooltip" data-placement="bottom" ng-click="ctrl.openDropdown()" class="btn navbar-button gf-timepicker-nav-btn">
<i class="fa fa-clock-o"></i>
<span ng-bind="ctrl.rangeString"></span>
<span ng-show="ctrl.isUtc" class="gf-timepicker-utc">UTC</span>
<span ng-show="ctrl.dashboard.refresh" class="text-warning">&nbsp; Refresh every {{ctrl.dashboard.refresh}}</span>
</button>
<li class="dashnav-refresh-action">
<a ng-click="ctrl.timeSrv.refreshDashboard()">
<i class="fa fa-refresh"></i>
</a>
</li>
</ul>
<button class="btn navbar-button navbar-button--refresh" ng-click="ctrl.timeSrv.refreshDashboard()">
<i class="fa fa-refresh"></i>
</button>
</div>

View File

@ -5,6 +5,7 @@ import './select_org_ctrl';
import './change_password_ctrl';
import './new_org_ctrl';
import './user_invite_ctrl';
import './user_groups_ctrl';
import './org_api_keys_ctrl';
import './org_details_ctrl';
import './prefs_control';

View File

@ -8,7 +8,7 @@ export class ChangePasswordCtrl {
$scope.command = {};
$scope.authProxyEnabled = config.authProxyEnabled;
$scope.ldapEnabled = config.ldapEnabled;
$scope.navModel = navModelSrv.getNav('profile', 'change-password');
$scope.navModel = navModelSrv.getNav('profile', 'change-password', 0);
$scope.changePassword = function() {
if (!$scope.userForm.$valid) { return; }

View File

@ -5,7 +5,7 @@ export class NewOrgCtrl {
/** @ngInject **/
constructor($scope, $http, backendSrv, navModelSrv) {
$scope.navModel = navModelSrv.getOrgNav(0);
$scope.navModel = navModelSrv.getNav('cfg', 'admin', 'global-orgs', 1);
$scope.newOrg = {name: ''};
$scope.createOrg = function() {

View File

@ -4,7 +4,7 @@ export class OrgApiKeysCtrl {
/** @ngInject **/
constructor ($scope, $http, backendSrv, navModelSrv) {
$scope.navModel = navModelSrv.getNav('cfg', 'apikeys');
$scope.navModel = navModelSrv.getNav('cfg', 'apikeys', 0);
$scope.roleTypes = ['Viewer', 'Editor', 'Admin'];
$scope.token = { role: 'Viewer' };

View File

@ -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() {

View File

@ -23,7 +23,7 @@ export class OrgUsersCtrl {
role: 'Viewer',
};
this.navModel = navModelSrv.getNav('cfg', 'users');
this.navModel = navModelSrv.getNav('cfg', 'users', 0);
this.get();
this.editor = { index: 0 };
@ -44,8 +44,7 @@ export class OrgUsersCtrl {
if (this.externalUserMngLinkName) {
return this.externalUserMngLinkName;
}
return "Add Members";
return "Add Member";
}
get() {

View File

@ -1,15 +1,12 @@
<navbar model="navModel"></navbar>
<page-header model="navModel"></page-header>
<div class="page-container">
<div class="page-header">
<page-h1 model="navModel"></page-h1>
</div>
<div class="page-container page-body">
<h2 class="page-sub-heading">
Change your password
</h2>
<div ng-if="ldapEnabled || authProxyEnabled">
You cannot change password when ldap or auth proxy authentication is enabled.
<br>
<br>
<a class="btn-text" href="profile">Back to profile</a>
</div>
<form name="userForm" class="gf-form-group" ng-hide="ldapEnabled || authProxyEnabled">

View File

@ -1,9 +1,9 @@
<navbar model="navModel"></navbar>
<page-header model="navModel"></page-header>
<div class="page-container" ng-form="playlistEditForm">
<div class="page-header">
<h1>New Organization</h1>
</div>
<div class="page-container page-body" ng-form="playlistEditForm">
<h2 class="page-sub-heading">
New Organization
</h2>
<p class="playlist-description">Each organization contains their own dashboards, data sources and configuration, and cannot be shared between orgs. While users may belong to more than one, mutiple organization are most frequently used in multi-tenant deployments. </p>

View File

@ -1,11 +1,9 @@
<navbar model="navModel"></navbar>
<page-header model="navModel"></page-header>
<div class="page-container">
<div class="page-header">
<page-h1 model="navModel"></page-h1>
</div>
<div class="page-container page-body">
<h3 class="page-heading">Add new</h3>
<form name="addTokenForm" class="gf-form-group">
<div class="gf-form-inline">
<div class="gf-form max-width-21">

View File

@ -1,26 +1,21 @@
<navbar model="navModel"></navbar>
<page-header model="navModel"></page-header>
<div class="page-container">
<div class="page-header">
<page-h1 model="navModel"></page-h1>
</div>
<div class="page-container page-body">
<h3 class="page-sub-heading">Organization profile</h3>
<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>
<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>

View File

@ -1,34 +1,52 @@
<navbar model="ctrl.navModel"></navbar>
<!-- <navbar model="ctrl.navModel"></navbar> -->
<!-- -->
<!-- <div class="page&#45;container"> -->
<!-- <div class="page&#45;header"> -->
<!-- <page&#45;h1 model="ctrl.navModel"></page&#45;h1> -->
<!-- -->
<!-- <button class="btn btn&#45;success" ng&#45;click="ctrl.openAddUsersView()" ng&#45;hide="ctrl.externalUserMngLinkUrl"> -->
<!-- <span>{{ctrl.addUsersBtnName}}</span> -->
<!-- </button> -->
<!-- -->
<!-- <div class="page&#45;header&#45;tabs"> -->
<!-- -->
<!-- <a class="btn btn&#45;inverse" ng&#45;href="{{ctrl.externalUserMngLinkUrl}}" target="_blank" ng&#45;if="ctrl.externalUserMngLinkUrl"> -->
<!-- <i class="fa fa&#45;external&#45;link&#45;square"></i> -->
<!-- {{ctrl.addUsersBtnName}} -->
<!-- </a> -->
<!-- -->
<!-- <ul class="gf&#45;tabs"> -->
<!-- <li class="gf&#45;tabs&#45;item"> -->
<!-- <a class="gf&#45;tabs&#45;link" ng&#45;click="ctrl.editor.index = 0" ng&#45;class="{active: ctrl.editor.index === 0}"> -->
<!-- Users ({{ctrl.users.length}}) -->
<!-- </a> -->
<!-- </li> -->
<!-- <li class="gf&#45;tabs&#45;item" ng&#45;show="ctrl.pendingInvites.length"> -->
<!-- <a class="gf&#45;tabs&#45;link" ng&#45;click="ctrl.editor.index = 1" ng&#45;class="{active: ctrl.editor.index === 1}"> -->
<!-- Pending Invites ({{ctrl.pendingInvites.length}}) -->
<!-- </a> -->
<!-- </li> -->
<!-- </ul> -->
<!-- </div> -->
<!-- </div> -->
<div class="page-container">
<div class="page-header">
<page-h1 model="ctrl.navModel"></page-h1>
<page-header model="ctrl.navModel"></page-header>
<div class="page-container page-body">
<div class="page-action-bar">
<div class="page-action-bar__spacer"></div>
<button class="btn btn-inverse" ng-show="ctrl.pendingInvites.length" ng-click="ctrl.editor.index = 1">
Pending Invites ({{ctrl.pendingInvites.length}})
</button>
<button class="btn btn-success" ng-click="ctrl.openAddUsersView()" ng-hide="ctrl.externalUserMngLinkUrl">
<i class="fa fa-plus"></i>
<span>{{ctrl.addUsersBtnName}}</span>
</button>
<div class="page-header-tabs">
<a class="btn btn-inverse" ng-href="{{ctrl.externalUserMngLinkUrl}}" target="_blank" ng-if="ctrl.externalUserMngLinkUrl">
<i class="fa fa-external-link-square"></i>
{{ctrl.addUsersBtnName}}
</a>
<ul class="gf-tabs">
<li class="gf-tabs-item">
<a class="gf-tabs-link" ng-click="ctrl.editor.index = 0" ng-class="{active: ctrl.editor.index === 0}">
Users ({{ctrl.users.length}})
</a>
</li>
<li class="gf-tabs-item" ng-show="ctrl.pendingInvites.length">
<a class="gf-tabs-link" ng-click="ctrl.editor.index = 1" ng-class="{active: ctrl.editor.index === 1}">
Pending Invites ({{ctrl.pendingInvites.length}})
</a>
</li>
</ul>
</div>
</div>
<a class="btn btn-inverse" ng-href="{{ctrl.externalUserMngLinkUrl}}" target="_blank" ng-if="ctrl.externalUserMngLinkUrl">
<i class="fa fa-external-link-square"></i>
{{ctrl.addUsersBtnName}}
</a>
</div>
<div class="grafana-info-box" ng-if="ctrl.externalUserMngInfo">
<span ng-bind-html="ctrl.externalUserMngInfo"></span>
@ -41,26 +59,26 @@
<th></th>
<th>Login</th>
<th>Email</th>
<th>
Seen
<tip>Time since user was seen using Grafana</tip>
</th>
<th>
Seen
<tip>Time since user was seen using Grafana</tip>
</th>
<th>Role</th>
<th style="width: 34px;"></th>
</tr>
</thead>
<tr ng-repeat="user in ctrl.users">
<td class="width-4 text-center">
<img class="filter-table__avatar" ng-src="{{user.avatarUrl}}"></img>
</td>
<img class="filter-table__avatar" ng-src="{{user.avatarUrl}}"></img>
</td>
<td>{{user.login}}</td>
<td><span class="ellipsis">{{user.email}}</span></td>
<td>{{user.lastSeenAtAge}}</td>
<td>{{user.lastSeenAtAge}}</td>
<td>
<div class="gf-form-select-wrapper width-9">
<select type="text" ng-model="user.role" class="gf-form-input" ng-options="f for f in ['Viewer', 'Editor', 'Read Only Editor', 'Admin']" ng-change="ctrl.updateOrgUser(user)">
</select>
</div>
<div class="gf-form-select-wrapper width-9">
<select type="text" ng-model="user.role" class="gf-form-input" ng-options="f for f in ['Viewer', 'Editor', 'Read Only Editor', 'Admin']" ng-change="ctrl.updateOrgUser(user)">
</select>
</div>
</td>
<td>
<a ng-click="ctrl.removeUser(user)" class="btn btn-danger btn-mini">

View File

@ -1,12 +1,9 @@
<navbar model="ctrl.navModel"></navbar>
<page-header model="ctrl.navModel"></page-header>
<div class="page-container">
<div class="page-header">
<page-h1 model="ctrl.navModel"></page-h1>
</div>
<div class="page-container page-body">
<h3 class="page-sub-heading">User Profile</h3>
<form name="ctrl.userForm" class="gf-form-group">
<h3 class="page-heading">Information</h3>
<div class="gf-form max-width-30">
<span class="gf-form-label width-8">Name</span>

View File

@ -1,20 +1,22 @@
<navbar model="ctrl.navModel"></navbar>
<page-header model="ctrl.navModel"></page-header>
<div class="page-container">
<div class="page-header">
<h1>User Groups</h1>
<div class="page-container page-body">
<div class="page-action-bar">
<div class="gf-form gf-form--grow">
<label class="gf-form-label">Search</label>
<input type="text" class="gf-form-input max-width-20" placeholder="Find User Group by name" tabindex="1" give-focus="true"
ng-model="ctrl.query" ng-model-options="{ debounce: 500 }" spellcheck='false' ng-change="ctrl.get()" />
</div>
<div class="page-action-bar__spacer"></div>
<a class="btn btn-success" ng-click="ctrl.openUserGroupModal()">
<i class="fa fa-plus"></i>
Create User Group
Add Team
</a>
</div>
<div class="gf-form width-15 gf-form-group">
<span style="position: relative;">
<input type="text" class="gf-form-input" placeholder="Find User Group by name" tabindex="1" give-focus="true"
ng-model="ctrl.query" ng-model-options="{ debounce: 500 }" spellcheck='false' ng-change="ctrl.get()" />
</span>
</div>
<div class="admin-list-table">
<table class="filter-table form-inline" ng-show="ctrl.userGroups.length > 0">
<thead>
@ -42,7 +44,6 @@
</td>
</tr>
</tbody>
</table>
</div>

View File

@ -14,7 +14,7 @@ export class ProfileCtrl {
constructor(private backendSrv, private contextSrv, private $location, navModelSrv) {
this.getUser();
this.getUserOrgs();
this.navModel = navModelSrv.getNav('profile');
this.navModel = navModelSrv.getNav('profile', 'profile-settings', 0);
}
getUser() {

View File

@ -15,7 +15,7 @@ export class UserGroupsCtrl {
/** @ngInject */
constructor(private backendSrv, navModelSrv) {
this.navModel = navModelSrv.getNav('cfg', 'users');
this.navModel = navModelSrv.getNav('cfg', 'teams', 0);
this.get();
}

View File

@ -1,9 +1,9 @@
<navbar model="ctrl.navModel"></navbar>
<page-header model="ctrl.navModel"></page-header>
<div class="page-container" ng-form="playlistEditForm">
<div class="page-header">
<page-h1 model="ctrl.navModel"></page-h1>
</div>
<div class="page-container page-body" ng-form="playlistEditForm">
<h3 class="page-sub-heading" ng-hide="ctrl.isNew">Edit Playlist</h3>
<h3 class="page-sub-heading" ng-show="ctrl.isNew">New Playlist</h3>
<p class="playlist-description">A playlist rotates through a pre-selected list of Dashboards. A Playlist can be a great way to build situational awareness, or just show off your metrics to your team or visitors.</p>
@ -103,7 +103,7 @@
<div class="clearfix"></div>
<div class="gf-form-button-row">
<a class="btn btn-success " ng-show="ctrl.isNew()"
<a class="btn btn-success " ng-show="ctrl.isNew"
ng-disabled="ctrl.playlistEditForm.$invalid || ctrl.isPlaylistEmpty()"
ng-click="ctrl.savePlaylist(ctrl.playlist, ctrl.playlistItems)">Create new playlist</a>
<a class="btn btn-success" ng-show="!ctrl.isNew()"

View File

@ -1,15 +1,15 @@
<navbar model="ctrl.navModel"></navbar>
<page-header model="ctrl.navModel"></page-header>
<div class="page-container">
<div class="page-header">
<page-h1 model="ctrl.navModel"></page-h1>
<a class="btn btn-primary pull-right" href="playlists/create">
<div class="page-container page-body">
<div class="page-action-bar">
<div class="page-action-bar__spacer"></div>
<a class="btn btn-success pull-right" href="playlists/create">
<i class="fa fa-plus"></i>
New Playlist
</a>
</div>
<table class="filter-table" style="margin-top: 20px">
<table class="filter-table">
<thead>
<th><strong>Name</strong></th>
<th><strong>Start url</strong></th>

View File

@ -1,5 +1,3 @@
///<reference path="../../headers/common.d.ts" />
import _ from 'lodash';
import coreModule from '../../core/core_module';
@ -11,10 +9,12 @@ export class PlaylistEditCtrl {
playlist: any = {
interval: '5m',
};
playlistItems: any = [];
dashboardresult: any = [];
tagresult: any = [];
navModel: any;
isNew: boolean;
/** @ngInject */
constructor(
@ -24,7 +24,9 @@ export class PlaylistEditCtrl {
$route,
navModelSrv
) {
this.navModel = navModelSrv.getNav('dashboards', 'playlists');
this.navModel = navModelSrv.getNav('dashboards', 'playlists', 0);
this.isNew = $route.current.params.id;
if ($route.current.params.id) {
var playlistId = $route.current.params.id;
@ -104,10 +106,6 @@ export class PlaylistEditCtrl {
});
}
isNew() {
return !this.playlist.id;
}
isPlaylistEmpty() {
return !this.playlistItems.length;
}

View File

@ -9,7 +9,7 @@ export class PlaylistsCtrl {
/** @ngInject */
constructor(private $scope, private backendSrv, navModelSrv) {
this.navModel = navModelSrv.getNav('dashboards', 'playlists');
this.navModel = navModelSrv.getNav('dashboards', 'playlists', 0);
backendSrv.get('/api/playlists').then(result => {
this.playlists = result;

View File

@ -41,7 +41,7 @@ export class DataSourceEditCtrl {
navModelSrv,
) {
this.navModel = navModelSrv.getNav('cfg', 'datasources');
this.navModel = navModelSrv.getNav('cfg', 'datasources', 0);
this.datasources = [];
this.tabIndex = 0;
@ -58,9 +58,7 @@ export class DataSourceEditCtrl {
this.isNew = true;
this.current = _.cloneDeep(defaults);
// add to nav & breadcrumbs
this.navModel.node = {text: 'New data source', icon: 'icon-gf icon-gf-fw icon-gf-datasources'};
this.navModel.breadcrumbs.push(this.navModel.node);
this.navModel.breadcrumbs.push({text: 'New'});
// We are coming from getting started
if (this.$location.search().gettingstarted) {
@ -87,7 +85,7 @@ export class DataSourceEditCtrl {
this.backendSrv.get('/api/datasources/' + id).then(ds => {
this.isNew = false;
this.current = ds;
this.navModel.node = {text: ds.name, icon: 'icon-gf icon-gf-fw icon-gf-datasources'};
this.navModel.node = {text: ds.name, icon: 'icon-gf icon-gf-fw icon-gf-datasources', id: 'ds-new'};
this.navModel.breadcrumbs.push(this.navModel.node);
if (datasourceCreated) {

View File

@ -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;

View File

@ -13,12 +13,6 @@
{{dash.title}}
</span>
</td>
<td>
<span>
Revision: {{dash.revision}}
<span ng-if="dash.imported" class="small">(Imported: {{dash.importedRevision}})</span>
<span>
</td>
<td style="text-align: right">
<button class="btn btn-secondary btn-small" ng-click="ctrl.import(dash, false)" ng-show="!dash.imported">
Import

View File

@ -1,91 +1,80 @@
<div class="scroll-canvas" grafana-scrollbar>
<navbar model="ctrl.navModel"></navbar>
<div class="page-container">
<div class="page-header">
<page-h1 model="ctrl.navModel"></page-h1>
<page-header model="ctrl.navModel"></page-header>
<div ng-if="ctrl.current.readOnly" class="grafana-info-box span8">Disclaimer. This datasource was added by config and cannot be modified using the UI. Please contact your server admin to update this datasource.</div>
<div class="page-container page-body">
<div class="page-header-tabs" ng-show="ctrl.hasDashboards">
<ul class="gf-tabs">
<li class="gf-tabs-item">
<a class="gf-tabs-link" ng-click="ctrl.tabIndex = 0" ng-class="{active: ctrl.tabIndex === 0}">
Config
</a>
</li>
<li class="gf-tabs-item">
<a class="gf-tabs-link" ng-click="ctrl.tabIndex = 1" ng-class="{active: ctrl.tabIndex === 1}">
Dashboards
</a>
</li>
</ul>
</div>
</div>
<div ng-if="ctrl.tabIndex === 0" class="tab-content">
<form name="ctrl.editForm" ng-if="ctrl.current">
<div class="gf-form-group">
<div class="gf-form-inline">
<div class="gf-form max-width-30">
<span class="gf-form-label width-7">Name</span>
<input class="gf-form-input max-width-23" type="text" ng-model="ctrl.current.name" placeholder="name" required>
<info-popover offset="0px -135px" mode="right-absolute">
The name is used when you select the data source in panels.
The <em>Default</em> data source is preselected in new
panels.
</info-popover>
</div>
<gf-form-switch class="gf-form" label="Default" checked="ctrl.current.isDefault" switch-class="max-width-6"></gf-form-switch>
</div>
<div class="gf-form">
<span class="gf-form-label width-7">Type</span>
<div class="gf-form-select-wrapper max-width-23">
<select class="gf-form-input" ng-model="ctrl.current.type" ng-options="v.id as v.name for v in ctrl.types" ng-change="ctrl.userChangedType()"></select>
</div>
</div>
</div>
<div class="alert alert-info gf-form-group" ng-if="ctrl.datasourceMeta.state === 'alpha'">
This plugin is marked as being in alpha state, which means it is in early development phase and
updates will include breaking changes.
</div>
<rebuild-on-change property="ctrl.datasourceMeta.id">
<plugin-component type="datasource-config-ctrl">
</plugin-component>
</rebuild-on-change>
<div ng-if="ctrl.testing" class="gf-form-group section">
<h5 ng-show="!ctrl.testing.done">Testing.... <i class="fa fa-spiner fa-spin"></i></h5>
<div class="alert-{{ctrl.testing.status}} alert" ng-show="ctrl.testing.done">
<div class="alert-icon">
<i class="fa fa-exclamation-triangle" ng-show="ctrl.testing.status === 'error'"></i>
<i class="fa fa-check" ng-show="ctrl.testing.status !== 'error'"></i>
</div>
<div class="alert-body">
<div class="alert-title">{{ctrl.testing.message}}</div>
</div>
</div>
</div>
<div class="gf-form-button-row">
<button type="submit" class="btn btn-success" ng-disabled="ctrl.current.readOnly" ng-click="ctrl.saveChanges()">Save</button>
<button type="submit" class="btn btn-danger" ng-disabled="ctrl.current.readOnly" ng-show="!ctrl.isNew" ng-click="ctrl.delete()">
Delete
</button>
<a class="btn btn-link" href="datasources">Cancel</a>
</div>
<br />
<br />
<br />
</form>
</div>
<div ng-if="ctrl.tabIndex === 1" class="tab-content">
<dashboard-import-list plugin="ctrl.datasourceMeta" datasource="ctrl.current"></dashboard-import-list>
<div ng-if="ctrl.current.readOnly" class="page-action-bar">
<div class="grafana-info-box span8">
Disclaimer. This datasource was added by config and cannot be modified using the UI. Please contact your server admin to update this datasource.
</div>
</div>
<h3 class="page-sub-heading" ng-hide="ctrl.isNew">Edit Data Source</h3>
<h3 class="page-sub-heading" ng-show="ctrl.isNew">New Data Source</h3>
<form name="ctrl.editForm" ng-if="ctrl.current">
<div class="gf-form-group">
<div class="gf-form-inline">
<div class="gf-form max-width-30">
<span class="gf-form-label width-7">Name</span>
<input class="gf-form-input max-width-23" type="text" ng-model="ctrl.current.name" placeholder="name" required>
<info-popover offset="0px -135px" mode="right-absolute">
The name is used when you select the data source in panels.
The <em>Default</em> data source is preselected in new
panels.
</info-popover>
</div>
<gf-form-switch class="gf-form" label="Default" checked="ctrl.current.isDefault" switch-class="max-width-6"></gf-form-switch>
</div>
<div class="gf-form">
<span class="gf-form-label width-7">Type</span>
<div class="gf-form-select-wrapper max-width-23">
<select class="gf-form-input" ng-model="ctrl.current.type" ng-options="v.id as v.name for v in ctrl.types" ng-change="ctrl.userChangedType()"></select>
</div>
</div>
</div>
<div class="alert alert-info gf-form-group" ng-if="ctrl.datasourceMeta.state === 'alpha'">
This plugin is marked as being in alpha state, which means it is in early development phase and
updates will include breaking changes.
</div>
<rebuild-on-change property="ctrl.datasourceMeta.id">
<plugin-component type="datasource-config-ctrl">
</plugin-component>
</rebuild-on-change>
<div ng-if="ctrl.hasDashboards">
<h3 class="section-heading">Bundled Plugin Dashboards</h3>
<div class="section">
<dashboard-import-list plugin="ctrl.datasourceMeta" datasource="ctrl.current"></dashboard-import-list>
</div>
</div>
<div ng-if="ctrl.testing" class="gf-form-group section">
<h5 ng-show="!ctrl.testing.done">Testing.... <i class="fa fa-spiner fa-spin"></i></h5>
<div class="alert-{{ctrl.testing.status}} alert" ng-show="ctrl.testing.done">
<div class="alert-icon">
<i class="fa fa-exclamation-triangle" ng-show="ctrl.testing.status === 'error'"></i>
<i class="fa fa-check" ng-show="ctrl.testing.status !== 'error'"></i>
</div>
<div class="alert-body">
<div class="alert-title">{{ctrl.testing.message}}</div>
</div>
</div>
</div>
<div class="gf-form-button-row">
<button type="submit" class="btn btn-success" ng-disabled="ctrl.current.readOnly" ng-click="ctrl.saveChanges()">Save</button>
<button type="submit" class="btn btn-danger" ng-disabled="ctrl.current.readOnly" ng-show="!ctrl.isNew" ng-click="ctrl.delete()">
Delete
</button>
<a class="btn btn-link" href="datasources">Cancel</a>
</div>
<br />
<br />
<br />
</form>
</div>

View File

@ -1,7 +1,7 @@
<div class="gf-form-group">
<h3 class="page-heading">HTTP settings</h3>
<h3 class="page-heading">HTTP</h3>
<div class="gf-form-group">
<div class="gf-form-inline">
<div class="gf-form max-width-30">
@ -38,39 +38,38 @@
</div>
</div>
<h3 class="page-heading">HTTP Auth</h3>
<div class="gf-form-group">
<div class="gf-form-inline">
<gf-form-switch class="gf-form" label="Basic Auth" checked="current.basicAuth" label-class="width-8" switch-class="max-width-6"></gf-form-switch>
<gf-form-switch class="gf-form" label="With Credentials" tooltip="Whether credentials such as cookies or auth headers should be sent with cross-site requests." checked="current.withCredentials" label-class="width-11" switch-class="max-width-6"></gf-form-switch>
</div>
<div class="gf-form-inline">
<gf-form-switch class="gf-form" ng-if="current.access=='proxy'" label="TLS Client Auth" label-class="width-8" checked="current.jsonData.tlsAuth" switch-class="max-width-6"></gf-form-switch>
<gf-form-switch class="gf-form" ng-if="current.access=='proxy'" label="With CA Cert" tooltip="Needed for verifing self-signed TLS Certs" checked="current.jsonData.tlsAuthWithCACert" label-class="width-11" switch-class="max-width-6"></gf-form-switch>
</div>
</div>
<h3 class="page-heading">Auth</h3>
<div class="gf-form-group">
<div class="gf-form-inline">
<gf-form-switch class="gf-form" ng-if="current.access=='proxy'" label="Skip TLS Verification (Insecure)" label-class="width-16" checked="current.jsonData.tlsSkipVerify" switch-class="max-width-6"></gf-form-switch>
<gf-form-switch class="gf-form" label="Basic Auth" checked="current.basicAuth" label-class="width-8" switch-class="max-width-6"></gf-form-switch>
<gf-form-switch class="gf-form" label="With Credentials" tooltip="Whether credentials such as cookies or auth headers should be sent with cross-site requests." checked="current.withCredentials" label-class="width-11" switch-class="max-width-6"></gf-form-switch>
</div>
<div class="gf-form-inline">
<gf-form-switch class="gf-form" ng-if="current.access=='proxy'" label="TLS Client Auth" label-class="width-8" checked="current.jsonData.tlsAuth" switch-class="max-width-6"></gf-form-switch>
<gf-form-switch class="gf-form" ng-if="current.access=='proxy'" label="With CA Cert" tooltip="Needed for verifing self-signed TLS Certs" checked="current.jsonData.tlsAuthWithCACert" label-class="width-11" switch-class="max-width-6"></gf-form-switch>
</div>
</div>
<div class="gf-form-inline">
<gf-form-switch class="gf-form" ng-if="current.access=='proxy'" label="Skip TLS Verification (Insecure)" label-class="width-16" checked="current.jsonData.tlsSkipVerify" switch-class="max-width-6"></gf-form-switch>
</div>
</div>
<div class="gf-form-group" ng-if="current.basicAuth">
<h6>Basic Auth Details</h6>
<div class="gf-form" ng-if="current.basicAuth">
<span class="gf-form-label width-7">
User
</span>
<input class="gf-form-input max-width-21" type="text" ng-model='current.basicAuthUser' placeholder="user" required></input>
</div>
<div class="gf-form" ng-if="current.basicAuth">
<span class="gf-form-label width-7">
User
</span>
<input class="gf-form-input max-width-21" type="text" ng-model='current.basicAuthUser' placeholder="user" required></input>
</div>
<div class="gf-form">
<span class="gf-form-label width-7">
Password
</span>
<input class="gf-form-input max-width-21" type="password" ng-model='current.basicAuthPassword' placeholder="password" required></input>
</div>
<div class="gf-form">
<span class="gf-form-label width-7">
Password
</span>
<input class="gf-form-input max-width-21" type="password" ng-model='current.basicAuthPassword' placeholder="password" required></input>
</div>
</div>
<div class="gf-form-group" ng-if="(current.jsonData.tlsAuth || current.jsonData.tlsAuthWithCACert) && current.access=='proxy'">

View File

@ -1,48 +1,46 @@
<div class="scroll-canvas" grafana-scrollbar>
<navbar model="ctrl.navModel"></navbar>
<div class="page-container">
<div class="page-header">
<page-h1 model="ctrl.navModel"></page-h1>
<page-header model="ctrl.navModel"></page-header>
<a class="page-header__cta btn btn-success" href="datasources/new">
Add data source
</a>
</div>
<div class="page-container page-body">
<div class="page-action-bar">
<div class="page-action-bar__spacer"></div>
<a class="page-header__cta btn btn-success" href="datasources/new">
<i class="fa fa-plus"></i>
Add data source
</a>
</div>
<section class="card-section" layout-mode>
<layout-selector></layout-selector>
<ol class="card-list" >
<li class="card-item-wrapper" ng-repeat="ds in ctrl.datasources">
<a class="card-item" href="datasources/edit/{{ds.id}}/">
<div class="card-item-header">
<div class="card-item-type">
{{ds.type}}
<section class="card-section" layout-mode>
<layout-selector></layout-selector>
<ol class="card-list">
<li class="card-item-wrapper" ng-repeat="ds in ctrl.datasources">
<a class="card-item" href="datasources/edit/{{ds.id}}/">
<div class="card-item-header">
<div class="card-item-type">
{{ds.type}}
</div>
</div>
<div class="card-item-body">
<figure class="card-item-figure">
<img ng-src="{{ds.typeLogoUrl}}">
</figure>
<div class="card-item-details">
<div class="card-item-name">
{{ds.name}}
<span ng-if="ds.isDefault">
<span class="btn btn-secondary btn-mini">default</span>
</span>
</div>
<div class="card-item-sub-name">
{{ds.url}}
</div>
</div>
<div class="card-item-body">
<figure class="card-item-figure">
<img ng-src="{{ds.typeLogoUrl}}">
</figure>
<div class="card-item-details">
<div class="card-item-name">
{{ds.name}}
<span ng-if="ds.isDefault">
<span class="btn btn-secondary btn-mini">default</span>
</span>
</div>
<div class="card-item-sub-name">
{{ds.url}}
</div>
</div>
</div>
</a>
</li>
</ol>
</section>
</div>
</a>
</li>
</ol>
</section>
<div ng-if="ctrl.datasources.length === 0">
<em>No data sources defined</em>
</div>
<div ng-if="ctrl.datasources.length === 0">
<em>No data sources defined</em>
</div>
</div>

View File

@ -1,62 +1,42 @@
<navbar model="ctrl.navModel"></navbar>
<div class="page-container" ng-init="ctrl.init()">
<div class="page-header">
<div class="plugin-header">
<span class="plugin-header-logo">
<img ng-src="{{ctrl.model.info.logos.large}}">
</span>
<div class="plugin-header-info-block">
<h1 class="plugin-header-name">{{ctrl.model.name}}</h1>
<div class="plugin-header-author">By {{ctrl.model.info.author.name}}</div>
<div class="plugin-header-stamps">
<span class="plugin-header-stamps-type">
<i class="{{ctrl.pluginIcon}}"></i> {{ctrl.model.type}}
</span>
</div>
</div>
</div>
<ul class="gf-tabs">
<li class="gf-tabs-item" ng-repeat="tab in ctrl.tabs">
<a class="gf-tabs-link" ng-click="ctrl.tabIndex = $index" ng-class="{active: ctrl.tabIndex === $index}">
{{::tab}}
</a>
</li>
</ul>
<page-header model="ctrl.navModel"></page-header>
<div class="page-container page-body" ng-init="ctrl.init()">
<div class="page-action-bar">
<button class="btn" ng-repeat="tab in ctrl.tabs" ng-class="{'btn-secondary': ctrl.tabIndex === $index, 'btn-inverse': ctrl.tabIndex !== $index}" ng-click="ctrl.tabIndex = $index">
{{tab}}
</button>
</div>
<div class="page-body">
<div class="tab-content page-content-with-sidebar" ng-if="ctrl.tabs[ctrl.tabIndex] === 'Readme'">
<div class="sidebar-container">
<div class="tab-content sidebar-content" ng-if="ctrl.tabs[ctrl.tabIndex] === 'Readme'">
<div ng-bind-html="ctrl.readmeHtml" class="markdown-html">
</div>
</div>
<div class="tab-content page-content-with-sidebar" ng-if="ctrl.tabs[ctrl.tabIndex] === 'Config'">
<div class="tab-content sidebar-content" ng-if="ctrl.tabs[ctrl.tabIndex] === 'Config'">
<div ng-if="ctrl.model.id">
<plugin-component type="app-config-ctrl"></plugin-component>
<div class="gf-form-button-row">
<button type="submit" class="btn btn-success" ng-click="ctrl.enable()" ng-show="!ctrl.model.enabled">Enable</button>
<button type="submit" class="btn btn-success" ng-click="ctrl.update()" ng-show="ctrl.model.enabled">Update</button>
<button type="submit" class="btn btn-danger" ng-click="ctrl.disable()" ng-show="ctrl.model.enabled">Disable</button>
</div>
<div class="gf-form-button-row">
<button type="submit" class="btn btn-success" ng-click="ctrl.enable()" ng-show="!ctrl.model.enabled">Enable</button>
<button type="submit" class="btn btn-success" ng-click="ctrl.update()" ng-show="ctrl.model.enabled">Update</button>
<button type="submit" class="btn btn-danger" ng-click="ctrl.disable()" ng-show="ctrl.model.enabled">Disable</button>
</div>
</div>
</div>
<div class="tab-content page-content-with-sidebar" ng-if="ctrl.tabs[ctrl.tabIndex] === 'Dashboards'">
<dashboard-import-list plugin="ctrl.model"></dashboard-import-list>
<div class="tab-content sidebar.content" ng-if="ctrl.tabs[ctrl.tabIndex] === 'Dashboards'">
<dashboard-import-list plugin="ctrl.model"></dashboard-import-list>
</div>
<aside class="page-sidebar">
<section class="page-sidebar-section">
<h4>Version</h4>
<span>{{ctrl.model.info.version}}</span>
<div ng-show="ctrl.model.hasUpdate">
<div ng-show="ctrl.model.hasUpdate">
<a ng-click="ctrl.updateAvailable()" bs-tooltip="ctrl.model.latestVersion">Update Available!</a>
</div>
</div>
</section>
<section class="page-sidebar-section" ng-show="ctrl.model.type === 'app'">
<h5>Includes</h4>

View File

@ -1,65 +1,81 @@
<div class="scroll-canvas" grafana-scrollbar>
<navbar model="ctrl.navModel"></navbar>
<page-header model="ctrl.navModel"></page-header>
<div class="page-container">
<div class="page-header">
<h1>
<i class="icon-gf icon-gf-apps"></i>
Plugins <span class="muted small">(currently installed)</span>
</h1>
<!-- <div class="page&#45;header&#45;canvas"> -->
<!-- <div class="page&#45;container"> -->
<!-- <navbar model="ctrl.navModel"></navbar> -->
<!-- -->
<!-- <div class="page&#45;header"> -->
<!-- <page&#45;h1 model="ctrl.navModel"></page&#45;h1> -->
<!-- -->
<!-- <div class="page&#45;header&#45;tabs"> -->
<!-- <ul class="gf&#45;tabs"> -->
<!-- <li class="gf&#45;tabs&#45;item"> -->
<!-- <a class="gf&#45;tabs&#45;link" href="plugins?type=panel" ng&#45;class="{active: ctrl.tabIndex === 0}"> -->
<!-- <i class="icon&#45;gf icon&#45;gf&#45;panel"></i> -->
<!-- Panels -->
<!-- </a> -->
<!-- </li> -->
<!-- <li class="gf&#45;tabs&#45;item"> -->
<!-- <a class="gf&#45;tabs&#45;link" href="plugins?type=datasource" ng&#45;class="{active: ctrl.tabIndex === 1}"> -->
<!-- <i class="gicon gicon&#45;datasources"></i> -->
<!-- Data sources -->
<!-- </a> -->
<!-- </li> -->
<!-- <li class="gf&#45;tabs&#45;item"> -->
<!-- <a class="gf&#45;tabs&#45;link" href="plugins?type=app" ng&#45;class="{active: ctrl.tabIndex === 2}"> -->
<!-- <i class="icon&#45;gf icon&#45;gf&#45;apps"></i> -->
<!-- Apps -->
<!-- </a> -->
<!-- </li> -->
<!-- </ul> -->
<!-- -->
<!-- <a class="get&#45;more&#45;plugins&#45;link pull&#45;right" href="https://grafana.com/plugins?utm_source=grafana_plugin_list" target="_blank"> -->
<!-- Find more <img src="public/img/icn&#45;plugins&#45;tiny.svg" />plugins on Grafana.com -->
<!-- </a> -->
<!-- </div> -->
<!-- </div> -->
<!-- </div> -->
<!-- </div> -->
<div class="page-header-tabs">
<ul class="gf-tabs">
<li class="gf-tabs-item">
<a class="gf-tabs-link" href="plugins?type=panel" ng-class="{active: ctrl.tabIndex === 0}">
Panels
</a>
</li>
<li class="gf-tabs-item">
<a class="gf-tabs-link" href="plugins?type=datasource" ng-class="{active: ctrl.tabIndex === 1}">
Data sources
</a>
</li>
<li class="gf-tabs-item">
<a class="gf-tabs-link" href="plugins?type=app" ng-class="{active: ctrl.tabIndex === 2}">
Apps
</a>
</li>
</ul>
<div class="page-container page-body">
<div class="page-action-bar">
<div class="gf-form">
<label class="gf-form-label">Search</label>
<input type="text" class="gf-form-input width-20" ng-model="ctrl.searchQuery" ng-change="ctrl.onQueryUpdated()" />
</div>
<a class="get-more-plugins-link" href="https://grafana.com/plugins?utm_source=grafana_plugin_list" target="_blank">
Find more <img src="public/img/icn-plugins-tiny.svg" />plugins on Grafana.com
</a>
</div>
</div>
<section class="card-section" layout-mode>
<layout-selector></layout-selector>
<ol class="card-list" >
<li class="card-item-wrapper" ng-repeat="plugin in ctrl.plugins">
<a class="card-item" href="plugins/{{plugin.id}}/edit">
<div class="card-item-header">
<div class="card-item-type">
<i class="icon-gf icon-gf-{{plugin.type}}"></i>
{{plugin.type}}
</div>
<div class="card-item-notice" ng-show="plugin.hasUpdate">
<span bs-tooltip="plugin.latestVersion">Update available!</span>
</div>
</div>
<div class="card-item-body">
<figure class="card-item-figure">
<img ng-src="{{plugin.info.logos.small}}">
</figure>
<div class="card-item-details">
<div class="card-item-name">{{plugin.name}}</div>
<div class="card-item-sub-name">By {{plugin.info.author.name}}</div>
</div>
</div>
</a>
</li>
</ol>
</section>
<div class="page-action-bar__spacer"></div>
<a class="btn btn-success" href="https://grafana.com/plugins?utm_source=grafana_plugin_list" target="_blank">
Find more plugins on Grafana.com
</a>
</div>
<section class="card-section" layout-mode>
<layout-selector></layout-selector>
<ol class="card-list" >
<li class="card-item-wrapper" ng-repeat="plugin in ctrl.plugins">
<a class="card-item" href="plugins/{{plugin.id}}/edit">
<div class="card-item-header">
<div class="card-item-type">
<i class="icon-gf icon-gf-{{plugin.type}}"></i>
{{plugin.type}}
</div>
<div class="card-item-notice" ng-show="plugin.hasUpdate">
<span bs-tooltip="plugin.latestVersion">Update available!</span>
</div>
</div>
<div class="card-item-body">
<figure class="card-item-figure">
<img ng-src="{{plugin.info.logos.small}}">
</figure>
<div class="card-item-details">
<div class="card-item-name">{{plugin.name}}</div>
<div class="card-item-sub-name">By {{plugin.info.author.name}}</div>
</div>
</div>
</a>
</li>
</ol>
</section>
</div>

View File

@ -27,7 +27,7 @@ export class PluginEditCtrl {
$routeParams,
navModelSrv,
) {
this.navModel = navModelSrv.getNav('cfg', 'plugins');
this.navModel = navModelSrv.getNav('cfg', 'plugins', 0);
this.model = {};
this.pluginId = $routeParams.pluginId;
this.tabIndex = 0;

View File

@ -1,34 +1,28 @@
///<reference path="../../headers/common.d.ts" />
import angular from 'angular';
import _ from 'lodash';
export class PluginListCtrl {
plugins: any[];
tabIndex: number;
navModel: any;
searchQuery: string;
allPlugins: any[];
/** @ngInject */
constructor(private backendSrv: any, $location, navModelSrv) {
this.tabIndex = 0;
this.navModel = navModelSrv.getNav('cfg', 'plugins');
this.navModel = navModelSrv.getNav('cfg', 'plugins', 0);
var pluginType = $location.search().type || 'panel';
switch (pluginType) {
case "datasource": {
this.tabIndex = 1;
break;
}
case "app": {
this.tabIndex = 2;
break;
}
case "panel":
default:
this.tabIndex = 0;
}
this.backendSrv.get('api/plugins', {embedded: 0, type: pluginType}).then(plugins => {
this.backendSrv.get('api/plugins', {embedded: 0}).then(plugins => {
this.plugins = plugins;
this.allPlugins = plugins;
});
}
onQueryUpdated() {
let regex = new RegExp(this.searchQuery, 'ig');
this.plugins = _.filter(this.allPlugins, item => {
return regex.test(item.name) || regex.test(item.type);
});
}
}

View File

@ -1,11 +1,7 @@
<navbar model="ctrl.navModel"></navbar>
<page-header model="ctrl.navModel"></page-header>
<div class="page-container">
<div class="page-header">
<page-h1 model="ctrl.navModel"></page-h1>
</div>
<table class="filter-table" style="margin-top: 20px">
<div class="page-container page-body">
<table class="filter-table">
<thead>
<th><strong>Name</strong></th>
<th><strong>Snapshot url</strong></th>

View File

@ -9,7 +9,7 @@ export class SnapshotsCtrl {
/** @ngInject */
constructor(private $rootScope, private backendSrv, navModelSrv) {
this.navModel = navModelSrv.getNav('dashboards', 'snapshots');
this.navModel = navModelSrv.getNav('dashboards', 'snapshots', 0);
this.backendSrv.get('/api/dashboard/snapshots').then(result => {
this.snapshots = result;
});

View File

@ -1,34 +1,10 @@
<navbar model="ctrl.navModel"></navbar>
<page-header model="ctrl.navModel"></page-header>
<div class="page-container">
<div class="page-header">
<page-h1 model="ctrl.navModel"></page-h1>
<div class="page-container page-body">
<a class="btn btn-success" ng-click="ctrl.switchTheme()">
<i class="fa fa-random"></i>
Switch theme
</a>
<h3 class="page-heading">Buttons</h3>
<div class="page-header-tabs">
<ul class="gf-tabs">
<li class="gf-tabs-item" ng-repeat="page in ctrl.pages">
<a class="gf-tabs-link" href="styleguide/{{page}}" ng-class="{active: ctrl.page[page]}">{{page}}</a>
</li>
</ul>
</div>
</div>
<div class="tab-pane" ng-if="ctrl.page.colors">
<ul>
<li class="style-guide-color-card" ng-repeat="color in ctrl.colors" style="background-color: {{color.value}}">
<strong>${{color.name}}</strong>
<em>{{color.value}}</em>
</li>
</ul>
</div>
<div class="tab-pane" ng-if="ctrl.page.buttons">
<div class="tab-pane">
<div ng-repeat="variant in ctrl.buttonVariants" class="row">
<div ng-repeat="btnSize in ctrl.buttonSizes" class="style-guide-button-list p-a-2 col-md-4">
<button ng-repeat="buttonName in ctrl.buttonNames" class="btn btn{{variant}}{{buttonName}} {{btnSize}}">
@ -38,7 +14,7 @@
</div>
</div>
<div class="tab-pane style-guide-icon-list" ng-if="ctrl.page.icons">
<div class="tab-pane style-guide-icon-list">
<div class="row">
<div ng-repeat="icon in ctrl.icons" class="col-md-2 col-sm-3 col-xs-4">
<i class="icon-gf icon-gf-{{icon}}" bs-tooltip="'icon-gf icon-gf-{{icon}}'"></i>
@ -46,13 +22,13 @@
</div>
</div>
<div class="tab-pane style-guide-plugin-authoring" ng-if="ctrl.page.plugins">
<p>From grafana 3.0 it's very easy to develop your own plugins and share them with other grafana users.</p>
<p>More information about plugin development can be found at <a href="http://docs.grafana.org/plugins/developing/development/" target="_blank">docs.grafana.org</a></p>
</div>
<h3 class="page-heading">Forms</h3>
<div class="tab-pane" ng-if="ctrl.page.forms">
forms
<div class="gf-form-inline">
<div class="gf-form">
<label class="gf-form-label">Label</label>
<input type="text" class="gf-form-input" />
</div>
</div>
</div>

View File

@ -8,7 +8,7 @@ class StyleGuideCtrl {
theme: string;
buttonNames = ['primary', 'secondary', 'inverse', 'success', 'warning', 'danger'];
buttonSizes = ['btn-small', '', 'btn-large'];
buttonVariants = ['-', '-outline-'];
buttonVariants = ['-'];
icons: any = [];
page: any;
pages = ['colors', 'buttons', 'icons', 'plugins'];
@ -16,7 +16,7 @@ class StyleGuideCtrl {
/** @ngInject **/
constructor(private $http, private $routeParams, private backendSrv, navModelSrv) {
this.navModel = navModelSrv.getNav('cfg', 'admin', 'styleguide');
this.navModel = navModelSrv.getNav('cfg', 'admin', 'styleguide', 1);
this.theme = config.bootData.user.lightTheme ? 'light': 'dark';
this.page = {};

View File

@ -85,6 +85,7 @@
@import "components/code_editor";
@import "components/dashboard_grid";
@import "components/dashboard_list";
@import "components/page_header";
// PAGES

View File

@ -1,95 +1,49 @@
.dashnav-back-to-dashboard {
.navbar-buttons--zoom {
display: none;
}
.navbar-page-btn {
max-width: 200px;
}
.gf-timepicker-nav-btn {
max-width: 120px;
}
.navbar-buttons--actions {
display: none;
}
// Media queries
// ---------------------
@include media-breakpoint-down(sm) {
// div.panel {
// width: 100% !important;
// padding: 0px !important;
// }
// .panel-margin {
// margin-right: 0;
// margin-left: 0;
// }
body {
padding: 0;
}
.page-dashboard .navbar-page-btn {
max-width: 200px;
}
.gf-timepicker-nav-btn {
max-width: 120px;
}
.dashnav-zoom-out,
.dashnav-move-timeframe,
.dashnav-action-icons {
display: none;
}
.page-container {
padding: ($spacer * 1) ($spacer * 2);
}
.dash-row-menu-container {
display: none;
}
}
@include media-breakpoint-down(xs) {
.page-dashboard .navbar-page-btn {
@include media-breakpoint-up(sm) {
.navbar-page-btn {
max-width: 250px;
}
}
// form styles
@include media-breakpoint-up(md) {
.page-dashboard .navbar-page-btn {
max-width: 280px;
}
.gf-timepicker-nav-btn {
max-width: 120px;
}
.dashnav-move-timeframe {
display: none;
}
.panel-in-fullscreen {
.dashnav-action-icons {
display: none;
}
.dashnav-back-to-dashboard {
display: block;
}
max-width: 200px;
}
}
@include media-breakpoint-up(lg) {
.page-dashboard .navbar-page-btn {
max-width: 290px;
@include media-breakpoint-up(md) {
.navbar-buttons--actions {
display: flex;
}
.navbar-page-btn {
max-width: 325px;
}
.gf-timepicker-nav-btn {
max-width: 240px;
}
.dashnav-zoom-out {
display: block;
}
.dashnav-move-timeframe {
display: block;
}
}
@include media-breakpoint-up(xl) {
.panel-in-fullscreen {
.dashnav-action-icons {
display: block;
}
@include media-breakpoint-up(lg) {
.navbar-buttons--zoom {
display: flex;
}
.page-dashboard .navbar-page-btn {
.navbar-page-btn {
max-width: none;
}
.gf-timepicker-nav-btn {

View File

@ -15,7 +15,7 @@ $dark-3: #262628;
$dark-4: #333333;
$dark-5: #444444;
$gray-1: #555555;
$gray-2: #7B7B7B;
$gray-2: #8e8e8e;
$gray-3: #b3b3b3;
$gray-4: #D8D9DA;
$gray-5: #ECECEC;
@ -51,7 +51,8 @@ $critical: #ed2e18;
// Scaffolding
// -------------------------
$body-bg: rgb(23,24,25);
$page-bg: $dark-2;
$page-bg: rgb(22, 23, 25);
$body-color: $gray-4;
$text-color: $gray-4;
$text-color-strong: $white;
@ -64,7 +65,7 @@ $text-shadow-faint: 1px 1px 4px rgb(45, 45, 45);
// gradients
$brand-gradient: linear-gradient(to right, rgba(255,213,0,0.7) 0%, rgba(255,68,0,0.7) 99%, rgba(255,68,0,0.7) 100%);
$page-gradient: linear-gradient(180deg, #222426 10px, rgba(15, 15, 16, .03) 100px, rgba(10, 10, 11, .03));
$page-gradient: linear-gradient(180deg, #222426 10px, rgb(22, 23, 25) 100px);
// Links
// -------------------------
@ -97,6 +98,11 @@ $panel-drop-zone-bg: repeating-linear-gradient(-128deg, #111, #111 10px, #191
$panel-header-hover-bg: $dark-4;
$panel-header-menu-hover-bg: $dark-5;
// page header
$page-header-bg: linear-gradient(90deg, #292a2d, black);
$page-header-shadow: inset 0px -4px 14px $dark-2;
$page-header-border-color: $dark-4;
$divider-border-color: #555;
// Graphite Target Editor
@ -121,8 +127,9 @@ $list-item-link-color: $text-color;
$list-item-shadow: $card-shadow;
// Scrollbars
$scrollbarBackground: #3a3a3a;
$scrollbarBackground: #404357;
$scrollbarBackground2: #3a3a3a;
$scrollbarBorder: black;
// Tables
@ -180,6 +187,7 @@ $input-invalid-border-color: lighten($red, 5%);
// Search
$search-shadow: 0 0 35px 0 $body-bg;
$search-filter-box-bg: $gray-blue;
// Dropdowns
// -------------------------
@ -274,7 +282,7 @@ $alert-warning-bg: linear-gradient(90deg, #d44939, #e0603d);
$alert-info-bg: linear-gradient(100deg, #1a4552, #00374a);
// popover
$popover-bg: $panel-bg;
$popover-bg: $page-bg;
$popover-color: $text-color;
$popover-border-color: $dark-4;
$popover-shadow: 0 0 20px black;
@ -300,7 +308,7 @@ $checkboxImageUrl: '../img/checkbox.png';
$info-box-background: linear-gradient(100deg, #1a4552, #00374a);
// footer
$footer-link-color: $gray-1;
$footer-link-color: $gray-2;
$footer-link-hover: $gray-4;
// collapse box

View File

@ -102,6 +102,8 @@ $brand-gradient: linear-gradient(to right, rgba(255,213,0,1.0) 0%, rgba(255,68,0
//$page-gradient: linear-gradient(120deg, transparent 40%, darken($gray-6, 4%) 98%);
$page-gradient: linear-gradient(120deg, $gray-7 40%, $gray-6 98%);
//$page-gradient: $gray-7;
// $page-gradient: linear-gradient(-60deg, transparent 70%, darken($page-bg, 4%) 98%);
$page-header-bg: linear-gradient(90deg, #292a2d, black);
// Links
// -------------------------
@ -135,6 +137,11 @@ $panel-drop-zone-bg: repeating-linear-gradient(-128deg, $body-bg, $body-bg 10px,
$panel-header-hover-bg: $gray-6;
$panel-header-menu-hover-bg: $gray-4;
// Page header
$page-header-bg: linear-gradient(90deg, #292a2d, black);
$page-header-shadow: inset 0px -4px 14px $dark-2;
$page-header-border-color: $dark-4;
$divider-border-color: $gray-2;
// Graphite Target Editor
@ -238,6 +245,7 @@ $breadcrumb-hover-hl: #d9dadd;
// search
$search-shadow: 0 5px 30px 0 $gray-4;
$search-filter-box-bg: $gray-4;
// Dropdowns
// -------------------------
@ -322,7 +330,7 @@ $alert-warning-bg: linear-gradient(90deg, #d44939, #e04d3d);
$alert-info-bg: $blue-dark;
// popover
$popover-bg: $panel-bg;
$popover-bg: $page-bg;
$popover-color: $text-color;
$popover-border-color: $gray-5;
$popover-shadow: 0 0 20px $white;

View File

@ -86,11 +86,12 @@ $font-family-monospace: Menlo, Monaco, Consolas, "Courier New", monospace;
$font-family-base: $font-family-sans-serif !default;
$font-size-root: 14px !default;
$font-size-base: 13px !default;
$font-size-base: 1rem !default;
$font-size-lg: 1.25rem !default;
$font-size-sm: .875rem !default;
$font-size-xs: .75rem !default;
$font-size-lg: 18px !default;
$font-size-md: 15px !default;
$font-size-sm: 11px !default;
$font-size-xs: 9px !default;
$line-height-base: 1.5 !default;
$font-weight-semi-bold: 500;
@ -159,9 +160,9 @@ $table-cell-padding: 4px 10px !default;
$table-sm-cell-padding: .3rem !default;
// Forms
$input-padding-x: .75rem !default;
$input-padding-y: .6rem !default;
$input-line-height: 1.35rem !default;
$input-padding-x: 10px !default;
$input-padding-y: 8px !default;
$input-line-height: 19px !default;
$input-btn-border-width: 1px;
$input-border-radius: 0 $border-radius $border-radius 0 !default;
@ -172,11 +173,11 @@ $label-border-radius: $border-radius 0 0 $border-radius !default;
$label-border-radius-lg: $border-radius-lg 0 0 $border-radius-lg !default;
$label-border-radius-sm: $border-radius-sm 0 0 $border-radius-sm !default;
$input-padding-x-sm: .5rem !default;
$input-padding-y-sm: .25rem !default;
$input-padding-x-sm: 7px !default;
$input-padding-y-sm: 4px !default;
$input-padding-x-lg: 1.5rem !default;
$input-padding-y-lg: .75rem !default;
$input-padding-x-lg: 20px !default;
$input-padding-y-lg: 10px !default;
$input-height: (($font-size-base * $line-height-base) + ($input-padding-y * 2)) !default;
$input-height-lg: (($font-size-lg * $line-height-lg) + ($input-padding-y-lg * 2)) !default;
@ -197,10 +198,14 @@ $form-icon-danger: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www
// Used for a bird's eye view of components dependent on the z-axis
// Try to avoid customizing these :)
$zindex-dropdown: 1000;
$zindex-tooltip: 1020;
$zindex-navbar-fixed: 1030;
$zindex-navbar-fixed: 1020;
$zindex-tooltip: 1030;
$zindex-modal-backdrop: 1040;
$zindex-modal: 1050;
$zindex-typeahead: 1060;
$zindex-sidemenu : $zindex-navbar-fixed + 5;
// Buttons
//
@ -224,9 +229,8 @@ $side-menu-width: 60px;
// dashboard
$panel-margin: 10px;
$dashboard-padding: $panel-margin * 2;
$panel-padding: 0px 10px 5px 10px;
// tabs
$tabs-padding-top: 0.6rem;
$tabs-padding-bottom: 0.4rem;
$tabs-top-margin: 0.5rem;
$tabs-padding: 9px 15px 9px;

View File

@ -43,5 +43,18 @@
background-image: url('../img/icons_#{$theme-name}_theme/icon_add_panel.svg');
}
.gicon-alert-notification-channel {
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');
}
.gicon-org {
background-image: url('../img/icons_#{$theme-name}_theme/icon_org.svg');
}
.gicon-zoom-out {
background-image: url('../img/icons_#{$theme-name}_theme/icon_zoom_out.svg');
}

View File

@ -21,7 +21,7 @@
}
}
@include media-breakpoint-down(md) {
@include media-breakpoint-down(sm) {
.react-grid-layout {
height: 100% !important;
}

View File

@ -18,7 +18,7 @@
position: relative;
top: -3px;
width: 250px;
font-size: 80%;
font-size: $font-size-sm;
margin-left: 22px;
color: $gray-2;
white-space: normal;
@ -91,7 +91,6 @@
color: $link-color-disabled;
position: relative;
top: 3px;
padding-left: 5px;
}
.gicon {
@ -300,7 +299,7 @@
// Typeahead
// ---------
.typeahead {
z-index: 1051;
z-index: $zindex-typeahead;
margin-top: 2px; // give it some space to breathe
}

View File

@ -5,7 +5,7 @@
.footer {
color: $footer-link-color;
padding: 5rem 0 1rem 0;
font-size: $font-size-xs;
font-size: $font-size-sm;
width: 98%; /* was causing horiz scrollbars - need to examine */
a {

View File

@ -1,4 +1,5 @@
$gf-form-margin: 3px;
$input-border: 1px solid $input-border-color;
.gf-form {
margin-bottom: $gf-form-margin;
@ -20,6 +21,15 @@ $gf-form-margin: 3px;
&--flex-end {
justify-content: flex-end;
}
&--alt {
flex-direction: column;
align-items: flex-start;
.gf-form-label {
padding: 4px 0;
}
}
}
.gf-form-disabled {
@ -115,7 +125,7 @@ $gf-form-margin: 3px;
background-color: $input-bg;
background-image: none;
background-clip: padding-box;
border: 1px solid $input-border-color;
border: $input-border;
@include border-radius($input-border-radius-sm);
@include box-shadow($input-box-shadow);
white-space: nowrap;
@ -268,6 +278,7 @@ $gf-form-margin: 3px;
position: relative;
background-color: $input-bg;
padding-right: $input-padding-x;
border: $input-border;
&::after {
position: absolute;
@ -279,6 +290,10 @@ $gf-form-margin: 3px;
content: '\f0d7';
pointer-events: none;
}
.gf-form-input {
border: none;
}
}
.gf-form-help-icon {

View File

@ -23,7 +23,7 @@
position: fixed;
z-index: $zindex-modal;
width: 100%;
background-color: $panel-bg;
background: $page-bg;
@include box-shadow(0 3px 7px rgba(0,0,0,0.3));
@include background-clip(padding-box);
outline: none;
@ -37,13 +37,10 @@
}
.modal-header {
background-color: $body-bg;
@include brand-bottom-border();
@include clearfix();
.gf-tabs-link.active {
background-color: $panel-bg;
}
background: $page-header-bg;
box-shadow: $page-header-shadow;
border-bottom: 1px solid $page-header-border-color;
@include clearfix();
}
.modal-header-title {

View File

@ -1,19 +1,14 @@
.navbar {
display: block;
overflow: visible;
position: relative;
padding-left: $side-menu-width;
box-shadow: $navbarShadow;
z-index: 1;
background: $navbarBackground;
}
.navbar-inner {
min-height: $navbarHeight;
// box-shadow: $navbarShadow;
z-index: $zindex-navbar-fixed;
// background: $navbarBackground;
height: $navbarHeight;
padding-right: $spacer;
display: flex;
@include clearfix();
flex-grow: 1;
}
.sidemenu-open {
@ -22,47 +17,6 @@
}
}
.navbar .nav {
position: relative;
left: 0;
float: left;
&--grow {
flex-grow: 1;
}
}
.navbar .nav > li {
float: left;
}
// Links
.navbar .nav > li > a {
float: none;
padding: 17px 13px 13px;
color: $navbarLinkColor;
text-decoration: none;
.fa { font-size: 115%; }
}
// Hover/focus
.navbar .nav > li > a:focus,
.navbar .nav > li > a:hover {
color: $navbarLinkColorHover;
text-decoration: none;
}
// Active nav items
.navbar .nav > .active > a,
.navbar .nav > .active > a:hover,
.navbar .nav > .active > a:focus {
color: $navbarLinkColorActive;
text-decoration: none;
background-color: $navbarLinkBackgroundActive;
}
.navbar-page-btn {
text-overflow: ellipsis;
overflow: hidden;
@ -106,3 +60,54 @@
.navbar-mini-btn-wrapper {
padding: 15px;
}
.navbar-buttons {
height: $navbarHeight;
display: flex;
align-items: center;
justify-content: flex-end;
margin-right: $spacer;
}
.navbar__spacer {
flex-grow: 1;
}
.navbar-button {
@include buttonBackground($btn-inverse-bg, $btn-inverse-bg-hl, $btn-inverse-text-color);
display: inline-block;
font-weight: $btn-font-weight;
padding: 8px 11px;
line-height: 16px;
color: $text-muted;
border: 1px solid #151515;
margin-right: 1px;
white-space: nowrap;
.gicon {
font-size: 16px;
}
.fa {
font-size: 16px;
}
&--add-panel {
padding: 5px 10px;
.gicon {
font-size: 22px;
}
}
&--tight {
padding: 9px 4px;
.fa {
font-size: 14px;
position: relative;
top: 2px;
}
}
}

View File

@ -0,0 +1,159 @@
.page-header-canvas {
background: $page-header-bg;
box-shadow: $page-header-shadow;
border-bottom: 1px solid $page-header-border-color;
}
.page-header {
padding: 2.5rem 0 0 0;
.btn {
float: right;
margin-left: 1rem;
// better align icons
.fa {
position: relative;
top: 1px;
}
}
}
.page-header__inner {
flex-grow: 1;
display: flex;
margin-bottom: 2.5rem;
}
.page-header__title {
font-size: $font-size-h2;
margin-bottom: 1px;
padding-top: $spacer;
}
.page-header__img {
border-radius: 50%;
position: relative;
top: 10px;
width: 50px;
height: 50px;
}
.page-header__icon {
font-size: 50px;
width: 50px;
height: 50px;
position: relative;
&.fa {
top: 10px;
}
&.gicon {
top: 10px;
}
&.icon-gf {
top: 3px;
}
}
.page-header__logo {
margin: 0 $spacer;
}
.page-header__sub-title {
color: $text-muted;
}
.page-header-stamps-type {
color: $link-color-disabled;
text-transform: uppercase;
}
.page-breadcrumbs {
display: flex;
padding: 10px 0;
line-height: 0.5;
}
.breadcrumb {
display: inline-block;
box-shadow: 0 0 15px 1px rgba(0, 0, 0, 0.35);
overflow: hidden;
border-radius: 5px;
counter-reset: flag;
}
.breadcrumb-item {
@include gradientBar($btn-inverse-bg, $btn-inverse-bg-hl, $btn-inverse-text-color);
text-decoration: none;
outline: none;
display: block;
float: left;
font-size: 12px;
line-height: 30px;
padding: 0 7px 0 37px;
position: relative;
box-shadow: $card-shadow;
&:first-child {
padding-left: 10px;
border-radius: 5px 0 0 5px; /*to match with the parent's radius*/
font-size: 18px;
}
&:first-child::before {
left: 14px;
}
&:last-child {
border-radius: 0 5px 5px 0; /*this was to prevent glitches on hover*/
padding-right: 20px;
}
&.active,
&:hover {
background: #333;
background: linear-gradient(#333, #000);
}
&.active::after,
&:hover::after {
background: #333;
background: linear-gradient(135deg, #333, #000);
}
&::after {
content: '';
position: absolute;
top: 0;
right: -14px; // half of square's length
// same dimension as the line-height of .breadcrumb-item
width: 30px;
height: 30px;
transform: scale(0.707) rotate(45deg);
// we need to prevent the arrows from getting buried under the next link
z-index: 1;
// background same as links but the gradient will be rotated to compensate with the transform applied
background: linear-gradient(135deg, $btn-inverse-bg, $btn-inverse-bg-hl);
// stylish arrow design using box shadow
box-shadow: 2px -2px 0 2px rgb(35, 31, 31), 3px -3px 0 2px rgba(255, 255, 255, 0.1);
// 5px - for rounded arrows and
// 50px - to prevent hover glitches on the border created using shadows*/
border-radius: 0 5px 0 50px;
}
// we dont need an arrow after the last link
&:last-child::after {
content: none;
}
}

View File

@ -1,20 +1,47 @@
.add-panel {
height: 100%;
}
.add-panel__header {
padding: 5px 15px;
display: flex;
align-items: center;
i {
font-size: 30px;
margin-right: $spacer;
}
}
.add-panel__title {
font-size: $font-size-md;
margin-right: $spacer/2;
}
.add-panel__sub-title {
font-style: italic;
color: $text-muted;
position: relative;
top: 1px;
}
.add-panel__items {
padding: 3px 8px;
display: flex;
flex-direction: row;
flex-wrap: wrap;
overflow: auto;
height: 100%;
height: calc(100% - 43px);
align-content: flex-start;
justify-content: space-around;
padding: 3px 0;
}
.add-panel__item {
background: $card-background;
box-shadow: $card-shadow;
border: $panel-border;
border-radius: 3px;
padding: $spacer/3 $spacer;
width: 31%;
height: 60px;

View File

@ -71,7 +71,7 @@
}
.ps__thumb-y {
@include gradient-vertical($blue, lighten($blue, 20%));
@include gradient-vertical($scrollbarBackground, $scrollbarBackground2);
border-radius: 6px;
width: 6px;
/* there must be 'right' for ps__thumb-y */

View File

@ -6,7 +6,7 @@
top: $navbarHeight;
z-index: $zindex-modal-backdrop;
background-color: $black;
@include opacity(70);
@include opacity(75);
}
.search-container {
@ -44,12 +44,6 @@
flex-grow: 1;
}
.search-switches {
flex-grow: 1;
padding: 1rem 1rem 0.75rem 1rem;
white-space: nowrap;
}
.search-field-icon {
font-size: $font-size-lg;
padding: 1rem 1rem 0.75rem 1.5rem;
@ -57,18 +51,45 @@
.search-dropdown {
display: flex;
flex-direction: column;
max-width: 800px;
background: $page-bg;
flex-direction: row;
height: calc(100% - #{$navbarHeight});
}
.search-dropdown__col_1 {
background: $page-bg;
max-width: 700px;
display: flex;
flex-direction: column;
flex-grow: 1;
}
.search-dropdown__col_2 {
flex-grow: 1;
height: 100%;
padding-top: 16px;
}
.search-filter-box {
background: $search-filter-box-bg;
border-radius: 2px;
padding: $spacer*1.5;
max-width: 340px;
margin-bottom: $spacer * 1.5;
margin-left: $spacer * 1.5;
}
.search-filter-box__header {
border-bottom: 1px solid $dark-5;
margin-bottom: $spacer * 1.5;
}
.search-results-container {
height: 100%;
display: block;
padding: $spacer;
position: relative;
flex-grow: 10;
margin-bottom: 1rem;
.label-tag {
margin-left: 6px;
@ -84,9 +105,17 @@
}
}
.search-section {
background: $panel-bg;
border: $panel-border;
padding: $panel-padding;
margin-bottom: 3px;
border-radius: 5px;
}
.search-section__header {
font-size: $font-size-h6;
padding: 0.6rem 0;
padding: 8px 0 2px 0;
color: $text-color-weak;
display: flex;
flex-grow: 1;
@ -105,7 +134,7 @@
}
.search-section__header__icon {
padding: 3px 10px;
padding: 5px 10px;
}
.search-section__header__toggle {
@ -170,24 +199,34 @@
}
.search-item__tags {
padding: 8px;
padding: 10px;
}
.search-item__actions {
flex: 0 0 auto;
padding: 0 10px 0 0;
}
.search-item__actions__item {
display: none;
display: inline-block;
opacity: 0;
width: 0;
transition: all 0.2s ease-in-out;
.fa-star, .fa-star-o {
color: $orange;
line-height: 37px;
}
}
.search-item:hover {
.search-item__actions__item {
opacity: 0.8;
width: 15px;
opacity: 1;
}
}
.search-button-row {
text-align: center;
padding: $spacer*2 $spacer;
background: $panel-bg;
}

View File

@ -4,26 +4,34 @@
flex-flow: column;
flex-direction: column;
width: $side-menu-width;
background: $navbarBackground;
z-index: 10;
z-index: $zindex-sidemenu;
a:focus {
text-decoration: none;
}
.sidemenu__logo_small_breakpoint {
display: none;
}
.sidemenu__close {
display: none;
}
}
.sidemenu-open {
.sidemenu {
background: $side-menu-bg;
position: initial;
height: auto;
box-shadow: $side-menu-shadow;
position: relative;
z-index: 2;
}
.sidemenu__top,
.sidemenu__bottom {
display: block;
@include media-breakpoint-up(sm) {
.sidemenu-open {
.sidemenu {
background: $side-menu-bg;
position: initial;
height: auto;
box-shadow: $side-menu-shadow;
position: relative;
z-index: $zindex-sidemenu;
}
.sidemenu__top,
.sidemenu__bottom {
display: block;
}
}
}
@ -42,21 +50,23 @@
position: relative;
@include left-brand-border();
&.active,
&:hover {
background-color: $side-menu-item-hover-bg;
@include left-brand-border-gradient();
@include media-breakpoint-up(sm) {
&.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 150ms ease-in-out 100ms forwards');
z-index: 1;
.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 150ms ease-in-out 100ms forwards');
z-index: $zindex-sidemenu;
}
}
}
}
@ -154,7 +164,7 @@ li.sidemenu-org-switcher {
}
}
.sidemenu__logo {
.sidemenu__logo, .sidemenu__logo_small_breakpoint {
display: block;
padding: 0.4rem 1.0rem 0.4rem 0.65rem;
min-height: $navbarHeight;
@ -172,3 +182,84 @@ li.sidemenu-org-switcher {
}
}
@include media-breakpoint-down(xs) {
.sidemenu-open {
.navbar {
padding-left: 60px !important;
}
}
.sidemenu-open--xs {
.sidemenu {
width: 100%;
background: $side-menu-bg;
position: initial;
height: auto;
box-shadow: $side-menu-shadow;
position: relative;
z-index: $zindex-sidemenu;
}
.sidemenu__close {
display: block;
font-size: $font-size-md;
}
.sidemenu__top,
.sidemenu__bottom {
display: block;
}
}
.sidemenu {
.sidemenu__logo {
display: none;
}
.sidemenu__logo_small_breakpoint {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: baseline;
&:hover {
background: transparent;
}
}
.sidemenu__top {
padding-top: 0rem;
}
.side-menu-header {
padding-left: 10px;
}
.sidemenu-link {
text-align: left;
}
.sidemenu-icon {
display: none
}
.dropdown-menu--sidemenu {
display: block;
position: unset;
width: 100%;
float: none;
margin-top: 0.5rem;
margin-bottom: 0.5rem;
> li > a {
padding-left: 15px;
}
}
.sidemenu__bottom {
.dropdown-menu--sidemenu {
display: flex;
flex-direction: column-reverse;
}
}
}
}

View File

@ -12,8 +12,10 @@
}
.tabbed-view-header {
background: $page-header-bg;
box-shadow: $page-header-shadow;
border-bottom: 1px solid $page-header-border-color;
@include clearfix();
@include brand-bottom-border();
}
.tabbed-view-title {
@ -34,7 +36,7 @@
margin: 0;
background-color: transparent;
border: none;
padding: ($tabs-padding-top + $tabs-top-margin) $spacer $tabs-padding-bottom;
padding: $tabs-padding;
color: $text-color;
i {
font-size: 120%;

View File

@ -1,41 +1,8 @@
.nav-tabs-alt {
& > li > a {
color: darken($link-color, 20%);
}
li > a:hover {
border-bottom: none;
}
li.active > a,
li.active > a:focus,
li.active > a:hover {
@include border-radius(3px);
border: 1px solid $divider-border-color;
background-color: transparent;
border-bottom: 1px solid $page-bg;
color: $link-color;
}
li.disabled > a {
color: $text-color;
}
.open .dropdown-toggle {
background-color: #060606;
border-color: transparent;
}
.tab-content {
padding: 10px;
background-color: $panel-bg;
}
}
.gf-tabs {
@include clearfix();
float: left;
margin: $tabs-top-margin 0 0 0;
position: relative;
top: 1px;
}
.gf-tabs-item {
@ -44,13 +11,17 @@
}
.gf-tabs-link {
padding: $tabs-padding-top $spacer $tabs-padding-bottom $spacer;
padding: $tabs-padding;
margin-right: $spacer/2;
border: 1px solid transparent;
position: relative;
display: block;
border: solid transparent;
border-width: 2px 1px 1px;
border-radius: 3px 3px 0 0;
@include border-radius(2px 2px 0 0);
i {
margin-right: 5px;
}
&:hover,
&:focus {
@ -60,20 +31,9 @@
&.active,
&.active:hover,
&.active:focus {
@include border-radius(3px);
border-color: rgba(216, 131, 40, 0.77);
border-bottom: 1px solid $page-bg;
border-color: $orange $dark-4 transparent;
background: $page-bg;
color: $link-color;
position: relative;
top: 1px;
}
}
.form-tabs-wrapper {
@include brand-bottom-border();
@include clearfix();
}
.form-tabs-content {
padding: $spacer*2 $spacer;
}

View File

@ -1,15 +1,15 @@
.nav.gf-timepicker-nav {
margin-right: 0;
}
.timepicker-timestring {
font-weight: normal;
}
.gf-timepicker-nav {
flex-wrap: nowrap;
display: flex;
}
.gf-timepicker-nav-btn {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
.gf-timepicker-dropdown {
@ -118,14 +118,3 @@
@extend .fa-chevron-left;
}
.gf-timepicker-time-control {
font-size: $font-size-sm;
a {
padding: 18px 7px 13px !important;
}
}
.dashnav-move-timeframe {
position: relative;
top: 1px;
}

View File

@ -21,33 +21,22 @@
.playlist-active,
.user-activity-low {
.react-resizable-handle
.add-row-panel-hint,
.dash-row-menu-container,
.panel-drop-zone
.dashnav-refresh-action,
.dashnav-zoom-out,
.navbar-button--refresh,
.navbar-buttons--zoom,
.navbar-buttons--actions,
.panel-menu-container,
.dashnav-action-icons,
.panel-info-corner--info,
.panel-info-corner--links,
.dashnav-move-timeframe {
.panel-info-corner--links {
opacity: 0;
transition: all 1.5s ease-in-out 1s;
}
// navbar buttons
.navbar {
border-color: transparent;
background: transparent;
transition: all 1.5s ease-in-out 1s;
.fa {
opacity: 0;
transition: all 1.5s ease-in-out 1s;
}
}
.navbar {
box-shadow: none;
background: transparent;
}
.navbar-page-btn {

Some files were not shown because too many files have changed in this diff Show More