Quellcode durchsuchen

rebase against master

benrubson vor 9 Jahren
Ursprung
Commit
f4037667fa
100 geänderte Dateien mit 2069 neuen und 2111 gelöschten Zeilen
  1. 1 0
      .gitignore
  2. 1 0
      CHANGELOG.md
  3. 1 1
      docker/create_docker_compose.sh
  4. 3 3
      docs/sources/installation/debian.md
  5. 3 3
      pkg/api/api.go
  6. 0 59
      pkg/api/app_settings.go
  7. 44 0
      pkg/api/cloudwatch/cloudwatch.go
  8. 0 8
      pkg/api/dataproxy.go
  9. 0 40
      pkg/api/dtos/apps.go
  10. 6 5
      pkg/api/dtos/index.go
  11. 26 0
      pkg/api/dtos/plugins.go
  12. 7 7
      pkg/api/index.go
  13. 88 0
      pkg/api/plugin_setting.go
  14. 1 1
      pkg/api/pluginproxy/pluginproxy.go
  15. 2 2
      pkg/api/pluginproxy/pluginproxy_test.go
  16. 13 13
      pkg/models/plugin_setting.go
  17. 4 0
      pkg/plugins/frontend_plugin.go
  18. 11 15
      pkg/plugins/queries.go
  19. 0 70
      pkg/services/sqlstore/app_settings.go
  20. 6 8
      pkg/services/sqlstore/migrations/plugin_setting.go
  21. 70 0
      pkg/services/sqlstore/plugin_setting.go
  22. 81 0
      public/app/core/components/colorpicker/colorpicker.ts
  23. 1 1
      public/app/core/components/grafana_app.ts
  24. 55 0
      public/app/core/components/popover/popover.ts
  25. 24 26
      public/app/core/components/sidemenu/sidemenu.html
  26. 16 1
      public/app/core/components/sidemenu/sidemenu.ts
  27. 12 1
      public/app/core/core.ts
  28. 4 2
      public/app/core/directives/annotation_tooltip.js
  29. 1 6
      public/app/core/directives/dash_edit_link.js
  30. 1 1
      public/app/core/directives/misc.js
  31. 5 5
      public/app/core/directives/plugin_component.ts
  32. 12 12
      public/app/core/routes/routes.ts
  33. 0 60
      public/app/core/services/popover_srv.js
  34. 55 0
      public/app/core/services/popover_srv.ts
  35. 45 18
      public/app/core/utils/kbn.js
  36. 1 1
      public/app/features/admin/partials/admin_home.html
  37. 36 47
      public/app/features/admin/partials/edit_org.html
  38. 51 102
      public/app/features/admin/partials/edit_user.html
  39. 24 52
      public/app/features/admin/partials/new_user.html
  40. 5 4
      public/app/features/admin/partials/orgs.html
  41. 1 3
      public/app/features/admin/partials/settings.html
  42. 1 3
      public/app/features/admin/partials/stats.html
  43. 4 1
      public/app/features/admin/partials/users.html
  44. 76 83
      public/app/features/annotations/partials/editor.html
  45. 0 49
      public/app/features/apps/edit_ctrl.ts
  46. 0 17
      public/app/features/apps/list_ctrl.ts
  47. 0 108
      public/app/features/apps/partials/edit.html
  48. 0 47
      public/app/features/apps/partials/list.html
  49. 2 2
      public/app/features/dashboard/dashnav/dashnav.html
  50. 6 0
      public/app/features/dashboard/dashnav/dashnav.ts
  51. 8 0
      public/app/features/dashboard/keybindings.js
  52. 16 15
      public/app/features/dashboard/partials/graphiteImport.html
  53. 3 1
      public/app/features/dashboard/partials/import.html
  54. 0 62
      public/app/features/dashboard/partials/linksEditor.html
  55. 85 148
      public/app/features/dashboard/partials/settings.html
  56. 53 85
      public/app/features/dashboard/partials/shareModal.html
  57. 22 25
      public/app/features/dashboard/submenu/submenu.html
  58. 12 45
      public/app/features/dashboard/timepicker/settings.html
  59. 0 2
      public/app/features/dashboard/timepicker/timepicker.ts
  60. 65 79
      public/app/features/dashlinks/editor.html
  61. 7 0
      public/app/features/datasources/partials/edit.html
  62. 15 1
      public/app/features/datasources/partials/http_settings.html
  63. 2 2
      public/app/features/datasources/partials/list.html
  64. 7 14
      public/app/features/org/partials/apikeyModal.html
  65. 7 15
      public/app/features/org/partials/newOrg.html
  66. 1 1
      public/app/features/org/partials/orgDetails.html
  67. 1 1
      public/app/features/org/partials/orgUsers.html
  68. 16 13
      public/app/features/panel/panel_directive.ts
  69. 31 56
      public/app/features/panel/partials/panelTime.html
  70. 49 66
      public/app/features/panellinks/module.html
  71. 0 1
      public/app/features/panellinks/module.js
  72. 46 27
      public/app/features/playlist/partials/playlist.html
  73. 1 1
      public/app/features/playlist/partials/playlists.html
  74. 1 1
      public/app/features/playlist/playlist_edit_ctrl.ts
  75. 0 0
      public/app/features/plugins/all.ts
  76. 51 0
      public/app/features/plugins/edit_ctrl.ts
  77. 17 0
      public/app/features/plugins/list_ctrl.ts
  78. 0 0
      public/app/features/plugins/page_ctrl.ts
  79. 185 0
      public/app/features/plugins/partials/edit.html
  80. 42 0
      public/app/features/plugins/partials/list.html
  81. 0 0
      public/app/features/plugins/partials/page.html
  82. 1 1
      public/app/features/profile/partials/profile.html
  83. 31 31
      public/app/features/snapshot/partials/snapshots.html
  84. 1 1
      public/app/features/styleguide/styleguide.html
  85. 174 288
      public/app/features/templating/partials/editor.html
  86. 8 0
      public/app/headers/common.d.ts
  87. 2 1
      public/app/partials/bootstrap/tabset.html
  88. 0 11
      public/app/partials/colorpicker.html
  89. 12 13
      public/app/partials/edit_json.html
  90. 8 0
      public/app/partials/help_modal.html
  91. 1 5
      public/app/partials/panelgeneral.html
  92. 20 47
      public/app/partials/reset_password.html
  93. 33 50
      public/app/partials/roweditor.html
  94. 2 2
      public/app/partials/valueSelectDropdown.html
  95. 2 0
      public/app/plugins/datasource/cloudwatch/annotation_query.d.ts
  96. 105 0
      public/app/plugins/datasource/cloudwatch/annotation_query.js
  97. 21 58
      public/app/plugins/datasource/cloudwatch/datasource.js
  98. 18 0
      public/app/plugins/datasource/cloudwatch/partials/annotations.editor.html
  99. 81 0
      public/app/plugins/datasource/cloudwatch/specs/annotation_query_specs.ts
  100. 0 55
      public/app/plugins/datasource/cloudwatch/specs/datasource_specs.ts

+ 1 - 0
.gitignore

@@ -31,6 +31,7 @@ public/css/*.min.css
 
 
 conf/custom.ini
 conf/custom.ini
 fig.yml
 fig.yml
+docker-compose.yml
 profile.cov
 profile.cov
 /grafana
 /grafana
 .notouch
 .notouch

+ 1 - 0
CHANGELOG.md

@@ -7,6 +7,7 @@
 * **Snapshots UI**: Dashboard snapshots list can be managed through UI, closes[#1984](https://github.com/grafana/grafana/issues/1984)
 * **Snapshots UI**: Dashboard snapshots list can be managed through UI, closes[#1984](https://github.com/grafana/grafana/issues/1984)
 * **Prometheus**: Prometheus annotation support, closes[#2883](https://github.com/grafana/grafana/pull/2883)
 * **Prometheus**: Prometheus annotation support, closes[#2883](https://github.com/grafana/grafana/pull/2883)
 * **Cli**: New cli tool for downloading and updating plugins
 * **Cli**: New cli tool for downloading and updating plugins
+* **Annotations**: Annotations can now contain links that can be clicked (you can navigate on to annotation popovers), closes [#1588](https://github.com/grafana/grafana/issues/1588)
 
 
 ### Breaking changes
 ### Breaking changes
 * **Plugin API**: Both datasource and panel plugin api (and plugin.json schema) have been updated, requiring an update to plugins. See [plugin api](https://github.com/grafana/grafana/blob/master/public/app/plugins/plugin_api.md) for more info.
 * **Plugin API**: Both datasource and panel plugin api (and plugin.json schema) have been updated, requiring an update to plugins. See [plugin api](https://github.com/grafana/grafana/blob/master/public/app/plugins/plugin_api.md) for more info.

+ 1 - 1
docker/build_fig.sh → docker/create_docker_compose.sh

@@ -7,7 +7,7 @@ template_dir=templates
 grafana_config_file=conf.tmp
 grafana_config_file=conf.tmp
 grafana_config=config
 grafana_config=config
 
 
-fig_file=fig.yml
+fig_file=docker-compose.yml
 fig_config=fig
 fig_config=fig
 
 
 if [ "$#" == 0 ]; then
 if [ "$#" == 0 ]; then

+ 3 - 3
docs/sources/installation/debian.md

@@ -122,8 +122,8 @@ To configure Grafana add a configuration file named `custom.ini` to the
 `conf` folder and override any of the settings defined in
 `conf` folder and override any of the settings defined in
 `conf/defaults.ini`.
 `conf/defaults.ini`.
 
 
-Start Grafana by executing `./grafana-server web`. The `grafana-server` binary needs
-the working directory to be the root install directory (where the binary
-and the `public` folder is located).
+Start Grafana by executing `./bin/grafana-server web`. The `grafana-server`
+binary needs the working directory to be the root install directory (where the
+binary and the `public` folder is located).
 
 
 
 

+ 3 - 3
pkg/api/api.go

@@ -126,9 +126,9 @@ func Register(r *macaron.Macaron) {
 			r.Patch("/invites/:code/revoke", wrap(RevokeInvite))
 			r.Patch("/invites/:code/revoke", wrap(RevokeInvite))
 
 
 			// apps
 			// apps
-			r.Get("/apps", wrap(GetOrgAppsList))
-			r.Get("/apps/:appId/settings", wrap(GetAppSettingsById))
-			r.Post("/apps/:appId/settings", bind(m.UpdateAppSettingsCmd{}), wrap(UpdateAppSettings))
+			r.Get("/plugins", wrap(GetPluginList))
+			r.Get("/plugins/:pluginId/settings", wrap(GetPluginSettingById))
+			r.Post("/plugins/:pluginId/settings", bind(m.UpdatePluginSettingCmd{}), wrap(UpdatePluginSetting))
 		}, reqOrgAdmin)
 		}, reqOrgAdmin)
 
 
 		// create new org
 		// create new org

+ 0 - 59
pkg/api/app_settings.go

@@ -1,59 +0,0 @@
-package api
-
-import (
-	"github.com/grafana/grafana/pkg/api/dtos"
-	"github.com/grafana/grafana/pkg/bus"
-	"github.com/grafana/grafana/pkg/middleware"
-	m "github.com/grafana/grafana/pkg/models"
-	"github.com/grafana/grafana/pkg/plugins"
-)
-
-func GetOrgAppsList(c *middleware.Context) Response {
-	orgApps, err := plugins.GetOrgAppSettings(c.OrgId)
-
-	if err != nil {
-		return ApiError(500, "Failed to list of apps", err)
-	}
-
-	result := make([]*dtos.AppSettings, 0)
-	for _, app := range plugins.Apps {
-		orgApp := orgApps[app.Id]
-		result = append(result, dtos.NewAppSettingsDto(app, orgApp))
-	}
-
-	return Json(200, result)
-}
-
-func GetAppSettingsById(c *middleware.Context) Response {
-	appId := c.Params(":appId")
-
-	if pluginDef, exists := plugins.Apps[appId]; !exists {
-		return ApiError(404, "PluginId not found, no installed plugin with that id", nil)
-	} else {
-		orgApps, err := plugins.GetOrgAppSettings(c.OrgId)
-		if err != nil {
-			return ApiError(500, "Failed to get org app settings ", nil)
-		}
-		orgApp := orgApps[appId]
-
-		return Json(200, dtos.NewAppSettingsDto(pluginDef, orgApp))
-	}
-}
-
-func UpdateAppSettings(c *middleware.Context, cmd m.UpdateAppSettingsCmd) Response {
-	appId := c.Params(":appId")
-
-	cmd.OrgId = c.OrgId
-	cmd.AppId = appId
-
-	if _, ok := plugins.Apps[cmd.AppId]; !ok {
-		return ApiError(404, "App type not installed.", nil)
-	}
-
-	err := bus.Dispatch(&cmd)
-	if err != nil {
-		return ApiError(500, "Failed to update App Plugin", err)
-	}
-
-	return ApiSuccess("App updated")
-}

+ 44 - 0
pkg/api/cloudwatch/cloudwatch.go

@@ -33,6 +33,7 @@ func init() {
 	actionHandlers = map[string]actionHandler{
 	actionHandlers = map[string]actionHandler{
 		"GetMetricStatistics":     handleGetMetricStatistics,
 		"GetMetricStatistics":     handleGetMetricStatistics,
 		"ListMetrics":             handleListMetrics,
 		"ListMetrics":             handleListMetrics,
+		"DescribeAlarms":          handleDescribeAlarms,
 		"DescribeAlarmsForMetric": handleDescribeAlarmsForMetric,
 		"DescribeAlarmsForMetric": handleDescribeAlarmsForMetric,
 		"DescribeAlarmHistory":    handleDescribeAlarmHistory,
 		"DescribeAlarmHistory":    handleDescribeAlarmHistory,
 		"DescribeInstances":       handleDescribeInstances,
 		"DescribeInstances":       handleDescribeInstances,
@@ -142,6 +143,49 @@ func handleListMetrics(req *cwRequest, c *middleware.Context) {
 	c.JSON(200, resp)
 	c.JSON(200, resp)
 }
 }
 
 
+func handleDescribeAlarms(req *cwRequest, c *middleware.Context) {
+	cfg := &aws.Config{
+		Region:      aws.String(req.Region),
+		Credentials: getCredentials(req.DataSource.Database),
+	}
+
+	svc := cloudwatch.New(session.New(cfg), cfg)
+
+	reqParam := &struct {
+		Parameters struct {
+			ActionPrefix    string    `json:"actionPrefix"`
+			AlarmNamePrefix string    `json:"alarmNamePrefix"`
+			AlarmNames      []*string `json:"alarmNames"`
+			StateValue      string    `json:"stateValue"`
+		} `json:"parameters"`
+	}{}
+	json.Unmarshal(req.Body, reqParam)
+
+	params := &cloudwatch.DescribeAlarmsInput{
+		MaxRecords: aws.Int64(100),
+	}
+	if reqParam.Parameters.ActionPrefix != "" {
+		params.ActionPrefix = aws.String(reqParam.Parameters.ActionPrefix)
+	}
+	if reqParam.Parameters.AlarmNamePrefix != "" {
+		params.AlarmNamePrefix = aws.String(reqParam.Parameters.AlarmNamePrefix)
+	}
+	if len(reqParam.Parameters.AlarmNames) != 0 {
+		params.AlarmNames = reqParam.Parameters.AlarmNames
+	}
+	if reqParam.Parameters.StateValue != "" {
+		params.StateValue = aws.String(reqParam.Parameters.StateValue)
+	}
+
+	resp, err := svc.DescribeAlarms(params)
+	if err != nil {
+		c.JsonApiErr(500, "Unable to call AWS API", err)
+		return
+	}
+
+	c.JSON(200, resp)
+}
+
 func handleDescribeAlarmsForMetric(req *cwRequest, c *middleware.Context) {
 func handleDescribeAlarmsForMetric(req *cwRequest, c *middleware.Context) {
 	cfg := &aws.Config{
 	cfg := &aws.Config{
 		Region:      aws.String(req.Region),
 		Region:      aws.String(req.Region),

+ 0 - 8
pkg/api/dataproxy.go

@@ -64,20 +64,12 @@ func NewReverseProxy(ds *m.DataSource, proxyPath string, targetUrl *url.URL) *ht
 	return &httputil.ReverseProxy{Director: director}
 	return &httputil.ReverseProxy{Director: director}
 }
 }
 
 
-var dsMap map[int64]*m.DataSource = make(map[int64]*m.DataSource)
-
 func getDatasource(id int64, orgId int64) (*m.DataSource, error) {
 func getDatasource(id int64, orgId int64) (*m.DataSource, error) {
-	// ds, exists := dsMap[id]
-	// if exists && ds.OrgId == orgId {
-	// 	return ds, nil
-	// }
-
 	query := m.GetDataSourceByIdQuery{Id: id, OrgId: orgId}
 	query := m.GetDataSourceByIdQuery{Id: id, OrgId: orgId}
 	if err := bus.Dispatch(&query); err != nil {
 	if err := bus.Dispatch(&query); err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 
 
-	dsMap[id] = &query.Result
 	return &query.Result, nil
 	return &query.Result, nil
 }
 }
 
 

+ 0 - 40
pkg/api/dtos/apps.go

@@ -1,40 +0,0 @@
-package dtos
-
-import (
-	"github.com/grafana/grafana/pkg/models"
-	"github.com/grafana/grafana/pkg/plugins"
-)
-
-type AppSettings struct {
-	Name     string                    `json:"name"`
-	AppId    string                    `json:"appId"`
-	Enabled  bool                      `json:"enabled"`
-	Pinned   bool                      `json:"pinned"`
-	Module   string                    `json:"module"`
-	BaseUrl  string                    `json:"baseUrl"`
-	Info     *plugins.PluginInfo       `json:"info"`
-	Pages    []*plugins.AppPluginPage  `json:"pages"`
-	Includes []*plugins.AppIncludeInfo `json:"includes"`
-	JsonData map[string]interface{}    `json:"jsonData"`
-}
-
-func NewAppSettingsDto(def *plugins.AppPlugin, data *models.AppSettings) *AppSettings {
-	dto := &AppSettings{
-		AppId:    def.Id,
-		Name:     def.Name,
-		Info:     &def.Info,
-		Module:   def.Module,
-		BaseUrl:  def.BaseUrl,
-		Pages:    def.Pages,
-		Includes: def.Includes,
-	}
-
-	if data != nil {
-		dto.Enabled = data.Enabled
-		dto.Pinned = data.Pinned
-		dto.Info = &def.Info
-		dto.JsonData = data.JsonData
-	}
-
-	return dto
-}

+ 6 - 5
pkg/api/dtos/index.go

@@ -16,9 +16,10 @@ type PluginCss struct {
 }
 }
 
 
 type NavLink struct {
 type NavLink struct {
-	Text     string     `json:"text"`
-	Icon     string     `json:"icon"`
-	Img      string     `json:"img"`
-	Url      string     `json:"url"`
-	Children []*NavLink `json:"children"`
+	Text     string     `json:"text,omitempty"`
+	Icon     string     `json:"icon,omitempty"`
+	Img      string     `json:"img,omitempty"`
+	Url      string     `json:"url,omitempty"`
+	Divider  bool       `json:"divider,omitempty"`
+	Children []*NavLink `json:"children,omitempty"`
 }
 }

+ 26 - 0
pkg/api/dtos/plugins.go

@@ -0,0 +1,26 @@
+package dtos
+
+import "github.com/grafana/grafana/pkg/plugins"
+
+type PluginSetting struct {
+	Name     string                    `json:"name"`
+	Type     string                    `json:"type"`
+	PluginId string                    `json:"pluginId"`
+	Enabled  bool                      `json:"enabled"`
+	Pinned   bool                      `json:"pinned"`
+	Module   string                    `json:"module"`
+	BaseUrl  string                    `json:"baseUrl"`
+	Info     *plugins.PluginInfo       `json:"info"`
+	Pages    []*plugins.AppPluginPage  `json:"pages"`
+	Includes []*plugins.AppIncludeInfo `json:"includes"`
+	JsonData map[string]interface{}    `json:"jsonData"`
+}
+
+type PluginListItem struct {
+	Name     string              `json:"name"`
+	Type     string              `json:"type"`
+	PluginId string              `json:"pluginId"`
+	Enabled  bool                `json:"enabled"`
+	Pinned   bool                `json:"pinned"`
+	Info     *plugins.PluginInfo `json:"info"`
+}

+ 7 - 7
pkg/api/index.go

@@ -53,15 +53,15 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
 		Icon: "icon-gf icon-gf-dashboard",
 		Icon: "icon-gf icon-gf-dashboard",
 		Url:  setting.AppSubUrl + "/",
 		Url:  setting.AppSubUrl + "/",
 		Children: []*dtos.NavLink{
 		Children: []*dtos.NavLink{
-			{Text: "Home dashboard", Icon: "fa fa-fw fa-list", Url: setting.AppSubUrl + "/"},
-			{Text: "Playlists", Icon: "fa fa-fw fa-list", Url: setting.AppSubUrl + "/playlists"},
-			{Text: "Snapshots", Icon: "fa-fw icon-gf icon-gf-snapshot", Url: setting.AppSubUrl + "/dashboard/snapshots"},
+			{Text: "Home", Url: setting.AppSubUrl + "/"},
+			{Text: "Playlists", Url: setting.AppSubUrl + "/playlists"},
+			{Text: "Snapshots", Url: setting.AppSubUrl + "/dashboard/snapshots"},
+			{Divider: true},
+			{Text: "New", Url: setting.AppSubUrl + "/dashboard/new"},
+			{Text: "Import", Url: setting.AppSubUrl + "/import/dashboard"},
 		},
 		},
 	})
 	})
 
 
-	// data.MainNavLinks = append(data.MainNavLinks, &dtos.NavLink{Text: "Playlists", Icon: "fa fa-fw fa-list", Url: setting.AppSubUrl + "/playlists"})
-	// data.MainNavLinks = append(data.MainNavLinks, &dtos.NavLink{Text: "Snapshots", Icon: "fa-fw icon-gf icon-gf-snapshot", Url: setting.AppSubUrl + "/dashboard/snapshots"})
-
 	if c.OrgRole == m.ROLE_ADMIN {
 	if c.OrgRole == m.ROLE_ADMIN {
 		data.MainNavLinks = append(data.MainNavLinks, &dtos.NavLink{
 		data.MainNavLinks = append(data.MainNavLinks, &dtos.NavLink{
 			Text: "Data Sources",
 			Text: "Data Sources",
@@ -72,7 +72,7 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
 		data.MainNavLinks = append(data.MainNavLinks, &dtos.NavLink{
 		data.MainNavLinks = append(data.MainNavLinks, &dtos.NavLink{
 			Text: "Plugins",
 			Text: "Plugins",
 			Icon: "icon-gf icon-gf-apps",
 			Icon: "icon-gf icon-gf-apps",
-			Url:  setting.AppSubUrl + "/apps",
+			Url:  setting.AppSubUrl + "/plugins",
 		})
 		})
 	}
 	}
 
 

+ 88 - 0
pkg/api/plugin_setting.go

@@ -0,0 +1,88 @@
+package api
+
+import (
+	"github.com/grafana/grafana/pkg/api/dtos"
+	"github.com/grafana/grafana/pkg/bus"
+	"github.com/grafana/grafana/pkg/middleware"
+	m "github.com/grafana/grafana/pkg/models"
+	"github.com/grafana/grafana/pkg/plugins"
+)
+
+func GetPluginList(c *middleware.Context) Response {
+	pluginSettingsMap, err := plugins.GetPluginSettings(c.OrgId)
+
+	if err != nil {
+		return ApiError(500, "Failed to get list of plugins", err)
+	}
+
+	result := make([]*dtos.PluginListItem, 0)
+	for _, pluginDef := range plugins.Plugins {
+		listItem := &dtos.PluginListItem{
+			PluginId: pluginDef.Id,
+			Name:     pluginDef.Name,
+			Type:     pluginDef.Type,
+			Info:     &pluginDef.Info,
+		}
+
+		if pluginSetting, exists := pluginSettingsMap[pluginDef.Id]; exists {
+			listItem.Enabled = pluginSetting.Enabled
+			listItem.Pinned = pluginSetting.Pinned
+		}
+
+		result = append(result, listItem)
+	}
+
+	return Json(200, result)
+}
+
+func GetPluginSettingById(c *middleware.Context) Response {
+	pluginId := c.Params(":pluginId")
+
+	if def, exists := plugins.Plugins[pluginId]; !exists {
+		return ApiError(404, "Plugin not found, no installed plugin with that id", nil)
+	} else {
+		dto := &dtos.PluginSetting{
+			Type:     def.Type,
+			PluginId: def.Id,
+			Name:     def.Name,
+			Info:     &def.Info,
+		}
+
+		if app, exists := plugins.Apps[pluginId]; exists {
+			dto.Pages = app.Pages
+			dto.Includes = app.Includes
+			dto.BaseUrl = app.BaseUrl
+			dto.Module = app.Module
+		}
+
+		query := m.GetPluginSettingByIdQuery{PluginId: pluginId, OrgId: c.OrgId}
+		if err := bus.Dispatch(&query); err != nil {
+			if err != m.ErrPluginSettingNotFound {
+				return ApiError(500, "Failed to get login settings", nil)
+			}
+		} else {
+			dto.Enabled = query.Result.Enabled
+			dto.Pinned = query.Result.Pinned
+			dto.JsonData = query.Result.JsonData
+		}
+
+		return Json(200, dto)
+	}
+}
+
+func UpdatePluginSetting(c *middleware.Context, cmd m.UpdatePluginSettingCmd) Response {
+	pluginId := c.Params(":pluginId")
+
+	cmd.OrgId = c.OrgId
+	cmd.PluginId = pluginId
+
+	if _, ok := plugins.Apps[cmd.PluginId]; !ok {
+		return ApiError(404, "Plugin not installed.", nil)
+	}
+
+	if err := bus.Dispatch(&cmd); err != nil {
+		return ApiError(500, "Failed to update plugin setting", err)
+	}
+
+	return ApiSuccess("Plugin settings updated")
+}

+ 1 - 1
pkg/api/pluginproxy/pluginproxy.go

@@ -26,7 +26,7 @@ type templateData struct {
 func getHeaders(route *plugins.AppPluginRoute, orgId int64, appId string) (http.Header, error) {
 func getHeaders(route *plugins.AppPluginRoute, orgId int64, appId string) (http.Header, error) {
 	result := http.Header{}
 	result := http.Header{}
 
 
-	query := m.GetAppSettingByAppIdQuery{OrgId: orgId, AppId: appId}
+	query := m.GetPluginSettingByIdQuery{OrgId: orgId, PluginId: appId}
 
 
 	if err := bus.Dispatch(&query); err != nil {
 	if err := bus.Dispatch(&query); err != nil {
 		return nil, err
 		return nil, err

+ 2 - 2
pkg/api/pluginproxy/pluginproxy_test.go

@@ -22,8 +22,8 @@ func TestPluginProxy(t *testing.T) {
 
 
 		setting.SecretKey = "password"
 		setting.SecretKey = "password"
 
 
-		bus.AddHandler("test", func(query *m.GetAppSettingByAppIdQuery) error {
-			query.Result = &m.AppSettings{
+		bus.AddHandler("test", func(query *m.GetPluginSettingByIdQuery) error {
+			query.Result = &m.PluginSetting{
 				SecureJsonData: map[string][]byte{
 				SecureJsonData: map[string][]byte{
 					"key": util.Encrypt([]byte("123"), "password"),
 					"key": util.Encrypt([]byte("123"), "password"),
 				},
 				},

+ 13 - 13
pkg/models/app_settings.go → pkg/models/plugin_setting.go

@@ -9,12 +9,12 @@ import (
 )
 )
 
 
 var (
 var (
-	ErrAppSettingNotFound = errors.New("AppSetting not found")
+	ErrPluginSettingNotFound = errors.New("Plugin setting not found")
 )
 )
 
 
-type AppSettings struct {
+type PluginSetting struct {
 	Id             int64
 	Id             int64
-	AppId          string
+	PluginId       string
 	OrgId          int64
 	OrgId          int64
 	Enabled        bool
 	Enabled        bool
 	Pinned         bool
 	Pinned         bool
@@ -39,17 +39,17 @@ func (s SecureJsonData) Decrypt() map[string]string {
 // COMMANDS
 // COMMANDS
 
 
 // Also acts as api DTO
 // Also acts as api DTO
-type UpdateAppSettingsCmd struct {
+type UpdatePluginSettingCmd struct {
 	Enabled        bool                   `json:"enabled"`
 	Enabled        bool                   `json:"enabled"`
 	Pinned         bool                   `json:"pinned"`
 	Pinned         bool                   `json:"pinned"`
 	JsonData       map[string]interface{} `json:"jsonData"`
 	JsonData       map[string]interface{} `json:"jsonData"`
 	SecureJsonData map[string]string      `json:"secureJsonData"`
 	SecureJsonData map[string]string      `json:"secureJsonData"`
 
 
-	AppId string `json:"-"`
-	OrgId int64  `json:"-"`
+	PluginId string `json:"-"`
+	OrgId    int64  `json:"-"`
 }
 }
 
 
-func (cmd *UpdateAppSettingsCmd) GetEncryptedJsonData() SecureJsonData {
+func (cmd *UpdatePluginSettingCmd) GetEncryptedJsonData() SecureJsonData {
 	encrypted := make(SecureJsonData)
 	encrypted := make(SecureJsonData)
 	for key, data := range cmd.SecureJsonData {
 	for key, data := range cmd.SecureJsonData {
 		encrypted[key] = util.Encrypt([]byte(data), setting.SecretKey)
 		encrypted[key] = util.Encrypt([]byte(data), setting.SecretKey)
@@ -59,13 +59,13 @@ func (cmd *UpdateAppSettingsCmd) GetEncryptedJsonData() SecureJsonData {
 
 
 // ---------------------
 // ---------------------
 // QUERIES
 // QUERIES
-type GetAppSettingsQuery struct {
+type GetPluginSettingsQuery struct {
 	OrgId  int64
 	OrgId  int64
-	Result []*AppSettings
+	Result []*PluginSetting
 }
 }
 
 
-type GetAppSettingByAppIdQuery struct {
-	AppId  string
-	OrgId  int64
-	Result *AppSettings
+type GetPluginSettingByIdQuery struct {
+	PluginId string
+	OrgId    int64
+	Result   *PluginSetting
 }
 }

+ 4 - 0
pkg/plugins/frontend_plugin.go

@@ -56,6 +56,10 @@ func (fp *FrontendPluginBase) handleModuleDefaults() {
 }
 }
 
 
 func evalRelativePluginUrlPath(pathStr string, pluginId string) string {
 func evalRelativePluginUrlPath(pathStr string, pluginId string) string {
+	if pathStr == "" {
+		return ""
+	}
+
 	u, _ := url.Parse(pathStr)
 	u, _ := url.Parse(pathStr)
 	if u.IsAbs() {
 	if u.IsAbs() {
 		return pathStr
 		return pathStr

+ 11 - 15
pkg/plugins/queries.go

@@ -5,44 +5,40 @@ import (
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
 )
 )
 
 
-func GetOrgAppSettings(orgId int64) (map[string]*m.AppSettings, error) {
-	query := m.GetAppSettingsQuery{OrgId: orgId}
+func GetPluginSettings(orgId int64) (map[string]*m.PluginSetting, error) {
+	query := m.GetPluginSettingsQuery{OrgId: orgId}
 
 
 	if err := bus.Dispatch(&query); err != nil {
 	if err := bus.Dispatch(&query); err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 
 
-	orgAppsMap := make(map[string]*m.AppSettings)
-	for _, orgApp := range query.Result {
-		orgAppsMap[orgApp.AppId] = orgApp
+	pluginMap := make(map[string]*m.PluginSetting)
+	for _, plug := range query.Result {
+		pluginMap[plug.PluginId] = plug
 	}
 	}
 
 
-	return orgAppsMap, nil
+	return pluginMap, nil
 }
 }
 
 
 func GetEnabledPlugins(orgId int64) (*EnabledPlugins, error) {
 func GetEnabledPlugins(orgId int64) (*EnabledPlugins, error) {
 	enabledPlugins := NewEnabledPlugins()
 	enabledPlugins := NewEnabledPlugins()
-	orgApps, err := GetOrgAppSettings(orgId)
+	orgPlugins, err := GetPluginSettings(orgId)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 
 
 	enabledApps := make(map[string]bool)
 	enabledApps := make(map[string]bool)
 
 
-	for appId, installedApp := range Apps {
-		var app AppPlugin
-		app = *installedApp
+	for pluginId, app := range Apps {
 
 
-		// check if the app is stored in the DB for this org and if so, use the
-		// state stored there.
-		if b, ok := orgApps[appId]; ok {
+		if b, ok := orgPlugins[pluginId]; ok {
 			app.Enabled = b.Enabled
 			app.Enabled = b.Enabled
 			app.Pinned = b.Pinned
 			app.Pinned = b.Pinned
 		}
 		}
 
 
 		if app.Enabled {
 		if app.Enabled {
-			enabledApps[app.Id] = true
-			enabledPlugins.Apps = append(enabledPlugins.Apps, &app)
+			enabledApps[pluginId] = true
+			enabledPlugins.Apps = append(enabledPlugins.Apps, app)
 		}
 		}
 	}
 	}
 
 

+ 0 - 70
pkg/services/sqlstore/app_settings.go

@@ -1,70 +0,0 @@
-package sqlstore
-
-import (
-	"time"
-
-	"github.com/grafana/grafana/pkg/bus"
-	m "github.com/grafana/grafana/pkg/models"
-	"github.com/grafana/grafana/pkg/setting"
-	"github.com/grafana/grafana/pkg/util"
-)
-
-func init() {
-	bus.AddHandler("sql", GetAppSettings)
-	bus.AddHandler("sql", GetAppSettingByAppId)
-	bus.AddHandler("sql", UpdateAppSettings)
-}
-
-func GetAppSettings(query *m.GetAppSettingsQuery) error {
-	sess := x.Where("org_id=?", query.OrgId)
-
-	query.Result = make([]*m.AppSettings, 0)
-	return sess.Find(&query.Result)
-}
-
-func GetAppSettingByAppId(query *m.GetAppSettingByAppIdQuery) error {
-	appSetting := m.AppSettings{OrgId: query.OrgId, AppId: query.AppId}
-	has, err := x.Get(&appSetting)
-	if err != nil {
-		return err
-	} else if has == false {
-		return m.ErrAppSettingNotFound
-	}
-	query.Result = &appSetting
-	return nil
-}
-
-func UpdateAppSettings(cmd *m.UpdateAppSettingsCmd) error {
-	return inTransaction2(func(sess *session) error {
-		var app m.AppSettings
-
-		exists, err := sess.Where("org_id=? and app_id=?", cmd.OrgId, cmd.AppId).Get(&app)
-		sess.UseBool("enabled")
-		sess.UseBool("pinned")
-		if !exists {
-			app = m.AppSettings{
-				AppId:          cmd.AppId,
-				OrgId:          cmd.OrgId,
-				Enabled:        cmd.Enabled,
-				Pinned:         cmd.Pinned,
-				JsonData:       cmd.JsonData,
-				SecureJsonData: cmd.GetEncryptedJsonData(),
-				Created:        time.Now(),
-				Updated:        time.Now(),
-			}
-			_, err = sess.Insert(&app)
-			return err
-		} else {
-			for key, data := range cmd.SecureJsonData {
-				app.SecureJsonData[key] = util.Encrypt([]byte(data), setting.SecretKey)
-			}
-			app.SecureJsonData = cmd.GetEncryptedJsonData()
-			app.Updated = time.Now()
-			app.Enabled = cmd.Enabled
-			app.JsonData = cmd.JsonData
-			app.Pinned = cmd.Pinned
-			_, err = sess.Id(app.Id).Update(&app)
-			return err
-		}
-	})
-}

+ 6 - 8
pkg/services/sqlstore/migrations/app_settings.go → pkg/services/sqlstore/migrations/plugin_setting.go

@@ -4,12 +4,12 @@ import . "github.com/grafana/grafana/pkg/services/sqlstore/migrator"
 
 
 func addAppSettingsMigration(mg *Migrator) {
 func addAppSettingsMigration(mg *Migrator) {
 
 
-	appSettingsV2 := Table{
-		Name: "app_settings",
+	pluginSettingTable := Table{
+		Name: "plugin_setting",
 		Columns: []*Column{
 		Columns: []*Column{
 			{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
 			{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
 			{Name: "org_id", Type: DB_BigInt, Nullable: true},
 			{Name: "org_id", Type: DB_BigInt, Nullable: true},
-			{Name: "app_id", Type: DB_NVarchar, Length: 255, Nullable: false},
+			{Name: "plugin_id", Type: DB_NVarchar, Length: 255, Nullable: false},
 			{Name: "enabled", Type: DB_Bool, Nullable: false},
 			{Name: "enabled", Type: DB_Bool, Nullable: false},
 			{Name: "pinned", Type: DB_Bool, Nullable: false},
 			{Name: "pinned", Type: DB_Bool, Nullable: false},
 			{Name: "json_data", Type: DB_Text, Nullable: true},
 			{Name: "json_data", Type: DB_Text, Nullable: true},
@@ -18,14 +18,12 @@ func addAppSettingsMigration(mg *Migrator) {
 			{Name: "updated", Type: DB_DateTime, Nullable: false},
 			{Name: "updated", Type: DB_DateTime, Nullable: false},
 		},
 		},
 		Indices: []*Index{
 		Indices: []*Index{
-			{Cols: []string{"org_id", "app_id"}, Type: UniqueIndex},
+			{Cols: []string{"org_id", "plugin_id"}, Type: UniqueIndex},
 		},
 		},
 	}
 	}
 
 
-	mg.AddMigration("Drop old table app_settings v1", NewDropTableMigration("app_settings"))
-
-	mg.AddMigration("create app_settings table v2", NewAddTableMigration(appSettingsV2))
+	mg.AddMigration("create plugin_setting table", NewAddTableMigration(pluginSettingTable))
 
 
 	//-------  indexes ------------------
 	//-------  indexes ------------------
-	addTableIndicesMigrations(mg, "v3", appSettingsV2)
+	addTableIndicesMigrations(mg, "v1", pluginSettingTable)
 }
 }

+ 70 - 0
pkg/services/sqlstore/plugin_setting.go

@@ -0,0 +1,70 @@
+package sqlstore
+
+import (
+	"time"
+
+	"github.com/grafana/grafana/pkg/bus"
+	m "github.com/grafana/grafana/pkg/models"
+	"github.com/grafana/grafana/pkg/setting"
+	"github.com/grafana/grafana/pkg/util"
+)
+
+func init() {
+	bus.AddHandler("sql", GetPluginSettings)
+	bus.AddHandler("sql", GetPluginSettingById)
+	bus.AddHandler("sql", UpdatePluginSetting)
+}
+
+func GetPluginSettings(query *m.GetPluginSettingsQuery) error {
+	sess := x.Where("org_id=?", query.OrgId)
+
+	query.Result = make([]*m.PluginSetting, 0)
+	return sess.Find(&query.Result)
+}
+
+func GetPluginSettingById(query *m.GetPluginSettingByIdQuery) error {
+	pluginSetting := m.PluginSetting{OrgId: query.OrgId, PluginId: query.PluginId}
+	has, err := x.Get(&pluginSetting)
+	if err != nil {
+		return err
+	} else if has == false {
+		return m.ErrPluginSettingNotFound
+	}
+	query.Result = &pluginSetting
+	return nil
+}
+
+func UpdatePluginSetting(cmd *m.UpdatePluginSettingCmd) error {
+	return inTransaction2(func(sess *session) error {
+		var pluginSetting m.PluginSetting
+
+		exists, err := sess.Where("org_id=? and plugin_id=?", cmd.OrgId, cmd.PluginId).Get(&pluginSetting)
+		sess.UseBool("enabled")
+		sess.UseBool("pinned")
+		if !exists {
+			pluginSetting = m.PluginSetting{
+				PluginId:       cmd.PluginId,
+				OrgId:          cmd.OrgId,
+				Enabled:        cmd.Enabled,
+				Pinned:         cmd.Pinned,
+				JsonData:       cmd.JsonData,
+				SecureJsonData: cmd.GetEncryptedJsonData(),
+				Created:        time.Now(),
+				Updated:        time.Now(),
+			}
+			_, err = sess.Insert(&pluginSetting)
+			return err
+		} else {
+			for key, data := range cmd.SecureJsonData {
+				pluginSetting.SecureJsonData[key] = util.Encrypt([]byte(data), setting.SecretKey)
+			}
+			pluginSetting.SecureJsonData = cmd.GetEncryptedJsonData()
+			pluginSetting.Updated = time.Now()
+			pluginSetting.Enabled = cmd.Enabled
+			pluginSetting.JsonData = cmd.JsonData
+			pluginSetting.Pinned = cmd.Pinned
+			_, err = sess.Id(pluginSetting.Id).Update(&pluginSetting)
+			return err
+		}
+	})
+}

+ 81 - 0
public/app/core/components/colorpicker/colorpicker.ts

@@ -0,0 +1,81 @@
+///<reference path="../../../headers/common.d.ts" />
+
+import config from 'app/core/config';
+import _ from 'lodash';
+import $ from 'jquery';
+import coreModule from 'app/core/core_module';
+
+var template = `
+<div class="graph-legend-popover">
+  <a class="drop-popopver-close" ng-click="ctrl.close();" href="" ng-hide="ctrl.autoClose">
+    <i class="fa fa-times-circle"></i>
+  </a>
+
+  <div ng-show="ctrl.series" class="p-b-1">
+    <label>Y Axis:</label>
+    <button ng-click="ctrl.toggleAxis(yaxis);" class="btn btn-small"
+            ng-class="{'btn-success': ctrl.series.yaxis === 1,
+                       'btn-inverse': ctrl.series.yaxis === 2}">
+      Left
+    </button>
+    <button ng-click="ctrl.toggleAxis(yaxis);"
+      class="btn btn-small"
+      ng-class="{'btn-success': ctrl.series.yaxis === 2,
+                 'btn-inverse': ctrl.series.yaxis === 1}">
+      Right
+    </button>
+  </div>
+
+  <p class="m-b-0">
+   <i ng-repeat="color in ctrl.colors" class="pointer fa fa-circle"
+    ng-style="{color:color}"
+    ng-click="ctrl.colorSelected(color);">&nbsp;</i>
+  </p>
+
+</div>
+`;
+
+export class ColorPickerCtrl {
+  colors: any;
+  autoClose: boolean;
+  series: any;
+  showAxisControls: boolean;
+
+  /** @ngInject */
+  constructor(private $scope, private $rootScope) {
+    this.colors = $rootScope.colors;
+    this.autoClose = $scope.autoClose;
+    this.series = $scope.series;
+  }
+
+  toggleAxis(yaxis) {
+    this.$scope.toggleAxis();
+
+    if (!this.$scope.autoClose) {
+      this.$scope.dismiss();
+    }
+  }
+
+  colorSelected(color) {
+    this.$scope.colorSelected(color);
+    if (!this.$scope.autoClose) {
+      this.$scope.dismiss();
+    }
+  }
+
+  close() {
+    this.$scope.dismiss();
+  }
+}
+
+export function colorPicker() {
+  return {
+    restrict: 'E',
+    controller: ColorPickerCtrl,
+    bindToController: true,
+    controllerAs: 'ctrl',
+    template: template,
+  };
+}
+
+coreModule.directive('gfColorPicker', colorPicker);

+ 1 - 1
public/app/core/components/grafana_app.ts

@@ -77,8 +77,8 @@ export class GrafanaCtrl {
         });
         });
       };
       };
 
 
-      $rootScope.performance.scopeCount = scopes;
       f(root);
       f(root);
+      $rootScope.performance.scopeCount = scopes;
       return count;
       return count;
     };
     };
 
 

+ 55 - 0
public/app/core/components/popover/popover.ts

@@ -0,0 +1,55 @@
+///<reference path="../../../headers/common.d.ts" />
+
+import _ from 'lodash';
+import $ from 'jquery';
+import coreModule from '../../core_module';
+import Drop from 'tether-drop';
+
+export function popoverDirective() {
+  return {
+    restrict: 'E',
+    transclude: true,
+    link: function(scope, elem, attrs, ctrl, transclude) {
+      var inputElem = elem.prev();
+      if (inputElem.length === 0) {
+        console.log('Failed to find input element for popover');
+        return;
+      }
+
+      var offset = attrs.offset || '0 -10px';
+
+      transclude(function(clone, newScope) {
+        var content = document.createElement("div");
+        _.each(clone, (node) => {
+          content.appendChild(node);
+        });
+
+        var drop = new Drop({
+          target: inputElem[0],
+          content: content,
+          position: 'right middle',
+          classes: 'drop-help',
+          openOn: 'click',
+          tetherOptions: {
+            offset: offset
+          }
+        });
+
+      // inputElem.on('focus.popover', function() {
+      //   drop.open();
+      // });
+      //
+      // inputElem.on('blur.popover', function() {
+      //   close();
+      // });
+
+        scope.$on('$destroy', function() {
+          drop.destroy();
+        });
+
+      });
+    }
+  };
+}
+
+coreModule.directive('gfPopover', popoverDirective);

+ 24 - 26
public/app/core/components/sidemenu/sidemenu.html

@@ -1,53 +1,51 @@
 <ul class="sidemenu">
 <ul class="sidemenu">
 
 
-	<li class="sidemenu-org-section" ng-if="ctrl.isSignedIn" class="dropdown">
-		<div class="sidemenu-org">
+	<li class="sidemenu-org-section" ng-if="::ctrl.isSignedIn" class="dropdown">
+		<a class="sidemenu-org" href="profile">
 			<div class="sidemenu-org-avatar">
 			<div class="sidemenu-org-avatar">
-				<img ng-if="ctrl.user.gravatarUrl" ng-src="{{ctrl.user.gravatarUrl}}">
+				<img ng-src="{{::ctrl.user.gravatarUrl}}">
 				<span class="sidemenu-org-avatar--missing">
 				<span class="sidemenu-org-avatar--missing">
 					<i class="fa fa-fw fa-user"></i>
 					<i class="fa fa-fw fa-user"></i>
 				</span>
 				</span>
 			</div>
 			</div>
 			<div class="sidemenu-org-details">
 			<div class="sidemenu-org-details">
-				<span class="sidemenu-org-user sidemenu-item-text">{{ctrl.user.name}}</span>
-				<span class="sidemenu-org-name sidemenu-item-text">{{ctrl.user.orgName}}</span>
+				<span class="sidemenu-org-user sidemenu-item-text">{{::ctrl.user.name}}</span>
+				<span class="sidemenu-org-name sidemenu-item-text">{{::ctrl.user.orgName}}</span>
 			</div>
 			</div>
-		</div>
+		</a>
 		<i class="fa fa-caret-right"></i>
 		<i class="fa fa-caret-right"></i>
 		<ul class="dropdown-menu" role="menu">
 		<ul class="dropdown-menu" role="menu">
-			<li ng-repeat="menuItem in ctrl.orgMenu" ng-class="menuItem.cssClass">
-				<span ng-if="menuItem.section">{{menuItem.section}}</span>
-				<a href="{{menuItem.url}}" ng-if="menuItem.url" target="{{menuItem.target}}">
-					<i class="{{menuItem.icon}}" ng-if="menuItem.icon"></i>
-					{{menuItem.text}}
+			<li ng-repeat="menuItem in ctrl.orgMenu" ng-class="::menuItem.cssClass">
+				<span ng-show="::menuItem.section">{{::menuItem.section}}</span>
+				<a href="{{::menuItem.url}}" ng-show="::menuItem.url" target="{{::menuItem.target}}">
+					<i class="{{::menuItem.icon}}" ng-show="::menuItem.icon"></i>
+					{{::menuItem.text}}
 				</a>
 				</a>
-				<a ng-click="menuItem.click()" ng-if="menuItem.click">
-					<i class="{{menuItem.icon}}"></i>
-					{{menuItem.text}}
+				<a ng-click="menuItem.click()" ng-show="::menuItem.click">
+					<i class="{{::menuItem.icon}}"></i>
+					{{::menuItem.text}}
 				</a>
 				</a>
 			</li>
 			</li>
 		</ul>
 		</ul>
 	</li>
 	</li>
 
 
-	<li ng-repeat="item in ctrl.mainLinks" class="dropdown">
-		<a href="{{item.url}}" class="sidemenu-item sidemenu-main-link" target="{{item.target}}">
+	<li ng-repeat="item in ::ctrl.mainLinks" class="dropdown">
+		<a href="{{::item.url}}" class="sidemenu-item sidemenu-main-link" target="{{::item.target}}">
 			<span class="icon-circle sidemenu-icon">
 			<span class="icon-circle sidemenu-icon">
-				<i class="{{item.icon}}" ng-show="item.icon"></i>
-				<img ng-src="{{item.img}}" ng-show="item.img">
+				<i class="{{::item.icon}}" ng-show="::item.icon"></i>
+				<img ng-src="{{::item.img}}" ng-show="::item.img">
 			</span>
 			</span>
-			<span class="sidemenu-item-text">{{item.text}}</span>
-			<span class="fa fa-caret-right" ng-if="item.children"></span>
+			<span class="sidemenu-item-text">{{::item.text}}</span>
+			<span class="fa fa-caret-right" ng-if="::item.children"></span>
 		</a>
 		</a>
-		<ul class="dropdown-menu" role="menu" ng-if="item.children">
-			<li ng-repeat="child in item.children">
-				<a href="{{child.url}}">
-					{{child.text}}
-				</a>
+		<ul class="dropdown-menu" role="menu" ng-if="::item.children">
+			<li ng-repeat="child in ::item.children" ng-class="{divider: child.divider}">
+				<a href="{{::child.url}}">{{::child.text}}</a>
 			</li>
 			</li>
 		</ul>
 		</ul>
 	</li>
 	</li>
 
 
-	<li ng-if="!ctrl.isSignedIn">
+	<li ng-show="::!ctrl.isSignedIn">
 		<a href="login" class="sidemenu-item" target="_self">
 		<a href="login" class="sidemenu-item" target="_self">
 			<span class="icon-circle sidemenu-icon"><i class="fa fa-fw fa-sign-in"></i></span>
 			<span class="icon-circle sidemenu-icon"><i class="fa fa-fw fa-sign-in"></i></span>
 			<span class="sidemenu-item-text">Sign in</span>
 			<span class="sidemenu-item-text">Sign in</span>

+ 16 - 1
public/app/core/components/sidemenu/sidemenu.ts

@@ -38,7 +38,6 @@ export class SideMenuCtrl {
  openUserDropdown() {
  openUserDropdown() {
    this.orgMenu = [
    this.orgMenu = [
      {section: 'You', cssClass: 'dropdown-menu-title'},
      {section: 'You', cssClass: 'dropdown-menu-title'},
-     {text: 'Preferences', url: this.getUrl('/profile')},
      {text: 'Profile', url: this.getUrl('/profile')},
      {text: 'Profile', url: this.getUrl('/profile')},
    ];
    ];
 
 
@@ -100,6 +99,22 @@ export function sideMenuDirective() {
     bindToController: true,
     bindToController: true,
     controllerAs: 'ctrl',
     controllerAs: 'ctrl',
     scope: {},
     scope: {},
+    link: function(scope, elem) {
+      // hack to hide dropdown menu
+      elem.on('click.dropdown', '.dropdown-menu a', function(evt) {
+        var menu = $(evt.target).parents('.dropdown-menu');
+        var parent = menu.parent();
+        menu.detach();
+
+        setTimeout(function() {
+          parent.append(menu);
+        }, 100);
+      });
+
+      scope.$on("$destory", function() {
+        elem.off('click.dropdown');
+      });
+    }
   };
   };
 }
 }
 
 

+ 12 - 1
public/app/core/core.ts

@@ -24,6 +24,8 @@ import './partials';
 import {grafanaAppDirective} from './components/grafana_app';
 import {grafanaAppDirective} from './components/grafana_app';
 import {sideMenuDirective} from './components/sidemenu/sidemenu';
 import {sideMenuDirective} from './components/sidemenu/sidemenu';
 import {searchDirective} from './components/search/search';
 import {searchDirective} from './components/search/search';
+import {popoverDirective} from './components/popover/popover';
+import {colorPicker} from './components/colorpicker/colorpicker';
 import {navbarDirective} from './components/navbar/navbar';
 import {navbarDirective} from './components/navbar/navbar';
 import {arrayJoin} from './directives/array_join';
 import {arrayJoin} from './directives/array_join';
 import 'app/core/controllers/all';
 import 'app/core/controllers/all';
@@ -32,4 +34,13 @@ import 'app/core/routes/routes';
 import './filters/filters';
 import './filters/filters';
 import coreModule from './core_module';
 import coreModule from './core_module';
 
 
-export {arrayJoin, coreModule, grafanaAppDirective, sideMenuDirective, navbarDirective, searchDirective};
+export {
+  arrayJoin,
+  coreModule,
+  grafanaAppDirective,
+  sideMenuDirective,
+  navbarDirective,
+  searchDirective,
+  colorPicker,
+  popoverDirective
+};

+ 4 - 2
public/app/core/directives/annotation_tooltip.js

@@ -25,7 +25,8 @@ function ($, _, coreModule) {
         var dashboard = dashboardSrv.getCurrent();
         var dashboard = dashboardSrv.getCurrent();
         var time = '<i>' + dashboard.formatDate(event.min) + '</i>';
         var time = '<i>' + dashboard.formatDate(event.min) + '</i>';
 
 
-        var tooltip = '<div class="graph-tooltip small"><div class="graph-tooltip-time">' + title + ' ' + time + '</div> ' ;
+        var tooltip = '<div class="graph-annotation">';
+        tooltip += '<div class="graph-annotation-title">' + title + "</div>";
 
 
         if (event.text) {
         if (event.text) {
           var text = sanitizeString(event.text);
           var text = sanitizeString(event.text);
@@ -42,9 +43,10 @@ function ($, _, coreModule) {
 
 
         if (tags && tags.length) {
         if (tags && tags.length) {
           scope.tags = tags;
           scope.tags = tags;
-          tooltip += '<span class="label label-tag" ng-repeat="tag in tags" tag-color-from-name="tag">{{tag}}</span><br/>';
+          tooltip += '<span class="label label-tag small" ng-repeat="tag in tags" tag-color-from-name="tag">{{tag}}</span><br/>';
         }
         }
 
 
+        tooltip += '<div class="graph-annotation-time">' + time + '</div>' ;
         tooltip += "</div>";
         tooltip += "</div>";
 
 
         var $tooltip = $(tooltip);
         var $tooltip = $(tooltip);

+ 1 - 6
public/app/core/directives/dash_edit_link.js

@@ -73,12 +73,7 @@ function ($, coreModule) {
           };
           };
 
 
           var src = "'" + payload.src + "'";
           var src = "'" + payload.src + "'";
-          var cssClass = payload.cssClass || 'gf-box';
-          var view = $('<div class="' + cssClass + '" ng-include="' + src + '"></div>');
-
-          if (payload.cssClass) {
-            view.addClass(payload.cssClass);
-          }
+          var view = $('<div class="tabbed-view" ng-include="' + src + '"></div>');
 
 
           elem.append(view);
           elem.append(view);
           $compile(elem.contents())(editorScope);
           $compile(elem.contents())(editorScope);

+ 1 - 1
public/app/core/directives/misc.js

@@ -39,7 +39,7 @@ function (angular, coreModule, kbn) {
         var tip = attrs.tip ? (' <tip>' + attrs.tip + '</tip>') : '';
         var tip = attrs.tip ? (' <tip>' + attrs.tip + '</tip>') : '';
         var showIf = attrs.showIf ? (' ng-show="' + attrs.showIf + '" ') : '';
         var showIf = attrs.showIf ? (' ng-show="' + attrs.showIf + '" ') : '';
 
 
-        var template = '<div class="editor-option text-center"' + showIf + '>' +
+        var template = '<div class="editor-option gf-form-checkbox text-center"' + showIf + '>' +
           ' <label for="' + attrs.model + '" class="small">' +
           ' <label for="' + attrs.model + '" class="small">' +
           attrs.text + tip + '</label>' +
           attrs.text + tip + '</label>' +
           '<input class="cr1" id="' + attrs.model + '" type="checkbox" ' +
           '<input class="cr1" id="' + attrs.model + '" type="checkbox" ' +

+ 5 - 5
public/app/core/directives/plugin_component.ts

@@ -160,13 +160,13 @@ function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $
       }
       }
       // AppConfigCtrl
       // AppConfigCtrl
       case 'app-config-ctrl': {
       case 'app-config-ctrl': {
-        let appModel = scope.ctrl.appModel;
-        return System.import(appModel.module).then(function(appModule) {
+        let model = scope.ctrl.model;
+        return System.import(model.module).then(function(appModule) {
           return {
           return {
-            baseUrl: appModel.baseUrl,
-            name: 'app-config-' + appModel.appId,
+            baseUrl: model.baseUrl,
+            name: 'app-config-' + model.pluginId,
             bindings: {appModel: "=", appEditCtrl: "="},
             bindings: {appModel: "=", appEditCtrl: "="},
-            attrs: {"app-model": "ctrl.appModel", "app-edit-ctrl": "ctrl"},
+            attrs: {"app-model": "ctrl.model", "app-edit-ctrl": "ctrl"},
             Component: appModule.ConfigCtrl,
             Component: appModule.ConfigCtrl,
           };
           };
         });
         });

+ 12 - 12
public/app/core/routes/routes.ts

@@ -11,7 +11,7 @@ function setupAngularRoutes($routeProvider, $locationProvider) {
   $locationProvider.html5Mode(true);
   $locationProvider.html5Mode(true);
 
 
   var loadOrgBundle = new BundleLoader('app/features/org/all');
   var loadOrgBundle = new BundleLoader('app/features/org/all');
-  var loadAppsBundle = new BundleLoader('app/features/apps/all');
+  var loadPluginsBundle = new BundleLoader('app/features/plugins/all');
   var loadAdminBundle = new BundleLoader('app/features/admin/admin');
   var loadAdminBundle = new BundleLoader('app/features/admin/admin');
 
 
   $routeProvider
   $routeProvider
@@ -165,23 +165,23 @@ function setupAngularRoutes($routeProvider, $locationProvider) {
     controller : 'SnapshotsCtrl',
     controller : 'SnapshotsCtrl',
     controllerAs: 'ctrl',
     controllerAs: 'ctrl',
   })
   })
-  .when('/apps', {
-    templateUrl: 'public/app/features/apps/partials/list.html',
-    controller: 'AppListCtrl',
+  .when('/plugins', {
+    templateUrl: 'public/app/features/plugins/partials/list.html',
+    controller: 'PluginListCtrl',
     controllerAs: 'ctrl',
     controllerAs: 'ctrl',
-    resolve: loadAppsBundle,
+    resolve: loadPluginsBundle,
   })
   })
-  .when('/apps/:appId/edit', {
-    templateUrl: 'public/app/features/apps/partials/edit.html',
-    controller: 'AppEditCtrl',
+  .when('/plugins/:pluginId/edit', {
+    templateUrl: 'public/app/features/plugins/partials/edit.html',
+    controller: 'PluginEditCtrl',
     controllerAs: 'ctrl',
     controllerAs: 'ctrl',
-    resolve: loadAppsBundle,
+    resolve: loadPluginsBundle,
   })
   })
-  .when('/apps/:appId/page/:slug', {
-    templateUrl: 'public/app/features/apps/partials/page.html',
+  .when('/plugins/:pluginId/page/:slug', {
+    templateUrl: 'public/app/features/plugins/partials/page.html',
     controller: 'AppPageCtrl',
     controller: 'AppPageCtrl',
     controllerAs: 'ctrl',
     controllerAs: 'ctrl',
-    resolve: loadAppsBundle,
+    resolve: loadPluginsBundle,
   })
   })
   .when('/global-alerts', {
   .when('/global-alerts', {
     templateUrl: 'public/app/features/dashboard/partials/globalAlerts.html',
     templateUrl: 'public/app/features/dashboard/partials/globalAlerts.html',

+ 0 - 60
public/app/core/services/popover_srv.js

@@ -1,60 +0,0 @@
-define([
-  'angular',
-  'lodash',
-  'jquery',
-  '../core_module',
-],
-function (angular, _, $, coreModule) {
-  'use strict';
-
-  coreModule.default.service('popoverSrv', function($templateCache, $timeout, $q, $http, $compile) {
-
-    this.getTemplate = function(url) {
-      return $q.when($templateCache.get(url) || $http.get(url, {cache: true}));
-    };
-
-    this.show = function(options) {
-      var popover;
-
-      // hide other popovers
-      $('.popover').each(function() {
-        popover = $(this).prev().data('popover');
-        if (popover) {
-          popover.scope.$destroy();
-          popover.destroy();
-        }
-      });
-
-      options.scope.dismiss = function() {
-        popover = options.element.data('popover');
-        if (popover) {
-          popover.destroy();
-        }
-        options.scope.$destroy();
-      };
-
-      this.getTemplate(options.templateUrl).then(function(result) {
-        $timeout(function() {
-          var template = _.isString(result) ? result : result.data;
-
-          options.element.popover({
-            content: template,
-            placement: options.placement || 'bottom',
-            html: true
-          });
-
-          popover = options.element.data('popover');
-          popover.hasContent = function () {
-            return template;
-          };
-
-          popover.toggle();
-          popover.scope = options.scope;
-          $compile(popover.$tip)(popover.scope);
-        }, 1);
-      });
-    };
-
-  });
-
-});

+ 55 - 0
public/app/core/services/popover_srv.ts

@@ -0,0 +1,55 @@
+///<reference path="../../headers/common.d.ts" />
+
+import config from 'app/core/config';
+import _ from 'lodash';
+import $ from 'jquery';
+import coreModule from 'app/core/core_module';
+import Drop from 'tether-drop';
+
+/** @ngInject **/
+function popoverSrv($compile, $rootScope) {
+
+  this.show = function(options) {
+    var popoverScope = _.extend($rootScope.$new(true), options.model);
+    var drop;
+
+    function destroyDrop() {
+      setTimeout(function() {
+        if (drop.tether) {
+          drop.destroy();
+        }
+      });
+    }
+
+    popoverScope.dismiss = function() {
+      popoverScope.$destroy();
+      destroyDrop();
+    };
+
+    var contentElement = document.createElement('div');
+    contentElement.innerHTML = options.template;
+
+    $compile(contentElement)(popoverScope);
+
+    drop = new Drop({
+      target: options.element,
+      content: contentElement,
+      position: options.position,
+      classes: 'drop-popover',
+      openOn: options.openOn || 'hover',
+      hoverCloseDelay: 200,
+      tetherOptions: {
+        constraints: [{to: 'window', pin: true, attachment: "both"}]
+      }
+    });
+
+    drop.on('close', () => {
+      popoverScope.dismiss({fromDropClose: true});
+      destroyDrop();
+    });
+
+    drop.open();
+  };
+}
+
+coreModule.service('popoverSrv', popoverSrv);

+ 45 - 18
public/app/core/utils/kbn.js

@@ -12,39 +12,66 @@ function($, _) {
 
 
   kbn.round_interval = function(interval) {
   kbn.round_interval = function(interval) {
     switch (true) {
     switch (true) {
-    // 0.5s
-    case (interval <= 500):
+    // 0.3s
+    case (interval <= 300):
       return 100;       // 0.1s
       return 100;       // 0.1s
-    // 5s
-    case (interval <= 5000):
+    // 0.75s
+    case (interval <= 750):
+      return 500;       // 0.5s
+    // 1.5s
+    case (interval <= 1500):
       return 1000;      // 1s
       return 1000;      // 1s
+    // 3.5s
+    case (interval <= 3500):
+      return 2000;      // 2s
     // 7.5s
     // 7.5s
     case (interval <= 7500):
     case (interval <= 7500):
       return 5000;      // 5s
       return 5000;      // 5s
-    // 15s
-    case (interval <= 15000):
+    // 12.5s
+    case (interval <= 12500):
       return 10000;     // 10s
       return 10000;     // 10s
+    // 17.5s
+    case (interval <= 17500):
+      return 15000;     // 15s
+    // 25s
+    case (interval <= 25000):
+      return 20000;     // 20s
     // 45s
     // 45s
     case (interval <= 45000):
     case (interval <= 45000):
       return 30000;     // 30s
       return 30000;     // 30s
-    // 3m
-    case (interval <= 180000):
+    // 1.5m
+    case (interval <= 90000):
       return 60000;     // 1m
       return 60000;     // 1m
-    // 9m
+    // 3.5m
+    case (interval <= 210000):
+      return 120000;    // 2m
+    // 7.5m
     case (interval <= 450000):
     case (interval <= 450000):
       return 300000;    // 5m
       return 300000;    // 5m
-    // 20m
-    case (interval <= 1200000):
+    // 12.5m
+    case (interval <= 750000):
       return 600000;    // 10m
       return 600000;    // 10m
+    // 12.5m
+    case (interval <= 1050000):
+      return 900000;    // 15m
+    // 25m
+    case (interval <= 1500000):
+      return 1200000;   // 20m
     // 45m
     // 45m
     case (interval <= 2700000):
     case (interval <= 2700000):
       return 1800000;   // 30m
       return 1800000;   // 30m
-    // 2h
-    case (interval <= 7200000):
+    // 1.5h
+    case (interval <= 5400000):
       return 3600000;   // 1h
       return 3600000;   // 1h
-    // 6h
-    case (interval <= 21600000):
+    // 2.5h
+    case (interval <= 9000000):
+      return 7200000;   // 2h
+    // 4.5h
+    case (interval <= 16200000):
       return 10800000;  // 3h
       return 10800000;  // 3h
+    // 9h
+    case (interval <= 32400000):
+      return 21600000;  // 6h
     // 24h
     // 24h
     case (interval <= 86400000):
     case (interval <= 86400000):
       return 43200000;  // 12h
       return 43200000;  // 12h
@@ -593,12 +620,12 @@ function($, _) {
           {text: 'packets/sec', value: 'pps'},
           {text: 'packets/sec', value: 'pps'},
           {text: 'bits/sec',    value: 'bps'},
           {text: 'bits/sec',    value: 'bps'},
           {text: 'bytes/sec',   value: 'Bps'},
           {text: 'bytes/sec',   value: 'Bps'},
-          {text: 'kilobites/sec', value: 'Kbits'},
+          {text: 'kilobits/sec', value: 'Kbits'},
           {text: 'kilobytes/sec',    value: 'KBs'},
           {text: 'kilobytes/sec',    value: 'KBs'},
-          {text: 'megabites/sec', value: 'Mbits'},
+          {text: 'megabits/sec', value: 'Mbits'},
           {text: 'megabytes/sec',    value: 'MBs'},
           {text: 'megabytes/sec',    value: 'MBs'},
           {text: 'gigabytes/sec',   value: 'GBs'},
           {text: 'gigabytes/sec',   value: 'GBs'},
-          {text: 'gigabites/sec',   value: 'Gbits'},
+          {text: 'gigabits/sec',   value: 'Gbits'},
         ]
         ]
       },
       },
       {
       {

+ 1 - 1
public/app/features/admin/partials/admin_home.html

@@ -1,4 +1,4 @@
-<navbar icon="fa fa-fw fa-cogs" title="Admin">
+<navbar icon="fa fa-fw fa-cogs" title="Admin" title-url="admin">
 </navbar>
 </navbar>
 
 
 <div class="page-container">
 <div class="page-container">

+ 36 - 47
public/app/features/admin/partials/edit_org.html

@@ -1,58 +1,47 @@
 <navbar icon="fa fa-fw fa-cogs" title="Admin" title-url="admin">
 <navbar icon="fa fa-fw fa-cogs" title="Admin" title-url="admin">
-	<nav-button title="Orgs" title-url="admin/orgs" icon="icon-gf icon-gf-users"></nav-button>
+	<a href="admin/orgs" class="navbar-page-btn">
+		<i class="icon-gf icon-gf-users"></i>
+		Orgs
+	</a>
 </navbar>
 </navbar>
 
 
 <div class="page-container">
 <div class="page-container">
 	<div class="page-header">
 	<div class="page-header">
-		<h1>
-			Edit Organization
-		</h1>
+		<h1>Edit Organization</h1>
 	</div>
 	</div>
 
 
-		<form name="orgDetailsForm">
-			<div>
-				<div class="tight-form">
-					<ul class="tight-form-list">
-						<li class="tight-form-item" style="width: 100px">
-							Name
-						</li>
-						<li>
-							<input type="text" required ng-model="org.name" class="input-xxlarge tight-form-input last" >
-						</li>
-					</ul>
-					<div class="clearfix"></div>
-				</div>
-			</div>
+	<form name="orgDetailsForm" class="gf-form-group">
+		<div class="gf-form">
+			<span class="gf-form-label width-10">Name</span>
+			<input type="text" required ng-model="org.name" class="gf-form-input max-width-14" >
+		</div>
 
 
-			<br>
-			<button type="submit" class="pull-right btn btn-success" ng-click="update()" ng-show="!createMode">Update</button>
-		</form>
+		<div class="gf-form-button-row">
+			<button type="submit" class="btn btn-success" ng-click="update()" ng-show="!createMode">Update</button>
+		</div>
+	</form>
 
 
-		<h3>
-			Organization Users
-		</h3>
+	<h3 class="page-heading">Organization Users</h3>
 
 
-		<table class="grafana-options-table form-inline">
-			<tr>
-				<th>Username</th>
-				<th>Email</th>
-				<th>Role</th>
-				<th></th>
-			</tr>
-			<tr ng-repeat="orgUser in orgUsers">
-				<td>{{orgUser.login}}</td>
-				<td>{{orgUser.email}}</td>
-				<td>
-					<select type="text" ng-model="orgUser.role" class="input-small" ng-options="f for f in ['Viewer', 'Editor', 'Read Only Editor', 'Admin']" ng-change="updateOrgUser(orgUser)">
-					</select>
-				</td>
-				<td style="width: 1%">
-					<a ng-click="removeOrgUser(orgUser)" class="btn btn-danger btn-mini">
-						<i class="fa fa-remove"></i>
-					</a>
-				</td>
-			</tr>
-		</table>
-
-	</div>
+	<table class="grafana-options-table">
+		<tr>
+			<th>Username</th>
+			<th>Email</th>
+			<th>Role</th>
+			<th></th>
+		</tr>
+		<tr ng-repeat="orgUser in orgUsers">
+			<td>{{orgUser.login}}</td>
+			<td>{{orgUser.email}}</td>
+			<td>
+				<select type="text" ng-model="orgUser.role" class="gf-form-input max-width-8" ng-options="f for f in ['Viewer', 'Editor', 'Read Only Editor', 'Admin']" ng-change="updateOrgUser(orgUser)">
+				</select>
+			</td>
+			<td style="width: 1%">
+				<a ng-click="removeOrgUser(orgUser)" class="btn btn-danger btn-mini">
+					<i class="fa fa-remove"></i>
+				</a>
+			</td>
+		</tr>
+	</table>
 </div>
 </div>

+ 51 - 102
public/app/features/admin/partials/edit_user.html

@@ -1,129 +1,78 @@
 <navbar icon="fa fa-fw fa-cogs" title="Admin" title-url="admin">
 <navbar icon="fa fa-fw fa-cogs" title="Admin" title-url="admin">
-	<nav-button title="Users" title-url="admin/users" icon="icon-gf icon-gf-users"></nav-button>
+	<a href="admin/users" class="navbar-page-btn">
+		<i class="icon-gf icon-gf-users"></i>
+		Users
+	</a>
 </navbar>
 </navbar>
 
 
 <div class="page-container">
 <div class="page-container">
 	<div class="page-header">
 	<div class="page-header">
-		<h1>
-			Edit User
-		</h1>
+		<h1>Edit User</h1>
 	</div>
 	</div>
 
 
-	<form name="userForm">
-		<div>
-			<div class="tight-form">
-				<ul class="tight-form-list">
-					<li class="tight-form-item" style="width: 100px">
-						Name
-					</li>
-					<li>
-						<input type="text" required ng-model="user.name" class="input-xxlarge tight-form-input last" >
-					</li>
-				</ul>
-				<div class="clearfix"></div>
+	<form name="userForm" class="gf-form-group">
+			<div class="gf-form">
+				<span class="gf-form-label width-10">Name</span>
+				<input type="text" required ng-model="user.name" class="gf-form-input max-width-25" >
 			</div>
 			</div>
-			<div class="tight-form">
-				<ul class="tight-form-list">
-					<li class="tight-form-item" style="width: 100px">
-						Email
-					</li>
-					<li>
-						<input type="email" ng-model="user.email" class="input-xxlarge tight-form-input last" >
-					</li>
-				</ul>
-				<div class="clearfix"></div>
+			<div class="gf-form">
+				<span class="gf-form-label width-10">Email</span>
+				<input type="email" ng-model="user.email" class="gf-form-input max-width-25" >
 			</div>
 			</div>
-			<div class="tight-form">
-				<ul class="tight-form-list">
-					<li class="tight-form-item" style="width: 100px">
-						Username
-					</li>
-					<li>
-						<input type="text" ng-model="user.login" class="input-xxlarge tight-form-input last" >
-					</li>
-				</ul>
-				<div class="clearfix"></div>
+			<div class="gf-form">
+				<span class="gf-form-label width-10">Username</span>
+				<input type="text" ng-model="user.login" class="gf-form-input max-width-25" >
 			</div>
 			</div>
-		</div>
 
 
-		<br>
-		<button type="submit" class="pull-right btn btn-success" ng-click="update()" ng-show="!createMode">Update</button>
+			<div class="gf-form-button-row">
+				<button type="submit" class="btn btn-success" ng-click="update()" ng-show="!createMode">Update</button>
+			</div>
 	</form>
 	</form>
 
 
-	<h3>
-		Change password
-	</h3>
+	<h3 class="page-heading">Change password</h3>
 
 
-	<form name="passwordForm">
-		<div>
-			<div class="tight-form">
-				<ul class="tight-form-list">
-					<li class="tight-form-item" style="width: 100px">
-						New password
-					</li>
-					<li>
-						<input type="password" required ng-minlength="4" ng-model="password" class="input-xxlarge tight-form-input last">
-					</li>
-				</ul>
-				<div class="clearfix"></div>
-			</div>
+	<form name="passwordForm" class="gf-form-group">
+		<div class="gf-form">
+				<span class="gf-form-label width-10">New password</span>
+				<input type="password" required ng-minlength="4" ng-model="password" class="gf-form-input max-width-25">
 		</div>
 		</div>
 
 
-		<br>
-		<button type="submit" class="pull-right btn btn-success" ng-click="setPassword()">Update</button>
+		<div class="gf-form-button-row">
+			<button type="submit" class="btn btn-success" ng-click="setPassword()">Update</button>
+		</div>
 	</form>
 	</form>
 
 
-	<h3>
-		Permissions
-	</h3>
+	<h3 class="page-heading">Permissions</h3>
 
 
-	<div>
-		<div class="tight-form last">
-			<ul class="tight-form-list">
-				<li class="tight-form-item last">
-					Grafana Admin&nbsp;
-					<input class="cr1" id="permissions.isGrafanaAdmin" type="checkbox"
-					ng-model="permissions.isGrafanaAdmin" ng-checked="permissions.isGrafanaAdmin">
-					<label for="permissions.isGrafanaAdmin" class="cr1"></label>
-				</li>
-			</ul>
-			<div class="clearfix"></div>
+	<form name="passwordForm" class="gf-form-group">
+		<div class="gf-form" >
+			<editor-checkbox text="Grafana Admin" model="permissions.isGrafanaAdmin" style="line-height: 1.5rem;"></editor-checkbox>
 		</div>
 		</div>
-		<br>
-		<button type="submit" class="pull-right btn btn-success" ng-click="updatePermissions()">Update</button>
-		<br>
-	</div>
 
 
-	<h3>
-		Organizations
-	</h3>
+		<div class="gf-form-button-row">
+			<button type="submit" class="btn btn-success" ng-click="updatePermissions()">Update</button>
+		</div>
+	</form>
+
+	<h3 class="page-heading">Organizations</h3>
 
 
-	<form name="addOrgForm">
-		<div class="tight-form">
-			<ul class="tight-form-list">
-				<li class="tight-form-item" style="width: 160px">
-					Add organization
-				</li>
-				<li>
-					<input type="text" ng-model="newOrg.name" bs-typeahead="searchOrgs"
-					required class="input-xlarge tight-form-input" placeholder="organization name">
-				</li>
-				<li class="tight-form-item">
-					Role
-				</li>
-				<li>
-					<select type="text" ng-model="newOrg.role" class="input-small tight-form-input" ng-options="f for f in ['Viewer', 'Editor', 'Read Only Editor', 'Admin']">
-					</select>
-				</li>
-				<li>
-					<button class="btn btn-success tight-form-btn" ng-click="addOrgUser()">Add</button>
-				</li>
-				<div class="clearfix"></div>
-			</ul>
+	<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>
+				<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">
+				<span class="gf-form-label">Role</span>
+				<select type="text" ng-model="newOrg.role" class="gf-form-input width-10" ng-options="f for f in ['Viewer', 'Editor', 'Read Only Editor', 'Admin']"></select>
+			</div>
+			<div class="gf-form">
+				<button class="btn btn-success gf-form-btn" ng-click="addOrgUser()">Add</button>
+			</div>
 		</div>
 		</div>
 	</form>
 	</form>
 
 
-	<table class="grafana-options-table form-inline">
+	<table class="grafana-options-table">
 		<tr>
 		<tr>
 			<th>Name</th>
 			<th>Name</th>
 			<th>Role</th>
 			<th>Role</th>
@@ -134,7 +83,7 @@
 				{{org.name}} <span class="label label-info" ng-show="org.orgId === user.orgId">Current</span>
 				{{org.name}} <span class="label label-info" ng-show="org.orgId === user.orgId">Current</span>
 			</td>
 			</td>
 			<td>
 			<td>
-				<select type="text" ng-model="org.role" class="input-small" ng-options="f for f in ['Viewer', 'Editor', 'Read Only Editor', 'Admin']" ng-change="updateOrgUser(org)">
+				<select type="text" ng-model="org.role" class="gf-form-input max-width-12" ng-options="f for f in ['Viewer', 'Editor', 'Read Only Editor', 'Admin']" ng-change="updateOrgUser(org)">
 				</select>
 				</select>
 			</td>
 			</td>
 			<td style="width: 1%">
 			<td style="width: 1%">

+ 24 - 52
public/app/features/admin/partials/new_user.html

@@ -1,63 +1,35 @@
 <navbar icon="fa fa-fw fa-cogs" title="Admin" title-url="admin">
 <navbar icon="fa fa-fw fa-cogs" title="Admin" title-url="admin">
-	<nav-button title="Users" title-url="admin/users" icon="icon-gf icon-gf-users"></nav-button>
+	<a href="admin/users" class="navbar-page-btn">
+		<i class="icon-gf icon-gf-users"></i>
+		Users
+	</a>
 </navbar>
 </navbar>
 
 
 <div class="page-container">
 <div class="page-container">
 	<div class="page-header">
 	<div class="page-header">
-		<h1>
-			Add new user
-		</h1>
+		<h1>Add new user</h1>
 	</div>
 	</div>
 
 
-	<form name="userForm">
-		<div>
-			<div class="tight-form">
-				<ul class="tight-form-list">
-					<li class="tight-form-item" style="width: 100px">
-						<strong>Name</strong>
-					</li>
-					<li>
-						<input type="text" required ng-model="user.name" class="input-xxlarge tight-form-input last" >
-					</li>
-				</ul>
-				<div class="clearfix"></div>
-			</div>
-			<div class="tight-form">
-				<ul class="tight-form-list">
-					<li class="tight-form-item" style="width: 100px">
-						<strong>Email</strong>
-					</li>
-					<li>
-						<input type="email" ng-model="user.email" class="input-xxlarge tight-form-input last" >
-					</li>
-				</ul>
-				<div class="clearfix"></div>
-			</div>
-			<div class="tight-form">
-				<ul class="tight-form-list">
-					<li class="tight-form-item" style="width: 100px">
-						<strong>Username</strong>
-					</li>
-					<li>
-						<input type="text" ng-model="user.login" class="input-xxlarge tight-form-input last" >
-					</li>
-				</ul>
-				<div class="clearfix"></div>
-			</div>
-			<div class="tight-form">
-				<ul class="tight-form-list">
-					<li class="tight-form-item" style="width: 100px">
-						<strong>Password</strong>
-					</li>
-					<li>
-						<input type="password" required ng-model="user.password" class="input-xxlarge tight-form-input last" >
-					</li>
-				</ul>
-				<div class="clearfix"></div>
-			</div>
+	<form name="userForm" class="gf-form-group">
+		<div class="gf-form">
+			<span class="gf-form-label width-10">Name</span>
+			<input type="text" required ng-model="user.name" class="gf-form-input max-width-20" >
+		</div>
+		<div class="gf-form">
+			<span class="gf-form-label width-10">Email</span>
+			<input type="email" ng-model="user.email" class="gf-form-input max-width-20" >
+		</div>
+		<div class="gf-form">
+			<span class="gf-form-label width-10">Username</span>
+			<input type="text" ng-model="user.login" class="gf-form-input max-width-20" >
+		</div>
+		<div class="gf-form">
+			<span class="gf-form-label width-10">Password</span>
+			<input type="password" required ng-model="user.password" class="gf-form-input max-width-20" >
 		</div>
 		</div>
 
 
-		<br>
-		<button type="submit" class="pull-right btn btn-success" ng-click="create()">Create</button>
+		<div class="gf-form-button-row">
+			<button type="submit" class="btn btn-success" ng-click="create()">Create</button>
+		</div>
 	</form>
 	</form>
 </div>
 </div>

+ 5 - 4
public/app/features/admin/partials/orgs.html

@@ -1,12 +1,13 @@
 <navbar icon="fa fa-fw fa-cogs" title="Admin" title-url="admin">
 <navbar icon="fa fa-fw fa-cogs" title="Admin" title-url="admin">
-	<nav-button title="Orgs" title-url="admin/orgs" icon="icon-gf icon-gf-users"></nav-button>
+	<a href="admin/orgs" class="navbar-page-btn">
+		<i class="icon-gf icon-gf-users"></i>
+		Orgs
+	</a>
 </navbar>
 </navbar>
 
 
 <div class="page-container">
 <div class="page-container">
 	<div class="page-header">
 	<div class="page-header">
-		<h1>
-			Organizations
-		</h1>
+		<h1>Organizations</h1>
 	</div>
 	</div>
 
 
 	<table class="filter-table form-inline">
 	<table class="filter-table form-inline">

+ 1 - 3
public/app/features/admin/partials/settings.html

@@ -3,9 +3,7 @@
 
 
 <div class="page-container">
 <div class="page-container">
 	<div class="page-header">
 	<div class="page-header">
-		<h1>
-			Server settings
-		</h1>
+		<h1>Server settings</h1>
 	</div>
 	</div>
 
 
 		<div class="grafana-info-box span8" style="margin: 20px 0 25px 0">
 		<div class="grafana-info-box span8" style="margin: 20px 0 25px 0">

+ 1 - 3
public/app/features/admin/partials/stats.html

@@ -3,9 +3,7 @@
 
 
 <div class="page-container">
 <div class="page-container">
 	<div class="page-header">
 	<div class="page-header">
-		<h1>
-			Stats
-		</h1>
+		<h1>Stats</h1>
 	</div>
 	</div>
 
 
 	<table class="filter-table form-inline">
 	<table class="filter-table form-inline">

+ 4 - 1
public/app/features/admin/partials/users.html

@@ -1,5 +1,8 @@
 <navbar icon="fa fa-fw fa-cogs" title="Admin" title-url="admin">
 <navbar icon="fa fa-fw fa-cogs" title="Admin" title-url="admin">
-	<nav-button title="Users" title-url="admin/users" icon="icon-gf icon-gf-users"></nav-button>
+	<a href="admin/users" class="navbar-page-btn">
+		<i class="icon-gf icon-gf-users"></i>
+		Users
+	</a>
 </navbar>
 </navbar>
 
 
 <div class="page-container">
 <div class="page-container">

+ 76 - 83
public/app/features/annotations/partials/editor.html

@@ -1,106 +1,99 @@
 <div ng-controller="AnnotationsEditorCtrl" ng-init="init()">
 <div ng-controller="AnnotationsEditorCtrl" ng-init="init()">
-
-	<div class="gf-box-header">
-		<div class="gf-box-title">
-			<i class="fa fa-bolt"></i>
+	<div class="tabbed-view-header">
+		<h2 class="tabbed-view-title">
 			Annotations
 			Annotations
-		</div>
-
-		<div class="tabs">
-			<ul class="nav nav-tabs">
-				<li ng-class="{active: mode === 'list'}">
-					<a ng-click="mode = 'list';">
-						List
-					</a>
-				</li>
+		</h2>
 
 
-				<li ng-class="{active: mode === 'edit'}" ng-show="mode === 'edit'">
-					<a>
-						{{currentAnnotation.name}}
-					</a>
-				</li>
-
-				<li ng-class="{active: mode === 'new'}">
-					<a ng-click="mode = 'new';">
-						<i class="fa fa-plus"></i>
-						New
-					</a>
-				</li>
-			</ul>
-		</div>
+		<ul class="gf-tabs">
+			<li class="gf-tabs-item" >
+				<a class="gf-tabs-link" ng-click="mode = 'list';" ng-class="{active: mode === 'list'}">
+					List
+				</a>
+			</li>
+			<li class="gf-tabs-item" ng-show="mode === 'edit'">
+				<a class="gf-tabs-link" ng-class="{active: mode === 'edit'}">
+					{{currentAnnotation.name}}
+				</a>
+			</li>
+			<li class="gf-tabs-item" ng-show="mode === 'new'">
+				<span class="active gf-tabs-link">New</span>
+			</li>
+		</ul>
 
 
-		<button class="gf-box-header-close-btn" ng-click="dismiss();">
+		<button class="tabbed-view-close-btn" ng-click="dismiss();">
 			<i class="fa fa-remove"></i>
 			<i class="fa fa-remove"></i>
 		</button>
 		</button>
 	</div>
 	</div>
-	<div class="gf-box-body">
-
 
 
+	<div class="tabbed-view-body">
 		<div class="editor-row row" ng-if="mode === 'list'">
 		<div class="editor-row row" ng-if="mode === 'list'">
-			<div class="span6">
-				<div ng-if="annotations.length === 0">
-					<em>No annotations defined</em>
-				</div>
-				<table class="grafana-options-table">
-					<tr ng-repeat="annotation in annotations">
-						<td style="width:90%">
-							<i class="fa fa-bolt" style="color:{{annotation.iconColor}}"></i> &nbsp;
-							{{annotation.name}}
-						</td>
-						<td style="width: 1%"><i ng-click="_.move(annotations,$index,$index-1)" ng-hide="$first" class="pointer fa fa-arrow-up"></i></td>
-						<td style="width: 1%"><i ng-click="_.move(annotations,$index,$index+1)" ng-hide="$last" class="pointer fa fa-arrow-down"></i></td>
+			<div ng-if="annotations.length === 0">
+				<em>No annotations defined</em>
+			</div>
+			<table class="grafana-options-table">
+				<tr ng-repeat="annotation in annotations">
+					<td style="width:90%">
+						<i class="fa fa-bolt" style="color:{{annotation.iconColor}}"></i> &nbsp;
+						{{annotation.name}}
+					</td>
+					<td style="width: 1%"><i ng-click="_.move(annotations,$index,$index-1)" ng-hide="$first" class="pointer fa fa-arrow-up"></i></td>
+					<td style="width: 1%"><i ng-click="_.move(annotations,$index,$index+1)" ng-hide="$last" class="pointer fa fa-arrow-down"></i></td>
 
 
-						<td style="width: 1%" class="nobg">
-							<a ng-click="edit(annotation)" class="btn btn-inverse btn-mini">
-								<i class="fa fa-edit"></i>
-								Edit
-							</a>
-						</td>
-						<td style="width: 1%" class="nobg">
-							<a ng-click="removeAnnotation(annotation)" class="btn btn-danger btn-mini">
-								<i class="fa fa-remove"></i>
-							</a>
-						</td>
-					</tr>
-				</table>
+					<td style="width: 1%">
+						<a ng-click="edit(annotation)" class="btn btn-inverse btn-mini">
+							<i class="fa fa-edit"></i>
+							Edit
+						</a>
+					</td>
+					<td style="width: 1%">
+						<a ng-click="removeAnnotation(annotation)" class="btn btn-danger btn-mini">
+							<i class="fa fa-remove"></i>
+						</a>
+					</td>
+				</tr>
+			</table>
+		</div>
+
+		<div class="gf-form" ng-show="mode === 'list'">
+			<div class="gf-form-button-row">
+				<a type="button" class="btn gf-form-button btn-success" ng-click="mode = 'new';"><i class="fa fa-plus" ></i>&nbsp;&nbsp;New</a>
 			</div>
 			</div>
 		</div>
 		</div>
 
 
-		<div ng-if="mode === 'edit' || mode === 'new'">
-			<div class="editor-row">
-				<div class="editor-option">
-					<label class="small">Name</label>
-					<input type="text" class="input-medium" ng-model='currentAnnotation.name' placeholder="name"></input>
-				</div>
-				<div class="editor-option">
-					<label class="small">Datasource</label>
-					<select ng-model="currentAnnotation.datasource" ng-options="f.name as f.name for f in datasources" ng-change="datasourceChanged()"></select>
-				</div>
-				<div class="editor-option text-center">
-					<label class="small">Icon color</label>
-					<spectrum-picker ng-model="currentAnnotation.iconColor"></spectrum-picker>
-				</div>
-				<div class="editor-option">
-					<label class="small">Icon size</label>
-					<select class="input-mini" ng-model="currentAnnotation.iconSize" ng-options="f for f in [7,8,9,10,13,15,17,20,25,30]"></select>
-				</div>
-				<editor-opt-bool text="Grid line" model="currentAnnotation.showLine"></editor-opt-bool>
-				<div class="editor-option text-center">
-					<label class="small">Line color</label>
-					<spectrum-picker ng-model="currentAnnotation.lineColor"></spectrum-picker>
+		<div class="annotations-basic-settings" ng-if="mode === 'edit' || mode === 'new'">
+			<div class="gf-form-group">
+				<div class="gf-form-inline">
+					<div class="gf-form gf-size-max-xxl">
+						<span class="gf-form-label">Name</span>
+						<input type="text" class="gf-form-input" ng-model='currentAnnotation.name' placeholder="name"></input>
+					</div>
+					<div class="gf-form">
+						<span class="gf-form-label max-width-10">Datasource</span>
+						<div class="gf-form-select-wrapper">
+							<select class="gf-form-input gf-size-auto" ng-model="currentAnnotation.datasource" ng-options="f.name as f.name for f in datasources" ng-change="datasourceChanged()"></select>
+						</div>
+					</div>
+					<div class="gf-form">
+						<label class="gf-form-label">
+							<span>Color</span>
+							<spectrum-picker ng-model="currentAnnotation.iconColor"></spectrum-picker>
+						</label>
+					</div>
 				</div>
 				</div>
 			</div>
 			</div>
 
 
-			<rebuild-on-change property="currentAnnotation.datasource">
+			<rebuild-on-change property="currentDatasource">
 				<plugin-component type="annotations-query-ctrl">
 				<plugin-component type="annotations-query-ctrl">
 				</plugin-component>
 				</plugin-component>
 			</rebuild-on-change>
 			</rebuild-on-change>
 
 
-			<br>
-			<button ng-show="mode === 'new'" type="button" class="btn btn-success" ng-click="add()">Add</button>
-			<button ng-show="mode === 'edit'" type="button" class="btn btn-success pull-left" ng-click="update();">Update</button>
-			<br>
-			<br>
+			<div class="gf-form">
+				<div class="gf-form-button-row p-y-0">
+					<button ng-show="mode === 'new'" type="button" class="btn gf-form-button btn-success" ng-click="add()">Add</button>
+					<button ng-show="mode === 'edit'" type="button" class="btn btn-success pull-left" ng-click="update()">Update</button>
+				</div>
+			</div>
 		</div>
 		</div>
+
 	</div>
 	</div>
 </div>
 </div>

+ 0 - 49
public/app/features/apps/edit_ctrl.ts

@@ -1,49 +0,0 @@
-///<reference path="../../headers/common.d.ts" />
-
-import angular from 'angular';
-import _ from 'lodash';
-
-export class AppEditCtrl {
-  appModel: any;
-  appId: any;
-  includedPanels: any;
-  includedDatasources: any;
-
-  /** @ngInject */
-  constructor(private backendSrv: any, private $routeParams: any) {
-    this.appModel = {};
-    this.appId = $routeParams.appId;
-
-    this.backendSrv.get(`/api/org/apps/${this.appId}/settings`).then(result => {
-      this.appModel = result;
-      this.includedPanels = _.where(result.includes, {type: 'panel'});
-      this.includedDatasources = _.where(result.includes, {type: 'datasource'});
-    });
-  }
-
-  update() {
-    var updateCmd = _.extend({
-      appId: this.appModel.appId,
-      orgId: this.appModel.orgId,
-      enabled: this.appModel.enabled,
-      pinned: this.appModel.pinned,
-      jsonData: this.appModel.jsonData,
-      secureJsonData: this.appModel.secureJsonData,
-    }, {});
-
-    this.backendSrv.post(`/api/org/apps/${this.appId}/settings`, updateCmd).then(function() {
-      window.location.href = window.location.href;
-    });
-  }
-
-  toggleEnabled() {
-    this.update();
-  }
-
-  togglePinned() {
-    this.update();
-  }
-}
-
-angular.module('grafana.controllers').controller('AppEditCtrl', AppEditCtrl);
-

+ 0 - 17
public/app/features/apps/list_ctrl.ts

@@ -1,17 +0,0 @@
-///<reference path="../../headers/common.d.ts" />
-
-import angular from 'angular';
-
-export class AppListCtrl {
-  apps: any[];
-
-  /** @ngInject */
-  constructor(private backendSrv: any) {
-
-    this.backendSrv.get('api/org/apps').then(apps => {
-      this.apps = apps;
-    });
-  }
-}
-
-angular.module('grafana.controllers').controller('AppListCtrl', AppListCtrl);

+ 0 - 108
public/app/features/apps/partials/edit.html

@@ -1,108 +0,0 @@
-<navbar title="Plugins" title-url="Plugins" icon="icon-gf icon-gf-apps">
-</navbar>
-
-<div class="page-container">
-	<div class="flex-container">
-		<div class="flex-column app-edit-logo-box">
-			<img src="{{ctrl.appModel.info.logos.large}}">
-		</div>
-		<div class="flex-column">
-			<h1>
-				{{ctrl.appModel.name}}
-			</h1>
-			<div class="app-edit-description">
-				{{ctrl.appModel.info.description}}<br>
-				<span style="small">
-					Version: {{ctrl.appModel.info.version}} &nbsp; &nbsp; Updated: {{ctrl.appModel.info.updated}}
-				</span>
-			</div>
-
-			<div class="form-inline">
-				<editor-checkbox text="Enabled" model="ctrl.appModel.enabled" change="ctrl.toggleEnabled()"></editor-checkbox>
-				&nbsp; &nbsp; &nbsp;
-				<editor-checkbox text="Pinned" model="ctrl.appModel.pinned" change="ctrl.togglePinned()"></editor-checkbox>
-			</div>
-		</div>
-		<div class="flex-column">
-			<ul class="app-edit-links">
-				<li>
-					By <a href="{{ctrl.appModel.info.author.url}}" class="external-link" target="_blank">{{ctrl.appModel.info.author.name}}</a>
-				</li>
-				<li ng-repeat="link in ctrl.appModel.info.links">
-					<a href="{{link.url}}" class="external-link" target="_blank">{{link.name}}</a>
-				</li>
-			</ul>
-		</div>
-	</div>
-
-	<section class="simple-box">
-		<h3 class="simple-box-header">Included with app:</h3>
-		<div class="flex-container">
-			<div class="simple-box-body simple-box-column">
-				<div class="simple-box-column-header">
-					<i class="fa fa-th-large"></i>
-					Dashboards
-				</div>
-				<ul>
-					<li><em class="small">None</em></li>
-				</ul>
-			</div>
-			<div class="simple-box-body simple-box-column">
-				<div class="simple-box-column-header">
-					<i class="fa fa-line-chart"></i>
-					Panels
-				</div>
-				<ul>
-					<li ng-show="!ctrl.includedPanels.length"><em class="small">None</em></li>
-					<li ng-repeat="panel in ctrl.includedPanels">
-						{{panel.name}}
-					</li>
-				</ul>
-			</div>
-			<div class="simple-box-body simple-box-column">
-				<div class="simple-box-column-header">
-					<i class="fa fa-database"></i>
-					Datasources
-				</div>
-				<ul>
-					<li ng-show="!ctrl.includedDatasources.length"><em class="small">None</em></li>
-					<li ng-repeat="ds in ctrl.includedDatasources">
-						{{ds.name}}
-					</li>
-				</ul>
-			</div>
-			<div class="simple-box-body simple-box-column">
-				<div class="simple-box-column-header">
-					<i class="fa fa-files-o"></i>
-					Pages
-				</div>
-				<ul>
-					<li ng-repeat="page in ctrl.appModel.pages">
-						<a href="apps/{{ctrl.appId}}/page/{{page.slug}}" class="external-link">{{page.name}}</a>
-					</li>
-				</ul>
-			</div>
-
-		</div>
-	</section>
-
-	<section class="simple-box">
-		<h3 class="simple-box-header">Dependencies:</h3>
-		<div class="simple-box-body">
-			Grafana 2.6.x
-		</div>
-	</section>
-
-	<section class="simple-box">
-		<h3 class="simple-box-header">Configuration:</h3>
-		<div class="simple-box-body">
-			<div ng-if="ctrl.appModel.appId">
-				<plugin-component type="app-config-ctrl"></plugin-component>
-				<div class="clearfix"></div>
-				<button type="submit" class="btn btn-success" ng-click="ctrl.update()">Save</button>
-			</div>
-		</div>
-	</section>
-
-
-</div>

+ 0 - 47
public/app/features/apps/partials/list.html

@@ -1,47 +0,0 @@
-<navbar title="Plugins" icon="icon-gf icon-gf-apps">
-</navbar>
-
-<div class="page-container">
-  <div class="page-header">
-    <h1>Plugins</h1>
-    </div>
-	<div ng-if="!ctrl.apps">
-		<em>No apps defined</em>
-	</div>
-
-	<ul class="filter-list">
-      <li ng-repeat="app in ctrl.apps">
-        <ul class="filter-list-card">
-					<li class="filter-list-card-image">
-						<img ng-src="{{app.info.logos.small}}">
-					</li>
-          <li>
-            <div class="filter-list-card-controls">
-              <div class="filter-list-card-config">
-								<a href="apps/{{app.appId}}/edit">
-									<i class="fa fa-cog"></i>
-								</a>
-              </div>
-            </div>
-						<span class="filter-list-card-title">
-							<a href="apps/{{app.appId}}/edit">
-								{{app.name}}
-							</a>
-							&nbsp; &nbsp;
-							<span class="label label-info" ng-if="app.enabled">
-								Enabled
-							</span>
-							&nbsp;
-							<span class="label label-info" ng-if="app.pinned">
-								Pinned
-							</span>
-
-						</span>
-            <span class="filter-list-card-status">
-              <span class="filter-list-card-state">Dashboards: 1</span>
-            </span>
-          </li>
-        </ul>
-      </li>
-	</ul>
-</div>

+ 2 - 2
public/app/features/dashboard/dashnav/dashnav.html

@@ -23,12 +23,12 @@
 	<li ng-show="dashboardMeta.canShare" class="dropdown">
 	<li ng-show="dashboardMeta.canShare" class="dropdown">
 		<a class="pointer" ng-click="hideTooltip($event)" bs-tooltip="'Share dashboard'" data-placement="bottom" data-toggle="dropdown"><i class="fa fa-share-square-o"></i></a>
 		<a class="pointer" ng-click="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">
 		<ul class="dropdown-menu">
-			<li ng-if="dashboardMeta.canEdit">
+			<li>
 				<a class="pointer" ng-click="shareDashboard(0)">
 				<a class="pointer" ng-click="shareDashboard(0)">
 					<i class="fa fa-link"></i> Link to Dashboard
 					<i class="fa fa-link"></i> Link to Dashboard
 				</a>
 				</a>
 			</li>
 			</li>
-			<li ng-if="dashboardMeta.canEdit">
+			<li>
 				<a class="pointer" ng-click="shareDashboard(1)">
 				<a class="pointer" ng-click="shareDashboard(1)">
 					<i class="icon-gf icon-gf-snapshot"></i>Snapshot sharing
 					<i class="icon-gf icon-gf-snapshot"></i>Snapshot sharing
 				</a>
 				</a>

+ 6 - 0
public/app/features/dashboard/dashnav/dashnav.ts

@@ -12,6 +12,8 @@ export class DashNavCtrl {
     $scope.init = function() {
     $scope.init = function() {
       $scope.onAppEvent('save-dashboard', $scope.saveDashboard);
       $scope.onAppEvent('save-dashboard', $scope.saveDashboard);
       $scope.onAppEvent('delete-dashboard', $scope.deleteDashboard);
       $scope.onAppEvent('delete-dashboard', $scope.deleteDashboard);
+      $scope.onAppEvent('export-dashboard', $scope.snapshot);
+      $scope.onAppEvent('quick-snapshot', $scope.quickSnapshot);
 
 
       $scope.showSettingsMenu = $scope.dashboardMeta.canEdit || $scope.contextSrv.isEditor;
       $scope.showSettingsMenu = $scope.dashboardMeta.canEdit || $scope.contextSrv.isEditor;
 
 
@@ -52,6 +54,10 @@ export class DashNavCtrl {
       });
       });
     };
     };
 
 
+    $scope.quickSnapshot = function() {
+      $scope.shareDashboard(1);
+    };
+
     $scope.openSearch = function() {
     $scope.openSearch = function() {
       $scope.appEvent('show-dash-search');
       $scope.appEvent('show-dash-search');
     };
     };

+ 8 - 0
public/app/features/dashboard/keybindings.js

@@ -60,6 +60,14 @@ function(angular, $) {
         scope.appEvent('zoom-out', evt);
         scope.appEvent('zoom-out', evt);
       }, { inputDisabled: true });
       }, { inputDisabled: true });
 
 
+      keyboardManager.bind('ctrl+e', function(evt) {
+        scope.appEvent('export-dashboard', evt);
+      }, { inputDisabled: true });
+
+      keyboardManager.bind('ctrl+i', function(evt) {
+        scope.appEvent('quick-snapshot', evt);
+      }, { inputDisabled: true });
+
       keyboardManager.bind('esc', function() {
       keyboardManager.bind('esc', function() {
         var popups = $('.popover.in');
         var popups = $('.popover.in');
         if (popups.length > 0) {
         if (popups.length > 0) {

+ 16 - 15
public/app/features/dashboard/partials/graphiteImport.html

@@ -1,22 +1,23 @@
 <div ng-controller="GraphiteImportCtrl" ng-init="init()">
 <div ng-controller="GraphiteImportCtrl" ng-init="init()">
 
 
 	<div ng-if="datasources.length > 0">
 	<div ng-if="datasources.length > 0">
-		<h2 style="margin-top: 30px;">Load dashboard from Graphite-Web</h2>
+		<h2 class="page-heading">Load dashboard from Graphite-Web</h2>
 
 
-		<div class="tight-form last">
-			<ul class="tight-form-list">
-				<li class="tight-form-item" style="width: 150px">
-					<strong>Data source</strong>
-				</li>
-				<li>
-					<select type="text" ng-model="options.sourceName" class="input-medium tight-form-input" ng-options="f for f in datasources">
-					</select>
-				</li>
-				<li style="float: right">
-					<button class="btn btn-inverse tight-form-btn" ng-click="listAll()">List dashboards</button>
-				</li>
-				<div class="clearfix"></div>
-			</ul>
+		<div class="gf-form-group">
+			<div class="gf-form-inline">
+				<div class="gf-form">
+					<span class="gf-form-label width-10">Data source</span>
+				</div>
+				<div class="gf-form">
+					<div class="gf-form-select-wrapper">
+						<select type="text" ng-model="options.sourceName" class="gf-form-input gf-size-auto" ng-options="f for f in datasources">
+						</select>
+					</div>
+				</div>
+				<div class="gf-form">
+					<button class="btn btn-success pull-right" ng-click="listAll()">List dashboards</button>
+				</div>
+			</div>
 		</div>
 		</div>
 
 
 		<table class="grafana-options-table" style="margin-top: 20px;">
 		<table class="grafana-options-table" style="margin-top: 20px;">

+ 3 - 1
public/app/features/dashboard/partials/import.html

@@ -24,7 +24,9 @@
 		<div class="gf-form">
 		<div class="gf-form">
 			<div class="gf-form-label">Dashboard source</div>
 			<div class="gf-form-label">Dashboard source</div>
 			<div>
 			<div>
-				<select type="text" ng-model="sourceName" class="input-medium tight-form-input" ng-options="f for f in datasources"></select>
+				<div class="gf-form-select-wrapper">
+					<select class="gf-form-input gf-size-auto" ng-model="sourceName" ng-options="f for f in datasources"></select>
+				</div>
 			</div>
 			</div>
 			<div class="gf-form-btn">
 			<div class="gf-form-btn">
 				<button class="btn btn-success" ng-click="startImport()">Import</button>
 				<button class="btn btn-success" ng-click="startImport()">Import</button>

+ 0 - 62
public/app/features/dashboard/partials/linksEditor.html

@@ -1,62 +0,0 @@
-<div ng-controller="DashLinksController">
-	<div class="editor-row">
-		<div class="tight-form-section">
-			<h5>Links and Dash Navigation</h5>
-
-			<div ng-repeat="link in panel.links">
-				<div class="tight-form" >
-					<ul class="tight-form-list">
-						<li class="tight-form-item">
-							<i class="fa fa-remove pointer" ng-click="deleteLink(link)"></i>
-						</li>
-
-						<li class="tight-form-item" style="width: 80px;">Link title</li>
-						<li>
-							<input type="text" ng-model="link.title" class="input-medium tight-form-input">
-						</li>
-
-						<li class="tight-form-item">Type</li>
-						<li>
-							<select class="input-medium tight-form-input" style="width: 101px;" ng-model="link.type" ng-options="f for f in ['dashboard','absolute']"></select>
-						</li>
-
-						<li class="tight-form-item" ng-show="link.type === 'dashboard'">Dashboard</li>
-						<li ng-show="link.type === 'dashboard'">
-							<input type="text"
-							ng-model="link.dashboard"
-							bs-typeahead="searchDashboards"
-							class="input-large tight-form-input">
-						</li>
-
-						<li class="tight-form-item" ng-show="link.type === 'absolute'">Url</li>
-						<li ng-show="link.type === 'absolute'">
-							<input type="text" ng-model="link.url" class="input-xlarge tight-form-input">
-						</li>
-					</ul>
-					<div class="clearfix"></div>
-				</div>
-				<div class="tight-form">
-					<ul class="tight-form-list" role="menu">
-						<li class="tight-form-item">
-							<i class="fa fa-remove invisible"></i>
-						</li>
-						<li class="tight-form-item" style="width: 80px;">
-							Params
-							<tip>Use var-variableName=value to pass templating variables.</tip>
-						</li>
-						<li>
-							<input type="text" ng-model="link.params" class="input-xxlarge tight-form-input">
-						</li>
-					</ul>
-					<div class="clearfix"></div>
-				</div>
-			</div>
-
-		</div>
-	</div>
-
-	<div class="editor-row">
-		<br>
-		<button class="btn btn-inverse" ng-click="addLink()"><i class="fa fa-plus"></i> Add link</button>
-	</div>
-</div>

+ 85 - 148
public/app/features/dashboard/partials/settings.html

@@ -1,106 +1,81 @@
-<div class="gf-box-header">
-	<div class="gf-box-title">
-		<i class="fa fa-cogs"></i>
+<div class="tabbed-view-header">
+	<h2 class="tabbed-view-title">
 		Settings
 		Settings
-	</div>
+	</h2>
 
 
-	<div ng-model="editor.index" bs-tabs style="text-transform:capitalize;">
-		<div ng-repeat="tab in ['General', 'Rows', 'Links', 'Time picker', 'Metadata']" data-title="{{tab}}">
-		</div>
-	</div>
+	<ul class="gf-tabs">
+		<li class="gf-tabs-item" ng-repeat="tab in ::['General', 'Rows', 'Links', 'Time picker', 'Metadata']">
+			<a class="gf-tabs-link" ng-click="editor.index = $index" ng-class="{active: editor.index === $index}">
+				{{::tab}}
+			</a>
+		</li>
+	</ul>
 
 
-	<button class="gf-box-header-close-btn" ng-click="dismiss();">
+	<button class="tabbed-view-close-btn" ng-click="dismiss();">
 		<i class="fa fa-remove"></i>
 		<i class="fa fa-remove"></i>
 	</button>
 	</button>
 </div>
 </div>
 
 
-<div class="gf-box-body" style="padding-bottom: 50px;">
+<div class="tabbed-view-body">
 	<div ng-if="editor.index == 0">
 	<div ng-if="editor.index == 0">
-		<div class="editor-row">
-			<div class="tight-form-section">
-				<h5>Dashboard info</h5>
-				<div class="tight-form">
-					<ul class="tight-form-list">
-						<li class="tight-form-item" style="width: 90px">
-							Title
-						</li>
-						<li>
-							<input type="text" class="input-large tight-form-input" ng-model='dashboard.title'></input>
-						</li>
-						<li class="tight-form-item">
-							Tags
-							<tip>Press enter to a add tag</tip>
-						</li>
-						<li>
-							<bootstrap-tagsinput ng-model="dashboard.tags" tagclass="label label-tag" placeholder="add tags">
-							</bootstrap-tagsinput>
-						</li>
-					</ul>
-					<div class="clearfix"></div>
-				</div>
-				<div class="tight-form last">
-					<ul class="tight-form-list">
-						<li class="tight-form-item" style="width: 90px">
-							Timezone
-						</li>
-						<li>
-							<select ng-model="dashboard.timezone" class='input-small tight-form-input' ng-options="f for f in ['browser','utc']"></select>
-						</li>
-					</ul>
-					<div class="clearfix"></div>
+
+		<h5 class="section-heading">Dashboard Detail</h5>
+		<div class="gf-form-group">
+			<div class="gf-form">
+				<label class="gf-form-label width-7">Title</label>
+				<input type="text" class="gf-form-input max-width-25" ng-model='dashboard.title'></input>
+			</div>
+			<div class="gf-form">
+				<label class="gf-form-label width-7">Tags<tip>Press enter to a add tag</tip></label>
+				<bootstrap-tagsinput ng-model="dashboard.tags" tagclass="label label-tag" placeholder="add tags">
+				</bootstrap-tagsinput>
+			</div>
+
+			<div class="gf-form">
+				<label class="gf-form-label width-7">Timezone</label>
+				<div class="gf-form-select-wrapper">
+					<select ng-model="dashboard.timezone" class='gf-form-input' ng-options="f for f in ['browser','utc']"></select>
 				</div>
 				</div>
 			</div>
 			</div>
 		</div>
 		</div>
 
 
-		<div class="editor-row">
-			<div class="tight-form-section">
-				<h5>Toggles</h5>
-				<div class="tight-form last">
-					<ul class="tight-form-list">
-						<li class="tight-form-item">
-							<editor-checkbox text="Editable" model="dashboard.editable"></editor-checkbox>
-						</li>
-						<li class="tight-form-item">
-							<editor-checkbox text="Hide Controls (CTRL+H)" model="dashboard.hideControls"></editor-checkbox>
-						</li>
-						<li class="tight-form-item last">
-							<editor-checkbox text="Shared Crosshair (CTRL+O)" model="dashboard.sharedCrosshair"></editor-checkbox>
-						</li>
-					</ul>
-					<div class="clearfix"></div>
+		<h5 class="section-heading">On/Off Toggles</h5>
+		<div class="gf-form-group">
+			<div class="gf-form-inline">
+				<div class="gf-form">
+					<editor-checkbox text="Editable" model="dashboard.editable"></editor-checkbox>
+				</div>
+				<div class="gf-form">
+					<editor-checkbox text="Hide Controls (CTRL+H)" model="dashboard.hideControls"></editor-checkbox>
+				</div>
+				<div class="gf-form">
+					<editor-checkbox text="Shared Crosshair (CTRL+O)" model="dashboard.sharedCrosshair"></editor-checkbox>
 				</div>
 				</div>
 			</div>
 			</div>
 		</div>
 		</div>
 	</div>
 	</div>
 
 
 	<div ng-if="editor.index == 1">
 	<div ng-if="editor.index == 1">
-		<div class="editor-row">
-			<div class="tight-form-section">
-				<h5>Rows settings</h5>
-				<div class="tight-form-container">
-					<div class="tight-form" ng-repeat="row in dashboard.rows">
-						<ul class="tight-form-list">
-							<li class="tight-form-item">
-								Title
-							</li>
-							<li>
-								<input type="text" class="input tight-form-input" style="width: 400px;" ng-model='row.title'></input>
-							</li>
-							<li class="tight-form-item">
-								<editor-checkbox text="Show title" model="row.showTitle"></editor-checkbox>
-							</li>
-							<li class="tight-form-item last">
-								<i ng-click="_.move(dashboard.rows,$index,$index-1)" ng-class="{'invisible': $first}" class="pointer fa fa-arrow-up"></i>
-							</li>
-							<li class="tight-form-item last">
-								<i ng-click="_.move(dashboard.rows,$index,$index+1)" ng-class="{'invisible': $last}" class="pointer fa fa-fw fa-arrow-down"></i>
-							</li>
-							<li class="tight-form-item last">
-								<i ng-click="dashboard.rows = _.without(dashboard.rows,row)" class="pointer fa fa-remove"></i>
-							</li>
-						</ul>
-						<div class="clearfix"></div>
-					</div>
+		<h5 class="section-heading">Rows settings</h5>
+
+		<div class="gf-form-group">
+			<div class="gf-form-inline" ng-repeat="row in dashboard.rows">
+				<div class="gf-form">
+					<span class="gf-form-label">Title</span>
+					<input type="text" class="gf-form-input max-width-14" ng-model='row.title'></input>
+					<editor-checkbox text="Show title" model="row.showTitle"></editor-checkbox>
+				</div>
+
+				<div class="gf-form">
+					<button class="btn btn-inverse btn-mini" style="margin-right: 5px;" ng-click="dashboard.rows = _.without(dashboard.rows,row)">
+						<i class="fa fa-trash"></i>
+					</button>
+					<button class="btn btn-inverse btn-mini" ng-hide="$first" style="margin-right: 5px;" ng-click="_.move(dashboard.rows,$index,$index-1)">
+						<i ng-class="{'invisible': $first}" class="fa fa-arrow-up"></i>
+					</button>
+					<button class="btn btn-inverse btn-mini" ng-hide="$last" style="margin-right: 5px;" ng-click="_.move(dashboard.rows,$index,$index+1)">
+						<i ng-class="{'invisible': $last}" class="fa fa-arrow-down"></i>
+					</button>
 				</div>
 				</div>
 			</div>
 			</div>
 		</div>
 		</div>
@@ -114,69 +89,31 @@
 		<gf-time-picker-settings dashboard="dashboard"></gf-time-picker-settings>
 		<gf-time-picker-settings dashboard="dashboard"></gf-time-picker-settings>
 	</div>
 	</div>
 
 
-  <div ng-if="editor.index == 4">
-    <div class="row">
-      <h5>Dashboard info</h5>
-      <div class="pull-left tight-form">
-        <div class="tight-form">
-          <ul class="tight-form-list">
-            <li class="tight-form-item" style="width: 120px">
-              Last updated at:
-            </li>
-            <li class="tight-form-item" style="width: 180px">
-              {{formatDate(dashboardMeta.updated)}}
-            </li>
-          </ul>
-          <div class="clearfix"></div>
-        </div>
-        <div class="tight-form">
-          <ul class="tight-form-list">
-            <li class="tight-form-item" style="width: 120px">
-              Last updated by:
-            </li>
-            <li class="tight-form-item" style="width: 180px">
-              {{dashboardMeta.updatedBy}}
-            </li>
-          </ul>
-          <div class="clearfix"></div>
-        </div> 
-        <div class="tight-form">
-          <ul class="tight-form-list">
-            <li class="tight-form-item" style="width: 120px">
-              Created at:
-            </li>
-            <li class="tight-form-item" style="width: 180px">
-              {{formatDate(dashboardMeta.created)}}
-            </li>
-          </ul>
-          <div class="clearfix"></div>
-        </div>
-        <div class="tight-form">
-          <ul class="tight-form-list">
-            <li class="tight-form-item" style="width: 120px">
-              Created by:
-            </li>
-            <li class="tight-form-item" style="width: 180px">
-              {{dashboardMeta.createdBy}}
-            </li>
-          </ul>
-          <div class="clearfix"></div>
-        </div>
-        <div class="tight-form">
-          <ul class="tight-form-list">
-            <li class="tight-form-item" style="width: 120px">
-              Current version:
-            </li>
-            <li class="tight-form-item" style="width: 180px">
-              {{dashboardMeta.version}}
-            </li>
-          </ul>
-          <div class="clearfix"></div>
-        </div>
-      </div>
-    </div>
-  </div>
-
+	<div ng-if="editor.index == 4">
+		<h5 class="section-heading">Dashboard info</h5>
+		<div class="gf-form-group">
+			<div class="gf-form">
+				<span class="gf-form-label width-10">Last updated at:</span>
+				<span class="gf-form-label width-18">{{formatDate(dashboardMeta.updated)}}</span>
+			</div>
+			<div class="gf-form">
+				<span class="gf-form-label width-10">Last updated by:</span>
+				<span class="gf-form-label width-18">{{dashboardMeta.updatedBy}}&nbsp;</span>
+			</div>
+			<div class="gf-form">
+				<span class="gf-form-label width-10">Created at:</span>
+				<span class="gf-form-label width-18">{{formatDate(dashboardMeta.created)}}&nbsp;</span>
+			</div>
+			<div class="gf-form">
+				<span class="gf-form-label width-10">Created by:</span>
+				<span class="gf-form-label width-18">{{dashboardMeta.createdBy}}&nbsp;</span>
+			</div>
+			<div class="gf-form">
+				<span class="gf-form-label width-10">Current version:</span>
+				<span class="gf-form-label width-18">{{dashboardMeta.version}}&nbsp;</span>
+			</div>
+		</div>
+	</div>
 </div>
 </div>
 
 
 <div class="clearfix"></div>
 <div class="clearfix"></div>

+ 53 - 85
public/app/features/dashboard/partials/shareModal.html

@@ -35,53 +35,30 @@
 
 
 	<div ng-include src="'shareLinkOptions.html'"></div>
 	<div ng-include src="'shareLinkOptions.html'"></div>
 
 
-	<div class="gf-form">
-		<div class="gf-form-row">
-			<span class="gf-fluid-input">
-				<textarea rows="5" data-share-panel-url class="input" ng-model='iframeHtml'></textarea>
-			</span>
+	<div class="gf-form-group position-center">
+		<div class="gf-form width-30" >
+			<textarea rows="5" data-share-panel-url class="gf-form-input width-30" ng-model='iframeHtml'></textarea>
+		</div>
+	</div>
+	<div class="gf-form-group">
+		<div class="gf-form position-center">
+			<button class="btn btn-inverse" data-clipboard-text="{{iframeHtml}}" clipboard-button><i class="fa fa-clipboard"></i> Copy</button>
 		</div>
 		</div>
-		<button class="btn btn-inverse" data-clipboard-text="{{iframeHtml}}" clipboard-button><i class="fa fa-clipboard"></i> Copy</button>
 	</div>
 	</div>
 </script>
 </script>
 
 
 <script type="text/ng-template" id="shareLinkOptions.html">
 <script type="text/ng-template" id="shareLinkOptions.html">
-	<div class="editor-row" style="margin: 11px 20px 33px 20px">
-		<div class="section">
-			<div class="tight-form">
-				<ul class="tight-form-list">
-					<li class="tight-form-item" style="width: 170px;">
-						<label class="checkbox-label" for="options.forCurrent">Current time range</label>
-					</li>
-					<li class="tight-form-item last">
-						<input class="cr1" id="options.forCurrent" type="checkbox" ng-model="options.forCurrent" ng-checked="options.forCurrent" ng-change="buildUrl()">
-						<label for="options.forCurrent" class="cr1"></label>
-					</li>
-				</ul>
-				<div class="clearfix"></div>
-			</div>
-			<div class="tight-form">
-				<ul class="tight-form-list">
-					<li class="tight-form-item" style="width: 170px">
-						<label class="checkbox-label" for="options.includeTemplateVars">Include template variables</label>
-					</li>
-					<li class="tight-form-item last">
-						<input class="cr1" id="options.includeTemplateVars" type="checkbox" ng-model="options.includeTemplateVars" ng-checked="options.includeTemplateVars" ng-change="buildUrl()">
-						<label for="options.includeTemplateVars" class="cr1"></label>
-					</li>
-				</ul>
-				<div class="clearfix"></div>
-			</div>
-			<div class="tight-form">
-				<ul class="tight-form-list">
-					<li class="tight-form-item" style="width: 170px">
-						Theme
-					</li>
-					<li>
-						<select class="input-small tight-form-input last" style="width: 211px" ng-model="options.theme" ng-options="f as f for f in ['current', 'dark', 'light']" ng-change="buildUrl()"></select>
-					</li>
-				</ul>
-				<div class="clearfix"></div>
+	<div class="gf-form-group position-center">
+		<div class="gf-form">
+			<editor-checkbox text="Current time range" model="options.forCurrent" change="updated()"></editor-checkbox>
+		</div>
+		<div class="gf-form">
+			<editor-checkbox text="Include template variables" model="options.includeTemplateVars" change="updated()"></editor-checkbox>
+		</div>
+		<div class="gf-form">
+			<span class="gf-form-label">Theme</span>
+			<div class="gf-form-select-wrapper max-width-10">
+				<select class="gf-form-input" ng-model="options.theme" ng-options="f as f for f in ['current', 'dark', 'light']" ng-change="buildUrl()"></select>
 			</div>
 			</div>
 		</div>
 		</div>
 	</div>
 	</div>
@@ -93,14 +70,19 @@
 	</div>
 	</div>
 
 
 	<div ng-include src="'shareLinkOptions.html'"></div>
 	<div ng-include src="'shareLinkOptions.html'"></div>
-	<div class="gf-form">
-		<div class="gf-form-row">
-			<button class="btn btn-inverse pull-right" data-clipboard-text="{{shareUrl}}" clipboard-button><i class="fa fa-clipboard"></i> Copy</button>
-			<span class="gf-fluid-input">
-				<input type="text" data-share-panel-url class="input" ng-model='shareUrl'></input>
-			</span>
+	<div class="gf-form-group position-center">
+		<div class="gf-form-inline">
+
+			<div class="gf-form width-30">
+				<input type="text" data-share-panel-url class="gf-form-input" ng-model="shareUrl"></input>
+			</div>
+			<div class="gf-form pull-right">
+				<button class="btn btn-inverse pull-right" data-clipboard-text="{{shareUrl}}" clipboard-button><i class="fa fa-clipboard"></i> Copy</button>
+			</div>
 		</div>
 		</div>
-		<div class="editor-row" style="margin-top: 5px;" ng-show="modeSharePanel">
+	</div>
+	<div class="gf-form-group">
+		<div class="gf-form position-center" ng-show="modeSharePanel">
 			<a href="{{imageUrl}}" target="_blank"><i class="fa fa-camera"></i> Direct link rendered image</a>
 			<a href="{{imageUrl}}" target="_blank"><i class="fa fa-camera"></i> Direct link rendered image</a>
 		</div>
 		</div>
 	</div>
 	</div>
@@ -132,29 +114,15 @@
 			</p>
 			</p>
 		</div>
 		</div>
 
 
-		<div class="editor-row share-modal-options" style="">
-			<div class="section" ng-if="step === 1">
-				<div class="tight-form">
-					<ul class="tight-form-list">
-						<li class="tight-form-item" style="width: 110px;">
-							Snapshot name
-						</li>
-						<li>
-							<input type="text" ng-model="snapshot.name" class="input-large tight-form-input last" >
-						</li>
-					</ul>
-					<div class="clearfix"></div>
-				</div>
-				<div class="tight-form">
-					<ul class="tight-form-list">
-						<li class="tight-form-item" style="width: 110px">
-							Expire
-						</li>
-						<li>
-							<select class="input-small tight-form-input last" style="width: 211px" ng-model="snapshot.expires" ng-options="f.value as f.text for f in expireOptions"></select>
-						</li>
-					</ul>
-					<div class="clearfix"></div>
+		<div class="gf-form-group share-modal-options position-center">
+			<div class="gf-form" ng-if="step === 1">
+				<span class="gf-form-label width-12">Snapshot name</span>
+				<input type="text" ng-model="snapshot.name" class="gf-form-input max-width-15" >
+			</div>
+			<div class="gf-form" ng-if="step === 1">
+				<span class="gf-form-label width-12">Expire</span>
+				<div class="gf-form-select-wrapper max-width-15">
+					<select class="gf-form-input" ng-model="snapshot.expires" ng-options="f.value as f.text for f in expireOptions"></select>
 				</div>
 				</div>
 			</div>
 			</div>
 
 
@@ -168,21 +136,21 @@
 					<button class="btn btn-inverse btn-large" data-clipboard-text="{{snapshotUrl}}" clipboard-button><i class="fa fa-clipboard"></i> Copy Link</button>
 					<button class="btn btn-inverse btn-large" data-clipboard-text="{{snapshotUrl}}" clipboard-button><i class="fa fa-clipboard"></i> Copy Link</button>
 				</div>
 				</div>
 			</div>
 			</div>
-		</div>
 
 
-		<div ng-if="step === 1">
-			<button class="btn btn-success btn-large" ng-click="createSnapshot()" ng-disabled="loading">
-				<i class="fa fa-save"></i>
-				Local Snapshot
-			</button>
-			<button class="btn btn-primary btn-large" ng-if="externalEnabled" ng-click="createSnapshot(true)" ng-disabled="loading">
-				<i class="fa fa-cloud-upload"></i>
-				{{sharingButtonText}}
-			</button>
-		</div>
+			<div ng-if="step === 1" class="gf-form-buttons-row">
+				<button class="btn btn-success btn-large" ng-click="createSnapshot()" ng-disabled="loading">
+					<i class="fa fa-save"></i>
+					Local Snapshot
+				</button>
+				<button class="btn btn-primary btn-large" ng-if="externalEnabled" ng-click="createSnapshot(true)" ng-disabled="loading">
+					<i class="fa fa-cloud-upload"></i>
+					{{sharingButtonText}}
+				</button>
+			</div>
 
 
-		<div class="pull-right" ng-if="step === 2" style="padding: 5px">
-			Did you make a mistake? <a class="pointer" ng-click="deleteSnapshot()" target="_blank">delete snapshot.</a>
+			<div class="pull-right" ng-if="step === 2" style="padding: 5px">
+				Did you make a mistake? <a class="pointer" ng-click="deleteSnapshot()" target="_blank">delete snapshot.</a>
+			</div>
 		</div>
 		</div>
 
 
 	</div>
 	</div>

+ 22 - 25
public/app/features/dashboard/submenu/submenu.html

@@ -1,30 +1,27 @@
 <div class="submenu-controls">
 <div class="submenu-controls">
-	<div class="tight-form borderless">
+	<ul ng-if="ctrl.dashboard.templating.list.length > 0">
+		<li ng-repeat="variable in ctrl.variables" class="submenu-item">
+			<span class="submenu-item-label template-variable " ng-show="!variable.hideLabel">
+				{{variable.label || variable.name}}:
+			</span>
+			<value-select-dropdown variable="variable" on-updated="ctrl.variableUpdated(variable)" get-values-for-tag="ctrl.getValuesForTag(variable, tagKey)"></value-select-dropdown>
+		</li>
+	</ul>
 
 
-		<ul class="tight-form-list" ng-if="ctrl.dashboard.templating.list.length > 0">
-			<li ng-repeat="variable in ctrl.variables" class="submenu-item">
-				<span class="template-variable tight-form-item" ng-show="!variable.hideLabel" style="padding-right: 5px">
-					{{variable.label || variable.name}}:
-				</span>
-				<value-select-dropdown variable="variable" on-updated="ctrl.variableUpdated(variable)" get-values-for-tag="ctrl.getValuesForTag(variable, tagKey)"></value-select-dropdown>
-			</li>
-		</ul>
+	<ul ng-if="ctrl.dashboard.annotations.list.length > 0">
+		<li ng-repeat="annotation in ctrl.dashboard.annotations.list" class="submenu-item annotation-segment" ng-class="{'annotation-disabled': !annotation.enable}">
+			<a ng-click="ctrl.disableAnnotation(annotation)">
+				<i class="fa fa-bolt" style="color:{{annotation.iconColor}}"></i>
+				{{annotation.name}}
+				<input class="cr1" id="hideYAxis" type="checkbox" ng-model="annotation.enable" ng-checked="annotation.enable">
+				<label for="hideYAxis" class="cr1"></label>
+			</a>
+		</li>
+	</ul>
 
 
-		<ul class="tight-form-list" ng-if="ctrl.dashboard.annotations.list.length > 0">
-			<li ng-repeat="annotation in ctrl.dashboard.annotations.list" class="submenu-item annotation-segment" ng-class="{'annotation-disabled': !annotation.enable}">
-				<a ng-click="ctrl.disableAnnotation(annotation)">
-					<i class="fa fa-bolt" style="color:{{annotation.iconColor}}"></i>
-					{{annotation.name}}
-					<input class="cr1" id="hideYAxis" type="checkbox" ng-model="annotation.enable" ng-checked="annotation.enable">
-					<label for="hideYAxis" class="cr1"></label>
-				</a>
-			</li>
-		</ul>
+	<ul class="pull-right" ng-if="ctrl.dashboard.links.length > 0">
+		<dash-links-container links="ctrl.dashboard.links"></dash-links-container>
+	</ul>
 
 
-		<ul class="tight-form-list pull-right" ng-if="ctrl.dashboard.links.length > 0">
-			<dash-links-container links="ctrl.dashboard.links"></dash-links-container>
-		</ul>
-
-		<div class="clearfix"></div>
-	</div>
+	<div class="clearfix"></div>
 </div>
 </div>

+ 12 - 45
public/app/features/dashboard/timepicker/settings.html

@@ -1,48 +1,15 @@
 <div class="editor-row">
 <div class="editor-row">
-	<div class="section">
-		<div>
-			<!-- <div class="tight&#45;form"> -->
-			<!-- 	<ul class="tight&#45;form&#45;list"> -->
-			<!-- 		<li class="tight&#45;form&#45;item" style="width: 118px"> -->
-			<!-- 			Relative times -->
-			<!-- 		</li> -->
-			<!-- 		<li> -->
-			<!-- 			<input type="text" class="input&#45;xlarge tight&#45;form&#45;input last" style="width: 450px" ng&#45;model="ctrl.panel.time_options" array&#45;join> -->
-			<!-- 		</li> -->
-			<!-- 	</ul> -->
-			<!-- 	<div class="clearfix"></div> -->
-			<!-- </div> -->
-			<div class="tight-form">
-				<ul class="tight-form-list">
-					<li class="tight-form-item" style="width: 118px">
-						Auto-refresh
-					</li>
-					<li>
-						<input type="text" class="input-xlarge tight-form-input last" style="width: 450px" ng-model="ctrl.panel.refresh_intervals" array-join>
-					</li>
-				</ul>
-				<div class="clearfix"></div>
-			</div>
-
-			<div class="tight-form last">
-				<ul class="tight-form-list">
-					<li class="tight-form-item" style="width: 118px">
-						Now delay
-					</li>
-					<li class="tight-form-item">
-						now-
-					</li>
-					<li>
-						<input type="text" class="input-mini tight-form-input last"
-						ng-model="ctrl.panel.nowDelay" placeholder="0m"
-						valid-time-span
-						bs-tooltip="'Enter 1m to ignore the last minute (because it can contain incomplete metrics)'"
-						data-placement="right">
-					</li>
-
-				</ul>
-				<div class="clearfix"></div>
-			</div>
-		</div>
+<div class="gf-form-group">
+	<div class="gf-form">
+		<span class="gf-form-label width-10">Auto-refresh</span>
+		<input type="text" class="gf-form-input max-width-25" ng-model="ctrl.panel.refresh_intervals" array-join>
+	</div>
+	<div class="gf-form">
+		<span class="gf-form-label width-10">Now delay now-</span>
+		<input type="text" class="gf-form-input max-width-25"
+				ng-model="ctrl.panel.nowDelay" placeholder="0m"
+				valid-time-span
+				bs-tooltip="'Enter 1m to ignore the last minute (because it can contain incomplete metrics)'"
+				data-placement="right">
 	</div>
 	</div>
 </div>
 </div>

+ 0 - 2
public/app/features/dashboard/timepicker/timepicker.ts

@@ -129,8 +129,6 @@ export class TimePickerCtrl {
   }
   }
 
 
   setRelativeFilter(timespan) {
   setRelativeFilter(timespan) {
-    this.panel.now = true;
-
     var range = {from: timespan.from, to: timespan.to};
     var range = {from: timespan.from, to: timespan.to};
 
 
     if (this.panel.nowDelay && range.to === 'now') {
     if (this.panel.nowDelay && range.to === 'now') {

+ 65 - 79
public/app/features/dashlinks/editor.html

@@ -1,90 +1,76 @@
 <div class="editor-row">
 <div class="editor-row">
-	<h5>Links and Dash Navigation</h5>
+	<h5 class="section-heading">Links and Dash Navigation</h5>
 
 
-	<div ng-repeat="link in dashboard.links" style="margin-top: 10px;">
-		<div class="tight-form">
-			<ul class="tight-form-list pull-right">
-				<li class="tight-form-item">
-					<i ng-click="moveLink($index, -1)" ng-hide="$first" class="pointer fa fa-arrow-up"></i>
-					<i ng-click="moveLink($index, 1)" ng-hide="$last" class="pointer fa fa-fw fa-arrow-down"></i>
-				</li>
-				<li class="tight-form-item last">
-					<i class="fa fa-remove pointer" ng-click="deleteLink($index)"></i>
-				</li>
-			</ul>
+	<div ng-repeat="link in dashboard.links">
 
 
-			<ul class="tight-form-list">
-				<li class="tight-form-item" style="width: 20px">
-					<i class="fa fa-fw fa-unlink"></i>
-				</li>
+		<div class="gf-form-group">
+			<div class="gf-form-inline">
+				<div class="gf-form">
+					<span class="gf-form-label width-6">Type</span>
+					<div class="gf-form-select-wrapper width-10">
+						<select class="gf-form-input" ng-model="link.type" ng-options="f for f in ['dashboards','link']" ng-change="updated()"></select>
+					</div>
+				</div>
 
 
-				<li class="tight-form-item">Type</li>
-				<li>
-					<select class="input-medium tight-form-input" style="width: 150px;" ng-model="link.type" ng-options="f for f in ['dashboards','link']" ng-change="updated()"></select>
-				</li>
+				<div class="gf-form" ng-show="link.type === 'dashboards'">
+					<span class="gf-form-label">With tags</span>
+					<bootstrap-tagsinput ng-model="link.tags" tagclass="label label-tag" placeholder="add tags"></bootstrap-tagsinput>
+				</div>
 
 
-				<li class="tight-form-item" ng-show="link.type === 'dashboards'">With tags</li>
-				<li ng-show="link.type === 'dashboards'">
-					<bootstrap-tagsinput ng-model="link.tags" tagclass="label label-tag" placeholder="add tags">
-					</bootstrap-tagsinput>
-				</li>
-				<li class="tight-form-item" ng-show="link.type === 'dashboards'">
+				<div class="gf-form" ng-show="link.type === 'dashboards'">
 					<editor-checkbox text="As dropdown" model="link.asDropdown" change="updated()"></editor-checkbox>
 					<editor-checkbox text="As dropdown" model="link.asDropdown" change="updated()"></editor-checkbox>
-				</li>
-				<li class="tight-form-item" ng-show="link.type === 'dashboards' && link.asDropdown">
-					Title
-				</li>
-				<li ng-show="link.type === 'dashboards' && link.asDropdown">
-					<input type="text" ng-model="link.title" class="input-medium tight-form-input" ng-model-onblur ng-change="updated()">
-				</li>
-				<li class="tight-form-item" ng-show="link.type === 'link'" style="width: 51px">Url</li>
-				<li ng-show="link.type === 'link'">
-					<input type="text" ng-model="link.url" class="input-xlarge tight-form-input" style="width: 302px;" ng-model-onblur ng-change="updated()">
-				</li>
-				<li class="tight-form-item" ng-show="link.type === 'link'">
+				</div>
+
+				<div class="gf-form max-width-30" ng-show="link.type === 'link'">
+					<li class="gf-form-label width-6">Url</li>
+					<input type="text" ng-model="link.url" class="gf-form-input" ng-model-onblur ng-change="updated()">
+				</div>
+
+				<div class="gf-form">
+					<button class="btn btn-inverse btn-mini" ng-click="moveLink($index, -1)" ng-hide="$first"><i class="fa fa-arrow-up"></i></button>
+				</div>
+				<div class="gf-form">
+					<button class="btn btn-inverse btn-mini" ng-click="moveLink($index, 1)" ng-hide="$last"><i class="fa fa-arrow-down"></i></button>
+				</div>
+				<div class="gf-form">
+					<button class="btn btn-inverse btn-mini" ng-click="deleteLink($index)"><i class="fa fa-trash" ></i></button>
+				</div>
+			</div>
+
+			<div class="gf-form" ng-show="link.type === 'dashboards' && link.asDropdown">
+				<span class="gf-form-label width-6">Title</span>
+				<input type="text" ng-model="link.title" class="gf-form-input max-width-25" ng-model-onblur ng-change="updated()">
+			</div>
+
+			<div class="gf-form-inline" ng-show="link.type === 'link'">
+				<div class="gf-form">
+					<span class="gf-form-label width-6">Title</span>
+					<input type="text" ng-model="link.title" class="gf-form-input max-width-10" ng-model-onblur ng-change="updated()">
+				</div>
+
+				<div class="gf-form">
+					<span class="gf-form-label width-6">Tooltip</span>
+					<input type="text" ng-model="link.tooltip" class="gf-form-input max-width-10" placeholder="Open dashboard" ng-model-onblur ng-change="updated()">
+				</div>
+
+				<div class="gf-form">
+					<span class="gf-form-label width-6">Icon</span>
+					<div class="gf-form-select-wrapper max-width-10">
+						<select class="gf-form-input" ng-model="link.icon" ng-options="k as k for (k, v) in iconMap" ng-change="updated()"></select>
+					</div>
+				</div>
+			</div>
+
+			<div class="gf-form-inline">
+				<div class="gf-form">
+					<span class="gf-form-label width-6">Include</span>
+					<editor-checkbox text="Time range" model="link.keepTime" change="updated()"></editor-checkbox>
+					<editor-checkbox text="Variable values" model="link.includeVars" change="updated()"></editor-checkbox>
 					<editor-checkbox text="Open in new tab " model="link.targetBlank" change="updated()"></editor-checkbox>
 					<editor-checkbox text="Open in new tab " model="link.targetBlank" change="updated()"></editor-checkbox>
-				</li>
-			</ul>
-			<div class="clearfix"></div>
-		</div>
-		<div class="tight-form" ng-if="link.type === 'link'">
-			<ul class="tight-form-list">
-				<li class="tight-form-item" style="width: 20px">
-					<i class="fa fa-fw fa-unlink invisible"></i>
-				</li>
-				<li class="tight-form-item" ng-show="link.type === 'link'" style="width: 31px">Title</li>
-				<li ng-show="link.type === 'link'">
-					<input type="text" ng-model="link.title" class="input-medium tight-form-input" ng-model-onblur ng-change="updated()">
-				</li>
-				<li class="tight-form-item" ng-show="link.type === 'link'" style="width: 51px">Tooltip</li>
-				<li ng-show="link.type === 'link'">
-					<input type="text" ng-model="link.tooltip" class="input-medium tight-form-input" style="width: 151px" placeholder="Open dashboard" ng-model-onblur ng-change="updated()">
-				</li>
-				<li class="tight-form-item" ng-show="link.type === 'link'">Icon</li>
-				<li ng-show="link.type === 'link'">
-					<select class="input-medium tight-form-input" style="width: 110px;" ng-model="link.icon" ng-options="k as k for (k, v) in iconMap" ng-change="updated()"></select>
-				</li>
-			</ul>
-			<div class="clearfix"></div>
-		</div>
-		<div class="tight-form last">
-			<ul class="tight-form-list">
-				<li class="tight-form-item" style="width: 20px">
-					<i class="fa fa-fw fa-unlink invisible"></i>
-				</li>
-				<li class="tight-form-item">
-					<editor-checkbox text="Keep current time range" model="link.keepTime" change="updated()"></editor-checkbox>
-				</li>
-				<li class="tight-form-item">
-					<editor-checkbox text="Add current variable values" model="link.includeVars" change="updated()"></editor-checkbox>
-				</li>
-			</ul>
-			<div class="clearfix"></div>
+				</div>
+			</div>
 		</div>
 		</div>
 	</div>
 	</div>
 </div>
 </div>
-<div class="editor-row">
-	<br>
-	<button class="btn btn-inverse" ng-click="addLink()"><i class="fa fa-plus"></i> Add link</button>
-</div>
 
 
+<button class="btn btn-inverse" ng-click="addLink()"><i class="fa fa-plus"></i> Add link</button>

+ 7 - 0
public/app/features/datasources/partials/edit.html

@@ -15,6 +15,12 @@
 			<div class="gf-form">
 			<div class="gf-form">
 				<span class="gf-form-label width-7">Name</span>
 				<span class="gf-form-label width-7">Name</span>
 				<input class="gf-form-input max-width-21" type="text" ng-model="current.name" placeholder="My data source name" required>
 				<input class="gf-form-input max-width-21" type="text" ng-model="current.name" placeholder="My data source name" required>
+				<gf-popover offset="0px -95px">
+					The name is used when you select the data source in panels.
+					The <code>Default</code> data source is preselected in new
+					panels.
+				</gf-popover>
+
 				<editor-checkbox text="Default" model="current.isDefault"></editor-checkbox>
 				<editor-checkbox text="Default" model="current.isDefault"></editor-checkbox>
 			</div>
 			</div>
 
 
@@ -24,6 +30,7 @@
 					<select class="gf-form-input gf-size-auto" ng-model="current.type" ng-options="k as v.name for (k, v) in types" ng-change="typeChanged()"></select>
 					<select class="gf-form-input gf-size-auto" ng-model="current.type" ng-options="k as v.name for (k, v) in types" ng-change="typeChanged()"></select>
 				</div>
 				</div>
 			</div>
 			</div>
+
 		</div>
 		</div>
 
 
 		<rebuild-on-change property="datasourceMeta.id">
 		<rebuild-on-change property="datasourceMeta.id">

+ 15 - 1
public/app/features/datasources/partials/http_settings.html

@@ -6,13 +6,27 @@
 	<div class="gf-form">
 	<div class="gf-form">
 		<span class="gf-form-label width-7">Url</span>
 		<span class="gf-form-label width-7">Url</span>
 		<input class="gf-form-input max-width-21" type="text" ng-model='current.url' placeholder="http://my.server.com:8080" ng-pattern="/^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/" required></input>
 		<input class="gf-form-input max-width-21" type="text" ng-model='current.url' placeholder="http://my.server.com:8080" ng-pattern="/^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/" required></input>
+
+		<gf-popover>
+			<p>Specify a complete HTTP url (http://your_server:8080)</p>
+			<span ng-show="current.access === 'direct'">
+				Your access method is <code>Direct</code>, this means the url
+				needs to be accessable from the browser.
+			</span>
+			<span ng-show="current.access === 'proxy'">
+				Your access method is currently <code>Proxy</code>, this means the url
+				needs to be accessable from the grafana backend.
+			</span>
+		</gf-popover>
 	</div>
 	</div>
 
 
 	<div class="gf-form">
 	<div class="gf-form">
 		<span class="gf-form-label width-7">
 		<span class="gf-form-label width-7">
 			Access <tip>Direct = url is used directly from browser, Proxy = Grafana backend will proxy the request</tip>
 			Access <tip>Direct = url is used directly from browser, Proxy = Grafana backend will proxy the request</tip>
 		</span>
 		</span>
-		<select class="gf-form-input gf-size-auto" ng-model="current.access" ng-options="f for f in ['direct', 'proxy']"></select>
+		<div class="gf-form-select-wrapper">
+			<select class="gf-form-input gf-size-auto" ng-model="current.access" ng-options="f for f in ['direct', 'proxy']"></select>
+		</div>
 	</div>
 	</div>
 
 
 	<div class="gf-form">
 	<div class="gf-form">

+ 2 - 2
public/app/features/datasources/partials/list.html

@@ -22,8 +22,8 @@
 		<table class="filter-table" ng-if="ctrl.datasources.length > 0">
 		<table class="filter-table" ng-if="ctrl.datasources.length > 0">
 			<thead>
 			<thead>
 				<tr>
 				<tr>
-					<th><strong>Name</strong></th>
-					<th><strong>Url</strong></th>
+					<th><strong>name</strong></th>
+					<th><strong>url</strong></th>
 					<th style="width: 60px;"></th>
 					<th style="width: 60px;"></th>
 					<th style="width: 85px;"></th>
 					<th style="width: 85px;"></th>
 					<th style="width: 44px;"></th>
 					<th style="width: 44px;"></th>

+ 7 - 14
public/app/features/org/partials/apikeyModal.html

@@ -10,23 +10,16 @@
 		</button>
 		</button>
 	</div>
 	</div>
 
 
-	<div class="gf-box-body" style="min-height: 0px;">
+	<div class="gf-box-body">
 
 
-		<div class="tight-form last">
-			<ul class="tight-form-list">
-				<li class="tight-form-item">
-					<strong>Key</strong>
-				</li>
-				<li class="tight-form-item last">
-					{{key}}
-				</li>
-			</ul>
-			<div class="clearfix"></div>
+		<div class="gf-form-group">
+			<div class="gf-form">
+				<span class="gf-form-label">Key</span>
+				<span class="gf-form-label">{{key}}</span>
+			</div>
 		</div>
 		</div>
-		<br>
-		<br>
 
 
-		<div class="grafana-info-box" style="text-align: left">
+		<div class="grafana-info-box" style="border: 0;">
 			You will only be able to view this key here once! It is not stored in this form. So be sure to copy it now.
 			You will only be able to view this key here once! It is not stored in this form. So be sure to copy it now.
 			<br>
 			<br>
 			<br>
 			<br>

+ 7 - 15
public/app/features/org/partials/newOrg.html

@@ -6,24 +6,16 @@
 
 
 		<h2 style="margin-top: 30px;">Add Organization</h2>
 		<h2 style="margin-top: 30px;">Add Organization</h2>
 
 
-		<form name="form">
-			<div class="tight-form last">
-				<ul class="tight-form-list">
-					<li class="tight-form-item" style="width: 100px;">
-						<strong>Org. name</strong>
-					</li>
-					<li>
-						<input type="text" ng-model="newOrg.name" required class="input-xxlarge tight-form-input last" placeholder="organization name">
-					</li>
-					<li>
-					</li>
-				</ul>
-				<div class="clearfix"></div>
+		<form name="form" class="gf-form-group">
+			<div class="gf-form">
+				<span class="gf-form-label width-10">Org. name</span>
+				<input type="text" ng-model="newOrg.name" required class="gf-form-input" placeholder="organization name">
 			</div>
 			</div>
 			<br>
 			<br>
-			<button class="btn btn-success pull-right" ng-click="createOrg()">Create</button>
+			<div class="gf-form-buttons-row">
+				<button class="btn btn-success pull-right" ng-click="createOrg()">Create</button>
+			</div>
 		</form>
 		</form>
-
 	</div>
 	</div>
 </div>
 </div>
 
 

+ 1 - 1
public/app/features/org/partials/orgDetails.html

@@ -1,4 +1,4 @@
-<navbar icon="icon-gf icon-gf-users" title="Organization">
+<navbar icon="icon-gf icon-gf-users" title="Organization" title-url="org">
 </navbar>
 </navbar>
 
 
 <div class="page-container">
 <div class="page-container">

+ 1 - 1
public/app/features/org/partials/orgUsers.html

@@ -1,4 +1,4 @@
-<navbar icon="icon-gf icon-gf-users" title="Organization Users" title-url="org">
+<navbar icon="icon-gf icon-gf-users" title="Organization Users" title-url="org/users">
 </navbar>
 </navbar>
 
 
 <div class="page-container">
 <div class="page-container">

+ 16 - 13
public/app/features/panel/panel_directive.ts

@@ -28,24 +28,27 @@ var panelTemplate = `
   </div>
   </div>
 
 
   <div class="panel-full-edit" ng-if="ctrl.editMode">
   <div class="panel-full-edit" ng-if="ctrl.editMode">
-    <div class="gf-box">
-      <div class="gf-box-header">
-        <div class="gf-box-title">
+    <div class="tabbed-view tabbed-view--panel-edit">
+      <div class="tabbed-view-header">
+        <h2 class="tabbed-view-title">
           <i ng-class="ctrl.icon"></i>
           <i ng-class="ctrl.icon"></i>
           {{ctrl.pluginName}}
           {{ctrl.pluginName}}
-        </div>
-
-        <div ng-model="ctrl.editorTabIndex" bs-tabs>
-          <div ng-repeat="tab in ctrl.editorTabs" data-title="{{tab.title}}">
-          </div>
-        </div>
-
-        <button class="gf-box-header-close-btn" ng-click="ctrl.exitFullscreen();">
-          Back to dashboard
+        </h2>
+
+        <ul class="gf-tabs">
+          <li class="gf-tabs-item" ng-repeat="tab in ::ctrl.editorTabs">
+            <a class="gf-tabs-link" ng-click="ctrl.editorTabIndex = $index" ng-class="{active: ctrl.editorTabIndex === $index}">
+              {{::tab.title}}
+            </a>
+          </li>
+        </ul>
+
+        <button class="tabbed-view-close-btn" ng-click="ctrl.exitFullscreen();">
+          <i class="fa fa-remove"></i>
         </button>
         </button>
       </div>
       </div>
 
 
-      <div class="gf-box-body">
+      <div class="tabbed-view-body">
         <div ng-repeat="tab in ctrl.editorTabs" ng-if="ctrl.editorTabIndex === $index">
         <div ng-repeat="tab in ctrl.editorTabs" ng-if="ctrl.editorTabIndex === $index">
           <panel-editor-tab editor-tab="tab" ctrl="ctrl" index="$index"></panel-editor-tab>
           <panel-editor-tab editor-tab="tab" ctrl="ctrl" index="$index"></panel-editor-tab>
         </div>
         </div>

+ 31 - 56
public/app/features/panel/partials/panelTime.html

@@ -1,59 +1,34 @@
-<div class="editor-row">
-	<div class="section tight-form-container" style="margin-bottom: 20px">
-		<div class="tight-form">
-			<ul class="tight-form-list">
-				<li class="tight-form-item tight-form-item-icon">
-					<i class="fa fa-clock-o"></i>
-				</li>
-				<li class="tight-form-item" style="width: 178px">
-					<strong>Override relative time</strong>
-				</li>
-				<li class="tight-form-item" style="width: 50px">
-					Last
-				</li>
-				<li>
-					<input type="text" class="input-small tight-form-input last" placeholder="1h"
-					  empty-to-null ng-model="ctrl.panel.timeFrom" valid-time-span
-					  ng-change="ctrl.refresh()" ng-model-onblur>
-				</li>
-			</ul>
-			<div class="clearfix"></div>
-		</div>
-		<div class="tight-form">
-			<ul class="tight-form-list">
-				<li class="tight-form-item tight-form-item-icon">
-					<i class="fa fa-clock-o"></i>
-				</li>
-				<li class="tight-form-item" style="width: 178px">
-					<strong>Add time shift</strong>
-				</li>
-				<li class="tight-form-item" style="width: 50px">
-					Amount
-				</li>
-				<li>
-					<input type="text" class="input-small tight-form-input last" placeholder="1h"
-					empty-to-null ng-model="ctrl.panel.timeShift" valid-time-span
-					ng-change="ctrl.refresh()" ng-model-onblur>
-				</li>
-			</ul>
-			<div class="clearfix"></div>
-		</div>
-		<div class="tight-form">
-			<ul class="tight-form-list">
-				<li class="tight-form-item tight-form-item-icon">
-					<i class="fa fa-clock-o"></i>
-				</li>
-				<li class="tight-form-item" style="width: 178px">
-					<strong>Hide time override info</strong>
-				</li>
-				<li class="tight-form-item last">
-					<input class="cr1" id="ctrl.panel.hideTimeOverride" type="checkbox"
-					ng-model="ctrl.panel.hideTimeOverride" ng-checked="ctrl.panel.hideTimeOverride" ng-change="ctrl.refresh()">
-					<label for="ctrl.panel.hideTimeOverride" class="cr1"></label>
-				</li>
-			</ul>
-			<div class="clearfix"></div>
-		</div>
+<div class="gf-form-group">
+	<div class="gf-form">
+		<span class="gf-form-label">
+			<i class="fa fa-clock-o"></i>
+		</span>
+
+		<span class="gf-form-label width-12">Override relative time</span>
+		<span class="gf-form-label width-6">Last</span>
+
+		<input type="text" class="gf-form-input max-width-8" placeholder="1h"
+			empty-to-null ng-model="ctrl.panel.timeFrom" valid-time-span
+			ng-change="ctrl.refresh()" ng-model-onblur>
+	</div>
+
+	<div class="gf-form">
+		<span class="gf-form-label">
+			<i class="fa fa-clock-o"></i>
+		</span>
+		<span class="gf-form-label width-12">Add time shift</span>
+		<span class="gf-form-label width-6">Amount</span>
+		<input type="text" class="gf-form-input max-width-8" placeholder="1h"
+			empty-to-null ng-model="ctrl.panel.timeShift" valid-time-span
+			ng-change="ctrl.refresh()" ng-model-onblur>
+	</div>
+
+	<div class="gf-form">
+		<span class="gf-form-label">
+			<i class="fa fa-clock-o"></i>
+		</span>
+		<editor-checkbox text="Hide time override info" model="ctrl.panel.hideTimeOverride" change="ctrl.refresh()"></editor-checkbox>
 	</div>
 	</div>
 </div>
 </div>
 
 
+

+ 49 - 66
public/app/features/panellinks/module.html

@@ -1,84 +1,67 @@
 <div class="editor-row">
 <div class="editor-row">
-  <div class="section">
-		<h5>Drilldown / detail link<tip>These links appear in the dropdown menu in the panel menu. </tip></h5>
+	<h5 class="section-heading">
+		Drilldown / detail link<tip>These links appear in the dropdown menu in the panel menu. </tip></h5>
+	</h5>
 
 
-		<div ng-repeat="link in panel.links" style="margin-top: 20px;">
-			<div class="tight-form">
-				<ul class="tight-form-list pull-right">
-					<li class="tight-form-item">
-						<i ng-click="moveLink($index, -1)" ng-hide="$first" class="pointer fa fa-arrow-up"></i>
-						<i ng-click="moveLink($index, 1)" ng-hide="$last" class="pointer fa fa-fw fa-arrow-down"></i>
-					</li>
-					<li class="tight-form-item last">
-						<i class="fa fa-remove pointer" ng-click="deleteLink(link)"></i>
-					</li>
-				</ul>
+	<div ng-repeat="link in panel.links" style="margin-top: 20px;">
+		<div class="gf-form-group">
 
 
-				<ul class="tight-form-list">
-					<li class="tight-form-item" style="width: 20px">
-						<i class="fa fa-fw fa-unlink"></i>
-					</li>
+			<div class="gf-form-inline">
+				<div class="gf-form width-2">
+					<i class="fa fa-fw fa-unlink"></i>
+				</div>
 
 
-					<li class="tight-form-item">Type</li>
-					<li>
-						<select class="input-medium tight-form-input" style="width: 150px;" ng-model="link.type" ng-options="f for f in ['dashboard','absolute']"></select>
-					</li>
+				<div class="gf-form">
+					<span class="gf-form-label width-7">Type</span>
+					<div class="gf-form-select-wrapper width-14">
+						<select class="gf-form-input" ng-model="link.type" ng-options="f for f in ['dashboard','absolute']"></select>
+					</div>
+				</div>
 
 
-					<li class="tight-form-item" ng-show="link.type === 'dashboard'" style="width: 73px;">Dashboard</li>
-					<li ng-show="link.type === 'dashboard'">
-						<input type="text" ng-model="link.dashboard" bs-typeahead="searchDashboards" class="input-large tight-form-input" ng-blur="dashboardChanged(link)">
-					</li>
+				<div class="gf-form">
+					<span class="gf-form-label width-7" ng-show="link.type === 'dashboard'">Dashboard</span>
+					<input ng-show="link.type === 'dashboard'" type="text" ng-model="link.dashboard" bs-typeahead="searchDashboards" class="gf-form-input max-width-14" ng-blur="dashboardChanged(link)">
 
 
-					<li class="tight-form-item" ng-show="link.type === 'absolute'" style="width: 73px;">Url</li>
-					<li ng-show="link.type === 'absolute'">
-						<input type="text" ng-model="link.url" class="input-large tight-form-input">
-					</li>
+					<span class="gf-form-label width-7" ng-show="link.type === 'absolute'">Url</span>
+					<input ng-show="link.type === 'absolute'" type="text" ng-model="link.url" class="gf-form-input max-width-14">
+				</div>
 
 
-				</ul>
-				<div class="clearfix"></div>
+				<div class="gf-form">
+					<button class="btn-inverse gf-form-btn btn-small" ng-click="deleteLink(link)"><i class="fa fa-trash"></i></button>
+				</div>
 			</div>
 			</div>
 
 
-			<div class="tight-form">
-				<ul class="tight-form-list">
-					<li class="tight-form-item" style="width: 20px">
-						<i class="fa fa-fw fa-unlink invisible"></i>
-					</li>
-					<li class="tight-form-item" style="width: 31px">Title</li>
-					<li>
-						<input type="text" ng-model="link.title" class="input-medium tight-form-input">
-					</li>
-					<li class="tight-form-item" style="width: 73px;">
-						Url params
-					</li>
-					<li>
-						<input type="text" ng-model="link.params" class="input-large tight-form-input">
-					</li>
-				</ul>
-				<div class="clearfix"></div>
+			<div class="gf-form-inline">
+				<div class="gf-form width-2">
+					<i class="fa fa-fw fa-unlink invisible"></i>
+				</div>
+
+				<div class="gf-form">
+					<div class="gf-form-label width-7">Title</div>
+					<input type="text" ng-model="link.title" class="gf-form-input">
+				</div>
+
+				<div class="gf-form">
+					<span class="gf-form-label width-7">Url params</span>
+					<input type="text" ng-model="link.params" class="gf-form-input">
+				</div>
 			</div>
 			</div>
-			<div class="tight-form last">
-				<ul class="tight-form-list">
-					<li class="tight-form-item" style="width: 20px">
-						<i class="fa fa-fw fa-unlink invisible"></i>
-					</li>
-					<li class="tight-form-item">
-						<editor-checkbox text="Keep current time range" model="link.keepTime"></editor-checkbox>
-					</li>
-					<li class="tight-form-item">
-						<editor-checkbox text="Add current variable values" model="link.includeVars"></editor-checkbox>
-					</li>
-					<li class="tight-form-item last">
-						<editor-checkbox text="Open in new tab " model="link.targetBlank"></editor-checkbox>
-					</li>
-				</ul>
-				<div class="clearfix"></div>
+
+			<div class="gf-form-inline">
+				<div class="gf-form width-2">
+					<i class="fa fa-fw fa-unlink invisible"></i>
+				</div>
+
+				<div class="gf-form">
+					<editor-checkbox text="Keep current time range" model="link.keepTime"></editor-checkbox>
+					<editor-checkbox text="Add current variable values" model="link.includeVars"></editor-checkbox>
+					<editor-checkbox text="Open in new tab " model="link.targetBlank"></editor-checkbox>
+				</div>
 			</div>
 			</div>
 		</div>
 		</div>
-
 	</div>
 	</div>
 </div>
 </div>
 
 
 <div class="editor-row">
 <div class="editor-row">
-	<br>
 	<button class="btn btn-inverse" ng-click="addLink()"><i class="fa fa-plus"></i> Add link</button>
 	<button class="btn btn-inverse" ng-click="addLink()"><i class="fa fa-plus"></i> Add link</button>
 </div>
 </div>

+ 0 - 1
public/app/features/panellinks/module.js

@@ -52,6 +52,5 @@ function (angular, _) {
       $scope.deleteLink = function(link) {
       $scope.deleteLink = function(link) {
         $scope.panel.links = _.without($scope.panel.links, link);
         $scope.panel.links = _.without($scope.panel.links, link);
       };
       };
-
     });
     });
 });
 });

+ 46 - 27
public/app/features/playlist/partials/playlist.html

@@ -1,4 +1,4 @@
-<navbar icon="fa fa-fw fa-list" title="Playlist">
+<navbar icon="fa fa-fw fa-list" title="Playlists" title-url="playlists">
 </navbar>
 </navbar>
 
 
 <div class="page-container" ng-form="playlistEditForm">
 <div class="page-container" ng-form="playlistEditForm">
@@ -7,6 +7,8 @@
 		<h1 ng-show="!ctrl.isNew()">Edit Playlist</h1>
 		<h1 ng-show="!ctrl.isNew()">Edit Playlist</h1>
 	</div>
 	</div>
 
 
+	<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>
+
 	<div class="gf-form-group">
 	<div class="gf-form-group">
 		<div class="gf-form">
 		<div class="gf-form">
 			<span class="gf-form-label width-7">Name</span>
 			<span class="gf-form-label width-7">Name</span>
@@ -19,24 +21,29 @@
 	</div>
 	</div>
 
 
 	<div class="gf-form-group">
 	<div class="gf-form-group">
-		<div class="max-width-28">
-			<h5 class="page-headering">Add dashboards</h5>
-			<div style="">
-				<playlist-search class="playlist-search-container" search-started="ctrl.searchStarted(promise)"></playlist-search>
-			</div>
-		</div>
+		<h3 class="page-headering">Dashboards</h3>
 	</div>
 	</div>
 
 
 	<div class="row">
 	<div class="row">
 		<div class="col-md-6">
 		<div class="col-md-6">
-			<h5>Search results ({{ctrl.filteredDashboards.length + ctrl.filteredTags.length}})</h5>
+			<div class="playlist-search-containerwrapper">
+				<div class="max-width-32">
+					<h5 class="page-headering playlist-column-header">Available</h5>
+					<div style="">
+						<playlist-search class="playlist-search-container" search-started="ctrl.searchStarted(promise)"></playlist-search>
+					</div>
+				</div>
+			</div>
+
 			<div ng-if="ctrl.filteredDashboards.length > 0">
 			<div ng-if="ctrl.filteredDashboards.length > 0">
-				<table class="grafana-options-table">
+				<table class="grafana-options-table playlist-available-list">
 					<tr ng-repeat="playlistItem in ctrl.filteredDashboards">
 					<tr ng-repeat="playlistItem in ctrl.filteredDashboards">
-						<td style="white-space: nowrap;">
-							{{playlistItem.title}}
+						<td>
+							<i class="icon-gf icon-gf-dashboard"></i>
+							&nbsp;&nbsp;{{playlistItem.title}}
+							<i class="fa fa-star" ng-show="playlistItem.isStarred"></i>
 						</td>
 						</td>
-						<td style="text-align: center">
+						<td class="add-dashboard">
 							<button class="btn btn-inverse btn-mini pull-right" ng-click="ctrl.addPlaylistItem(playlistItem)">
 							<button class="btn btn-inverse btn-mini pull-right" ng-click="ctrl.addPlaylistItem(playlistItem)">
 								<i class="fa fa-plus"></i>
 								<i class="fa fa-plus"></i>
 								Add to playlist
 								Add to playlist
@@ -46,31 +53,40 @@
 				</table>
 				</table>
 			</div>
 			</div>
 			<div class="playlist-search-results-container" ng-if="ctrl.filteredTags.length > 0;">
 			<div class="playlist-search-results-container" ng-if="ctrl.filteredTags.length > 0;">
-					<div ng-repeat="tag in ctrl.filteredTags" class="pointer" style="width: 180px; float: left;"
-						ng-click="ctrl.addTagPlaylistItem(tag, $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>
+				<table class="grafana-options-table playlist-available-list">
+					<tr ng-repeat="tag in ctrl.filteredTags">
+						<td>
+							<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>
+						</td>
+						<td class="add-dashboard">
+							<button class="btn btn-inverse btn-mini pull-right" ng-click="ctrl.addPlaylistItem(playlistItem)">
+								<i class="fa fa-plus"></i>
+								Add to playlist
+							</button>
+						</td>
+					</tr>
+				</table>
+			</div>
 		</div>
 		</div>
 
 
 		<div class="col-md-6">
 		<div class="col-md-6">
-			<h5>Added dashboards</h5>
-			<table class="grafana-options-table">
+			<h5 class="page headering playlist-column-header">Selected</h5>
+			<table class="grafana-options-table playlist-available-list">
 				<tr ng-repeat="playlistItem in ctrl.playlistItems">
 				<tr ng-repeat="playlistItem in ctrl.playlistItems">
-					<td style="white-space: nowrap;" ng-if="playlistItem.type === 'dashboard_by_id'">
-						{{playlistItem.title}}
+					<td ng-if="playlistItem.type === 'dashboard_by_id'">
+						<i class="icon-gf icon-gf-dashboard"></i>&nbsp;&nbsp;{{playlistItem.title}}
 					</td>
 					</td>
-					<td style="white-space: nowrap;"  ng-if="playlistItem.type === 'dashboard_by_tag'">
+					<td ng-if="playlistItem.type === 'dashboard_by_tag'">
 						<a class="search-result-tag label label-tag" tag-color-from-name="playlistItem.title">
 						<a class="search-result-tag label label-tag" tag-color-from-name="playlistItem.title">
 							<i class="fa fa-tag"></i>
 							<i class="fa fa-tag"></i>
 							<span>{{playlistItem.title}}</span>
 							<span>{{playlistItem.title}}</span>
 						</a>
 						</a>
 					</td>
 					</td>
 
 
-					<td style="text-align: right">
+					<td class="selected-playlistitem-settings">
 						<button class="btn btn-inverse btn-mini" ng-hide="$first" ng-click="ctrl.movePlaylistItemUp(playlistItem)">
 						<button class="btn btn-inverse btn-mini" ng-hide="$first" ng-click="ctrl.movePlaylistItemUp(playlistItem)">
 							<i class="fa fa-arrow-up"></i>
 							<i class="fa fa-arrow-up"></i>
 						</button>
 						</button>
@@ -89,7 +105,10 @@
 	<div class="clearfix"></div>
 	<div class="clearfix"></div>
 
 
 	<div class="gf-form-button-row">
 	<div class="gf-form-button-row">
-		<a class="btn btn-success"
+		<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()"
 			ng-disabled="ctrl.playlistEditForm.$invalid || ctrl.isPlaylistEmpty()"
 			ng-disabled="ctrl.playlistEditForm.$invalid || ctrl.isPlaylistEmpty()"
 			ng-click="ctrl.savePlaylist(ctrl.playlist, ctrl.playlistItems)">Save</a>
 			ng-click="ctrl.savePlaylist(ctrl.playlist, ctrl.playlistItems)">Save</a>
 		<a class="btn-text" ng-click="ctrl.backToList()">Cancel</a>
 		<a class="btn-text" ng-click="ctrl.backToList()">Cancel</a>

+ 1 - 1
public/app/features/playlist/partials/playlists.html

@@ -1,4 +1,4 @@
-<navbar icon="fa fa-fw fa-list" title="Playlists">
+<navbar icon="fa fa-fw fa-list" title="Playlists" title-url="playlists">
 </navbar>
 </navbar>
 
 
 <div class="page-container">
 <div class="page-container">

+ 1 - 1
public/app/features/playlist/playlist_edit_ctrl.ts

@@ -11,7 +11,7 @@ export class PlaylistEditCtrl {
   searchQuery: string = '';
   searchQuery: string = '';
   loading: boolean = false;
   loading: boolean = false;
   playlist: any = {
   playlist: any = {
-    interval: '10m',
+    interval: '5m',
   };
   };
   playlistItems: any = [];
   playlistItems: any = [];
   dashboardresult: any = [];
   dashboardresult: any = [];

+ 0 - 0
public/app/features/apps/all.ts → public/app/features/plugins/all.ts


+ 51 - 0
public/app/features/plugins/edit_ctrl.ts

@@ -0,0 +1,51 @@
+///<reference path="../../headers/common.d.ts" />
+
+import angular from 'angular';
+import _ from 'lodash';
+
+export class PluginEditCtrl {
+  model: any;
+  pluginId: any;
+  includedPanels: any;
+  includedDatasources: any;
+  tabIndex: number;
+
+  /** @ngInject */
+  constructor(private backendSrv: any, private $routeParams: any) {
+    this.model = {};
+    this.pluginId = $routeParams.pluginId;
+    this.tabIndex = 0;
+
+    this.backendSrv.get(`/api/org/plugins/${this.pluginId}/settings`).then(result => {
+      this.model = result;
+      this.includedPanels = _.where(result.includes, {type: 'panel'});
+      this.includedDatasources = _.where(result.includes, {type: 'datasource'});
+    });
+  }
+
+  update() {
+    var updateCmd = _.extend({
+      pluginId: this.model.pluginId,
+      orgId: this.model.orgId,
+      enabled: this.model.enabled,
+      pinned: this.model.pinned,
+      jsonData: this.model.jsonData,
+      secureJsonData: this.model.secureJsonData,
+    }, {});
+
+    this.backendSrv.post(`/api/org/plugins/${this.pluginId}/settings`, updateCmd).then(function() {
+      window.location.href = window.location.href;
+    });
+  }
+
+  toggleEnabled() {
+    this.update();
+  }
+
+  togglePinned() {
+    this.update();
+  }
+}
+
+angular.module('grafana.controllers').controller('PluginEditCtrl', PluginEditCtrl);
+

+ 17 - 0
public/app/features/plugins/list_ctrl.ts

@@ -0,0 +1,17 @@
+///<reference path="../../headers/common.d.ts" />
+
+import angular from 'angular';
+
+export class PluginListCtrl {
+  plugins: any[];
+
+  /** @ngInject */
+  constructor(private backendSrv: any) {
+
+    this.backendSrv.get('api/org/plugins').then(plugins => {
+      this.plugins = plugins;
+    });
+  }
+}
+
+angular.module('grafana.controllers').controller('PluginListCtrl', PluginListCtrl);

+ 0 - 0
public/app/features/apps/page_ctrl.ts → public/app/features/plugins/page_ctrl.ts


+ 185 - 0
public/app/features/plugins/partials/edit.html

@@ -0,0 +1,185 @@
+<navbar title="Plugins" title-url="plugins" icon="icon-gf icon-gf-apps">
+	<a href="plugins/apps" class="navbar-page-btn">
+		<i class="fa fa-chevron-right"></i>
+		Apps
+	</a>
+</navbar>
+
+<div class="page-container">
+  <div class="plugin-header">
+		<span ng-show="ctrl.model.info.logos.large" class="plugin-header-logo">
+			<img 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="icon-gf icon-gf-apps"></i> {{ctrl.model.type}}
+				</span>
+			</div>
+		</div>
+	</div>
+
+	<ul class="nav nav-tabs nav-tabs-alt">
+		<li ng-repeat="tab in ::['Overview', 'Details', 'Config']" ng-class="{active: ctrl.tabIndex === $index}">
+			<a ng-click="ctrl.tabIndex= $index">
+				{{::tab}}
+			</a>
+		</li>
+	</ul>
+
+	<div class="page-body">
+		<div class="tab-content page-content-with-sidebar" ng-if="ctrl.tabIndex === 0">
+			README.md
+		</div>
+
+		<div class="tab-content page-content-with-sidebar" ng-if="ctrl.tabIndex === 1">
+			Details
+		</div>
+
+		<div class="tab-content page-content-with-sidebar" ng-if="ctrl.tabIndex === 2">
+			<div class="gf-form-inline">
+				<div class="gf-form">
+					<editor-checkbox text="Enabled" model="ctrl.model.enabled" change="ctrl.toggleEnabled()"></editor-checkbox>
+				</div>
+				<div class="gf-form">
+					<editor-checkbox text="Pinned" model="ctrl.model.pinned" change="ctrl.togglePinned()"></editor-checkbox>
+				</div>
+			</div>
+
+			<div ng-if="ctrl.model.pluginId">
+				<plugin-component type="app-config-ctrl"></plugin-component>
+				<div class="clearfix"></div>
+				<button type="submit" class="btn btn-success" ng-click="ctrl.update()">Save</button>
+			</div>
+
+		</div>
+
+		<aside class="page-sidebar">
+			<section class="page-sidebar-section">
+				<h4>Version</h4>
+				<span>1.0.1</span>
+			</section>
+			<section class="page-sidebar-section" ng-show="ctrl.model.type === 'app'">
+				<h5>Includes</h4>
+				<ul class="ui-list">
+					<li ng-show="!ctrl.includedPanels.length"><em>None</em></li>
+					<li ng-repeat="panel in ctrl.includedPanels">
+						{{panel.name}}
+					</li>
+					<li ng-repeat="ds in ctrl.includedDatasources">
+						{{ds.name}}
+					</li>
+					<li ng-repeat="page in ctrl.model.pages">
+						<a href="apps/{{ctrl.appId}}/page/{{page.slug}}" class="external-link">{{page.name}}</a>
+					</li>
+				</ul>
+			</section>
+			<section class="page-sidebar-section">
+				<h5>Dependencies</h4>
+				<span>TODO</span>
+			</section>
+			<section class="page-sidebar-section">
+				<h5>Links</h4>
+				<ul class="ui-list">
+					<li ng-repeat="link in ctrl.model.info.links">
+						<a href="{{link.url}}" class="external-link" target="_blank">{{link.name}}</a>
+					</li>
+				</ul>
+			</section>
+		</aside>
+	</div>
+</div>
+</div>
+
+<!-- 	<div class="app&#45;edit&#45;description"> -->
+	<!-- 		{{ctrl.model.info.description}}<br> -->
+	<!-- 		<span style="small"> -->
+		<!-- 			Version: {{ctrl.model.info.version}} &#38;nbsp; &#38;nbsp; Updated: {{ctrl.model.info.updated}} -->
+		<!-- 		</span> -->
+	<!-- 	</div> -->
+<!--  -->
+<!-- </div> -->
+		<!-- <div class="flex&#45;column"> -->
+			<!-- 	<ul class="app&#45;edit&#45;links"> -->
+				<!-- 		<li> -->
+					<!-- 			By <a href="{{ctrl.model.info.author.url}}" class="external&#45;link" target="_blank">{{ctrl.model.info.author.name}}</a> -->
+					<!-- 		</li> -->
+				<!-- 		<li ng&#45;repeat="link in ctrl.model.info.links"> -->
+					<!-- 			<a href="{{link.url}}" class="external&#45;link" target="_blank">{{link.name}}</a> -->
+					<!-- 		</li> -->
+				<!-- 	</ul> -->
+			<!-- </div> -->
+
+		<!-- <section class="simple&#45;box"> -->
+			<!-- 	<h3 class="simple&#45;box&#45;header">Included with app:</h3> -->
+			<!-- 	<div class="flex&#45;container"> -->
+				<!-- 		<div class="simple&#45;box&#45;body simple&#45;box&#45;column"> -->
+					<!-- 			<div class="simple&#45;box&#45;column&#45;header"> -->
+						<!-- 				<i class="fa fa&#45;th&#45;large"></i> -->
+						<!-- 				Dashboards -->
+						<!-- 			</div> -->
+					<!-- 			<ul> -->
+						<!-- 				<li><em class="small">None</em></li> -->
+						<!-- 			</ul> -->
+					<!-- 		</div> -->
+				<!-- 		<div class="simple&#45;box&#45;body simple&#45;box&#45;column"> -->
+					<!-- 			<div class="simple&#45;box&#45;column&#45;header"> -->
+						<!-- 				<i class="fa fa&#45;line&#45;chart"></i> -->
+						<!-- 				Panels -->
+						<!-- 			</div> -->
+					<!-- 			<ul> -->
+						<!-- 				<li ng&#45;show="!ctrl.includedPanels.length"><em class="small">None</em></li> -->
+						<!-- 				<li ng&#45;repeat="panel in ctrl.includedPanels"> -->
+							<!-- 					{{panel.name}} -->
+							<!-- 				</li> -->
+						<!-- 			</ul> -->
+					<!-- 		</div> -->
+				<!-- 		<div class="simple&#45;box&#45;body simple&#45;box&#45;column"> -->
+					<!-- 			<div class="simple&#45;box&#45;column&#45;header"> -->
+						<!-- 				<i class="fa fa&#45;database"></i> -->
+						<!-- 				Datasources -->
+						<!-- 			</div> -->
+					<!-- 			<ul> -->
+						<!-- 				<li ng&#45;show="!ctrl.includedDatasources.length"><em class="small">None</em></li> -->
+						<!-- 				<li ng&#45;repeat="ds in ctrl.includedDatasources"> -->
+							<!-- 					{{ds.name}} -->
+							<!-- 				</li> -->
+						<!-- 			</ul> -->
+					<!-- 		</div> -->
+				<!-- 		<div class="simple&#45;box&#45;body simple&#45;box&#45;column"> -->
+					<!-- 			<div class="simple&#45;box&#45;column&#45;header"> -->
+						<!-- 				<i class="fa fa&#45;files&#45;o"></i> -->
+						<!-- 				Pages -->
+						<!-- 			</div> -->
+					<!-- 			<ul> -->
+						<!-- 				<li ng&#45;repeat="page in ctrl.model.pages"> -->
+							<!-- 					<a href="apps/{{ctrl.appId}}/page/{{page.slug}}" class="external&#45;link">{{page.name}}</a> -->
+							<!-- 				</li> -->
+						<!-- 			</ul> -->
+					<!-- 		</div> -->
+				<!--  -->
+				<!-- 	</div> -->
+			<!-- </section> -->
+		<!--  -->
+		<!-- <section class="simple&#45;box"> -->
+			<!-- 	<h3 class="simple&#45;box&#45;header">Dependencies:</h3> -->
+			<!-- 	<div class="simple&#45;box&#45;body"> -->
+				<!-- 		Grafana 2.6.x -->
+				<!-- 	</div> -->
+			<!-- </section> -->
+		<!--  -->
+		<!-- <section class="simple&#45;box"> -->
+			<!-- 	<h3 class="simple&#45;box&#45;header">Configuration:</h3> -->
+			<!-- 	<div class="simple&#45;box&#45;body"> -->
+				<!-- 		<div ng&#45;if="ctrl.model.appId"> -->
+					<!-- 			<plugin&#45;component type="app&#45;config&#45;ctrl"></plugin&#45;component> -->
+					<!-- 			<div class="clearfix"></div> -->
+					<!-- 			<button type="submit" class="btn btn&#45;success" ng&#45;click="ctrl.update()">Save</button> -->
+					<!-- 		</div> -->
+				<!-- 	</div> -->
+			<!-- </section> -->
+		<!--  -->
+		<!--  -->
+		<!-- </div> -->

+ 42 - 0
public/app/features/plugins/partials/list.html

@@ -0,0 +1,42 @@
+<navbar title="Plugins" icon="icon-gf icon-gf-apps" title-url="plugins">
+</navbar>
+
+<div class="page-container">
+  <div class="page-header">
+    <h1>Plugins</h1>
+	</div>
+
+	<table class="filter-table">
+		<thead>
+			<tr>
+				<th><strong>Name</strong></th>
+				<th><strong>Type</strong></th>
+				<th style="width: 60px;"></th>
+				<th style="width: 80px;"></th>
+			</tr>
+		</thead>
+		<tbody>
+			<tr ng-repeat="plugin in ctrl.plugins">
+				<td>
+					<a href="plugins/{{plugin.pluginId}}/edit">
+						{{plugin.name}}
+					</a>
+				</td>
+				<td>
+					{{plugin.type}}
+				</td>
+				<td>
+					<span class="label label-info" ng-if="plugin.enabled">Enabled</span>
+					<span class="label label-info" ng-if="plugin.pinned">Pinned</span>
+				</td>
+				<td class="text-right">
+					<a href="plugins/{{plugin.pluginId}}/edit" class="btn btn-inverse btn-small">
+							<i class="fa fa-edit"></i>
+							Edit
+						</a>
+					</td>
+				</tr>
+			</tbody>
+		</table>
+	</div>
+</div>

+ 0 - 0
public/app/features/apps/partials/page.html → public/app/features/plugins/partials/page.html


+ 1 - 1
public/app/features/profile/partials/profile.html

@@ -1,4 +1,4 @@
-<navbar icon="icon-gf icon-gf-users" title="Profile">
+<navbar icon="icon-gf icon-gf-users" title="Profile" title-url="profile">
 </navbar>
 </navbar>
 
 
 <div class="page-container">
 <div class="page-container">

+ 31 - 31
public/app/features/snapshot/partials/snapshots.html

@@ -1,40 +1,40 @@
-<navbar icon="icon-gf icon-gf-snapshot" title="Snapshots">
+<navbar icon="icon-gf icon-gf-snapshot" title="Snapshots" title-url="dashboard/snapshots">
 </navbar>
 </navbar>
 
 
 <div class="page-container">
 <div class="page-container">
-  <div class="page-wide">
-
+  <div class="page-header">
     <h1>Available snapshots</h1>
     <h1>Available snapshots</h1>
+  </div>
 
 
-     <table class="filter-table" style="margin-top: 20px">
-      <thead>
-        <th><strong>Name</strong></th>
-        <th><strong>Snapshot url</strong></th>
-        <th style="width: 70px"></th>
-        <th style="width: 25px"></th>
 
 
-     </thead>
+   <table class="filter-table" style="margin-top: 20px">
+    <thead>
+      <th><strong>Name</strong></th>
+      <th><strong>Snapshot url</strong></th>
+      <th style="width: 70px"></th>
+      <th style="width: 25px"></th>
 
 
-      <tr ng-repeat="snapshot in ctrl.snapshots">
-        <td>
-					<a href="dashboard/snapshot/{{snapshot.key}}">{{snapshot.name}}</a>
-        </td>
-        <td >
-          <a href="dashboard/snapshot/{{snapshot.key}}">dashboard/snapshot/{{snapshot.key}}</a>
-        </td>
-        <td class="text-center">
-          <a href="dashboard/snapshot/{{snapshot.key}}" class="btn btn-inverse btn-mini">
-            <i class="fa fa-eye"></i>
-            View
-          </a>
-        </td>
-        <td  class="text-right">
-          <a ng-click="ctrl.removeSnapshot(snapshot)" class="btn btn-danger btn-mini">
-            <i class="fa fa-remove"></i>
-          </a>
-        </td>
-      </tr>
-    </table>
+   </thead>
+
+    <tr ng-repeat="snapshot in ctrl.snapshots">
+      <td>
+				<a href="dashboard/snapshot/{{snapshot.key}}">{{snapshot.name}}</a>
+      </td>
+      <td >
+        <a href="dashboard/snapshot/{{snapshot.key}}">dashboard/snapshot/{{snapshot.key}}</a>
+      </td>
+      <td class="text-center">
+        <a href="dashboard/snapshot/{{snapshot.key}}" class="btn btn-inverse btn-mini">
+          <i class="fa fa-eye"></i>
+          View
+        </a>
+      </td>
+      <td  class="text-right">
+        <a ng-click="ctrl.removeSnapshot(snapshot)" class="btn btn-danger btn-mini">
+          <i class="fa fa-remove"></i>
+        </a>
+      </td>
+    </tr>
+  </table>
 
 
-  </div>
 </div>
 </div>

+ 1 - 1
public/app/features/styleguide/styleguide.html

@@ -1,4 +1,4 @@
-<navbar icon="fa fa-fw fa-adjust" title="Style Guide">
+<navbar icon="fa fa-fw fa-adjust" title="Style Guide" title-url="styleguide">
 </navbar>
 </navbar>
 
 
 <div class="page-container">
 <div class="page-container">

+ 174 - 288
public/app/features/templating/partials/editor.html

@@ -1,332 +1,218 @@
 <div ng-controller="TemplateEditorCtrl" ng-init="init()">
 <div ng-controller="TemplateEditorCtrl" ng-init="init()">
-	<div class="gf-box-header">
-		<div class="gf-box-title">
-			<i class="fa fa-code"></i>
+	<div class="tabbed-view-header">
+		<h2 class="tabbed-view-title">
 			Templating
 			Templating
-		</div>
-
-		<div class="tabs">
-			<ul class="nav nav-tabs">
-				<li ng-class="{active: mode === 'list'}">
-					<a ng-click="mode = 'list';">
-						Variables
-					</a>
-				</li>
-
-				<li ng-class="{active: mode === 'edit'}" ng-show="mode === 'edit'">
-					<a>
-						{{current.name}}
-					</a>
-				</li>
-
-				<li ng-class="{active: mode === 'new'}">
-					<a ng-click="mode = 'new';">
-						<i class="fa fa-plus"></i>
-						New
-					</a>
-				</li>
-			</ul>
-		</div>
-
-		<button class="gf-box-header-close-btn" ng-click="dismiss();dashboard.refresh();">
+		</h2>
+
+		<ul class="gf-tabs">
+			<li class="gf-tabs-item" >
+				<a class="gf-tabs-link" ng-click="mode = 'list';" ng-class="{active: mode === 'list'}">
+					Variables
+				</a>
+			</li>
+			<li class="gf-tabs-item" ng-show="mode === 'edit'">
+				<a class="gf-tabs-link" ng-class="{active: mode === 'edit'}">
+					{{current.name}}
+				</a>
+			</li>
+			<li class="gf-tabs-item" ng-show="mode === 'new'">
+				<span class="active gf-tabs-link">New</span>
+			</li>
+		</ul>
+
+		<button class="tabbed-view-close-btn" ng-click="dismiss();dashboard.refresh();">
 			<i class="fa fa-remove"></i>
 			<i class="fa fa-remove"></i>
 		</button>
 		</button>
-
 	</div>
 	</div>
 
 
-	<div class="gf-box-body">
+	<div class="tabbed-view-body">
 
 
 		<div ng-if="mode === 'list'">
 		<div ng-if="mode === 'list'">
+			<div ng-if="variables.length === 0">
+				<em>No template variables defined</em>
+			</div>
+			<table class="grafana-options-table">
+				<tr ng-repeat="variable in variables">
+					<td style="width: 1%">
+						<span class="template-variable">
+							${{variable.name}}
+						</span>
+					</td>
+					<td class="max-width" style="max-width: 200px;">
+						{{variable.query}}
+					</td>
+
+					<td style="width: 1%"><i ng-click="_.move(variables,$index,$index-1)" ng-hide="$first" class="pointer fa fa-arrow-up"></i></td>
+					<td style="width: 1%"><i ng-click="_.move(variables,$index,$index+1)" ng-hide="$last" class="pointer fa fa-arrow-down"></i></td>
+					<td style="width: 1%">
+            <a ng-click="duplicate(variable)" class="btn btn-inverse btn-mini">
+              Duplicate
+            </a>
+          </td>
+					<td style="width: 1%">
+						<a ng-click="edit(variable)" class="btn btn-inverse btn-mini">
+							<i class="fa fa-edit"></i>
+							Edit
+						</a>
+					</td>
+					<td style="width: 1%">
+						<a ng-click="removeVariable(variable)" class="btn btn-danger btn-mini">
+							<i class="fa fa-remove"></i>
+						</a>
+					</td>
+				</tr>
+			</table>
+		</div>
 
 
-			<div class="editor-row row">
-				<div style="max-width: 1024px">
-					<div ng-if="variables.length === 0">
-						<em>No template variables defined</em>
-					</div>
-					<table class="grafana-options-table">
-						<tr ng-repeat="variable in variables">
-							<td style="width: 1%">
-								<span class="template-variable">
-									${{variable.name}}
-								</span>
-							</td>
-							<td class="max-width" style="max-width: 200px;">
-								{{variable.query}}
-							</td>
-							<td style="width: 1%">
-								<a ng-click="edit(variable)" class="btn btn-inverse btn-small">
-									<i class="fa fa-edit"></i>
-									Edit
-								</a>
-							</td>
-              <td style="width: 1%">
-                <a ng-click="duplicate(variable)" class="btn btn-inverse btn-small">
-                  Duplicate
-                </a>
-              </td>
-							<td style="width: 1%"><i ng-click="_.move(variables,$index,$index-1)" ng-hide="$first" class="pointer fa fa-arrow-up"></i></td>
-							<td style="width: 1%"><i ng-click="_.move(variables,$index,$index+1)" ng-hide="$last" class="pointer fa fa-arrow-down"></i></td>
-							<td style="width: 1%">
-								<a ng-click="removeVariable(variable)" class="btn btn-danger btn-small">
-									<i class="fa fa-remove"></i>
-								</a>
-							</td>
-						</tr>
-					</table>
-				</div>
+		<div class="gf-form" ng-show="mode === 'list'">
+			<div class="gf-form-button-row">
+				<a type="button" class="btn gf-form-button btn-success" ng-click="mode = 'new';"><i class="fa fa-plus" ></i>&nbsp;&nbsp;New</a>
 			</div>
 			</div>
 		</div>
 		</div>
 
 
 		<div ng-if="mode === 'edit' || mode === 'new'">
 		<div ng-if="mode === 'edit' || mode === 'new'">
-			<div class="editor-row">
-				<div class="tight-form-section">
-					<h5>Variable</h5>
-					<div class="tight-form last">
-						<ul class="tight-form-list">
-							<li class="tight-form-item" style="width: 100px">
-								Name
-							</li>
-							<li>
-								<input type="text" class="input-large tight-form-input" placeholder="name" ng-model='current.name'></input>
-							</li>
-							<li class="tight-form-item">
-								Type
-							</li>
-							<li>
-								<select class="input-small tight-form-input" ng-model="current.type" ng-options="f for f in ['query', 'interval', 'custom']" ng-change="typeChanged()"></select>
-							</li>
-							<li class="tight-form-item" ng-show="current.type === 'query'">
-								Data source
-							</li>
-							<li ng-show="current.type === 'query'">
-								<select class="input input-medium tight-form-input last" ng-model="current.datasource" ng-options="f.value as f.name for f in datasources"></select>
-							</li>
-						</ul>
-						<div class="clearfix"></div>
+			<h5 class="section-heading">Variable</h5>
+			<div class="gf-form-group">
+				<div class="gf-form-inline">
+					<div class="gf-form">
+						<span class="gf-form-label width-7">Name</span>
+						<input type="text" class="gf-form-input max-width-14" placeholder="name" ng-model='current.name'></input>
 					</div>
 					</div>
-				</div>
-			</div>
-
-			<div class="editor-row">
-				<div class="tight-form-section">
-					<h5>Value Options</h5>
-
-					<div ng-show="current.type === 'interval'">
-						<div class="tight-form">
-							<ul class="tight-form-list">
-								<li class="tight-form-item" style="width: 160px">
-									Values
-								</li>
-								<li>
-									<input type="text" style="width: 345px" class="input-xxlarge tight-form-input last" placeholder="name" ng-model='current.query' placeholder="1m,10m,1h,6h,1d,7d" ng-model-onblur ng-change="runQuery()"></input>
-								</li>
-							</ul>
-							<div class="clearfix"></div>
-						</div>
-						<div class="tight-form last">
-							<ul class="tight-form-list">
-								<li class="tight-form-item" style="width: 160px">
-									<editor-checkbox text="Include auto interval" model="current.auto" change="runQuery()"></editor-checkbox>
-								</li>
-								<li class="tight-form-item" ng-show="current.auto">
-									Auto interval steps <tip>How many times should the current time range be divided to calculate the value</tip>
-								</li>
-								<li>
-									<select class="input-mini tight-form-input last" ng-model="current.auto_count" ng-options="f for f in [3,5,10,30,50,100,200]" ng-change="runQuery()"></select>
-								</li>
-								<li class="tight-form-item" ng-show="current.auto">
-									Auto interval min value <tip>The calculated value will not go below this threshold</tip>
-								</li>
-								<li>
-									<input type="text" style="width: 35px" class="input-xxlarge tight-form-input last" ng-model="current.auto_min" ng-change="runQuery()"></input>
-								</li>
-							</ul>
-							<div class="clearfix"></div>
+					<div class="gf-form">
+						<span class="gf-form-label width-7">Type</span>
+						<div class="gf-form-select-wrapper max-width-10">
+							<select class="gf-form-input  max-width-10" ng-model="current.type" ng-options="f for f in ['query', 'interval', 'custom']" ng-change="typeChanged()"></select>
 						</div>
 						</div>
 					</div>
 					</div>
-
-					<div ng-show="current.type === 'custom'">
-						<div class="tight-form last">
-							<ul class="tight-form-list">
-								<li class="tight-form-item" style="width: 180px">
-									Values seperated by comma
-								</li>
-								<li>
-									<input type="text" class="input tight-form-input last" style="width: 325px;" ng-model='current.query' ng-blur="runQuery()" placeholder="1, 10, 20, myvalue"></input>
-								</li>
-							</ul>
-							<div class="clearfix"></div>
-						</div>
-            <div class="tight-form">
-              <ul class="tight-form-list">
-                <li class="tight-form-item" style="width: 100px;">
-                  <editor-checkbox text="All value" model="current.includeAll" change="runQuery()"></editor-checkbox>
-                </li>
-                <li ng-show="current.includeAll">
-                  <input type="text" class="input-xlarge tight-form-input" style="width:364px" ng-model='current.options[0].value'></input>
-                </li>
-                <li class="tight-form-item" ng-show="current.includeAll">
-                  All format
-                </li>
-                <li ng-show="current.includeAll">
-                  <select class="input-medium tight-form-input last" ng-model="current.allFormat" ng-change="runQuery()" ng-options="f for f in ['glob', 'wildcard', 'regex wildcard', 'regex values', 'lucene', 'pipe']"></select>
-                </li>
-              </ul>
-              <div class="clearfix"></div>
-            </div>
-					</div>
-
-					<div ng-show="current.type === 'query'">
-
-						<div class="tight-form">
-							<ul class="tight-form-list">
-								<li class="tight-form-item" style="width: 100px">
-									Query
-								</li>
-								<li>
-									<input type="text" style="width: 588px" class="input-xxlarge tight-form-input last" ng-model='current.query' placeholder="metric name or tags query" ng-model-onblur ng-change="runQuery()"></input>
-								</li>
-							</ul>
-							<div class="clearfix"></div>
-						</div>
-						<div class="tight-form">
-							<ul class="tight-form-list">
-								<li class="tight-form-item" style="width: 100px;">
-									Regex
-									<tip>Optional, if you want to extract part of a series name or metric node segment</tip>
-								</li>
-								<li>
-									<input type="text" style="width: 588px" class="input tight-form-input last" ng-model='current.regex' placeholder="/.*-(.*)-.*/" ng-model-onblur ng-change="runQuery()"></input>
-								</li>
-							</ul>
-							<div class="clearfix"></div>
-						</div>
-
-						<div class="tight-form">
-							<ul class="tight-form-list">
-								<li class="tight-form-item" style="width: 100px;">
-									<editor-checkbox text="All value" model="current.includeAll" change="runQuery()"></editor-checkbox>
-								</li>
-								<li ng-show="current.includeAll">
-									<input type="text" class="input-xlarge tight-form-input" style="width:364px" ng-model='current.options[0].value'></input>
-								</li>
-								<li class="tight-form-item" ng-show="current.includeAll">
-									All format
-								</li>
-								<li ng-show="current.includeAll">
-									<select class="input-medium tight-form-input last" ng-model="current.allFormat" ng-change="runQuery()" ng-options="f for f in ['glob', 'wildcard', 'regex wildcard', 'regex values', 'lucene', 'pipe']"></select>
-								</li>
-							</ul>
-							<div class="clearfix"></div>
-						</div>
-
-						<div class="tight-form last">
-							<ul class="tight-form-list">
-								<li class="tight-form-item last">
-									<editor-checkbox text="Refresh on load" model="current.refresh"></editor-checkbox>
-									<tip>Check if you want values to be updated on dashboard load, will slow down dashboard load time</tip>
-								</li>
-							</ul>
-							<div class="clearfix"></div>
+					<div class="gf-form">
+						<span class="gf-form-label width-7" ng-show="current.type === 'query'">Data source</span>
+						<div class="gf-form-select-wrapper" ng-show="current.type === 'query'">
+							<select class="gf-form-input max-width-14" ng-model="current.datasource" ng-options="f.value as f.name for f in datasources"></select>
 						</div>
 						</div>
 					</div>
 					</div>
 				</div>
 				</div>
+				<div class="gf-form">
+					<span class="gf-form-label width-7">Label</span>
+					<input type="text" class="gf-form-input max-width-14" ng-model='current.label' placeholder="optional display name"></input>
+					<editor-checkbox class="width-13" text="Hide label" model="current.hideLabel" change="runQuery()"></editor-checkbox>
+				</div>
 			</div>
 			</div>
 
 
-			<div class="editor-row">
-				<div class="tight-form-section" ng-hide="current.type === 'interval'">
-					<h5>Multi-value selection <tip>Enables multiple values to be selected at the same time</tip></h5>
-					<div class="tight-form last">
-						<ul class="tight-form-list">
-							<li class="tight-form-item last" style="width: 100px;">
-								<editor-checkbox text="Enable" model="current.multi" change="runQuery()"></editor-checkbox>
-							</li>
-							<li class="tight-form-item" ng-show="current.multi">
-								Multi format
-							</li>
-							<li ng-show="current.multi">
-								<select class="input-medium tight-form-input last" ng-model="current.multiFormat" ng-change="runQuery()" ng-options="f for f in ['glob', 'regex values', 'lucene', 'pipe']"></select>
-							</li>
-						</ul>
-						<div class="clearfix"></div>
+			<h5 class="section-heading">Value Options</h5>
+			<div ng-show="current.type === 'interval'" class="gf-form-group">
+				<div class="gf-form">
+					<span class="gf-form-label width-7">Values</span>
+					<input type="text" class="gf-form-input max-width-28" placeholder="name" ng-model='current.query' placeholder="1m,10m,1h,6h,1d,7d" ng-model-onblur ng-change="runQuery()"></input>
+				</div>
+				<div class="gf-form">
+					<editor-checkbox text="Include auto interval" model="current.auto" change="runQuery()"></editor-checkbox>
+					<span class="gf-form-label" ng-show="current.auto">
+						Auto interval steps <tip>How many times should the current time range be divided to calculate the value</tip>
+					</span>
+					<div class="gf-form-select-wrapper max-width-10" ng-show="current.auto">
+						<select class="gf-form-input" ng-model="current.auto_count" ng-options="f for f in [3,5,10,30,50,100,200]" ng-change="runQuery()"></select>
 					</div>
 					</div>
 				</div>
 				</div>
+				<div class="gf-form">
+					<span class="gf-form-label" ng-show="current.auto">
+						Auto interval min value <tip>The calculated value will not go below this threshold</tip>
+					</span>
+					<input type="text" class="gf-form-input max-width-10" ng-show="current.auto" ng-model="current.auto_min" ng-change="runQuery()"></input>
+				</div>
+			</div>
 
 
-				<div class="tight-form-section">
-					<h5>Display options</h5>
-					<div class="tight-form last">
-						<ul class="tight-form-list">
-							<li class="tight-form-item" style="width: 100px">
-								Variable Label
-							</li>
-							<li>
-								<input type="text" class="input-medium tight-form-input" ng-model='current.label' placeholder=""></input>
-							</li>
-							<li class="tight-form-item last">
-								<editor-checkbox text="Hide label" model="current.hideLabel" change="runQuery()"></editor-checkbox>
-							</li>
-						</ul>
-						<div class="clearfix"></div>
+			<div ng-show="current.type === 'custom'" class="gf-form-group">
+				<div class="gf-form">
+					<span class="gf-form-label width-13">Values seperated by comma</span>
+					<input type="text" class="gf-form-input max-width-22" ng-model='current.query' ng-blur="runQuery()" placeholder="1, 10, 20, myvalue"></input>
+				</div>
+				<div class="gf-form ">
+					<editor-checkbox class="width-13" text="All value" model="current.includeAll" change="runQuery()"></editor-checkbox>
+					<input ng-show="current.includeAll" type="text" class="gf-form-input max-width-22" ng-model='current.options[0].value' style="margin-left: 4px;"></input>
+				</div>
+				<div class="gf-form">
+					<span class="gf-form-label width-13" ng-show="current.includeAll">All format</span>
+					<div class="gf-form-select-wrapper max-width-10" ng-show="current.includeAll">
+						<select class="gf-form-input" ng-model="current.allFormat" ng-change="runQuery()" ng-options="f for f in ['glob', 'wildcard', 'regex wildcard', 'regex values', 'lucene', 'pipe']"></select>
 					</div>
 					</div>
 				</div>
 				</div>
 			</div>
 			</div>
 
 
-			<div class="editor-row" ng-if="current.type === 'query'">
-				<div class="tight-form-section">
-					<h5>Value groups/tags (Experimental feature)</h5>
-					<div class="tight-form last" ng-if="current.useTags">
-						<ul class="tight-form-list">
-							<li class="tight-form-item" style="width: 135px">
-								Tags query
-							</li>
-							<li>
-								<input type="text" style="width: 588px" class="input-xxlarge tight-form-input last" ng-model='current.tagsQuery' placeholder="metric name or tags query" ng-model-onblur></input>
-							</li>
-						</ul>
-						<div class="clearfix"></div>
-					</div>
-					<div class="tight-form" ng-if="current.useTags">
-						<ul class="tight-form-list">
-							<li class="tight-form-item" style="width: 135px;">
-								Tag values query
-							</li>
-							<li>
-								<input type="text" style="width: 588px" class="input tight-form-input last" ng-model='current.tagValuesQuery' placeholder="apps.$tag.*" ng-model-onblur></input>
-							</li>
-						</ul>
-						<div class="clearfix"></div>
+			<div ng-show="current.type === 'query'" class="gf-form-group">
+				<div class="gf-form">
+					<span class="gf-form-label width-7">Query</span>
+					<input type="text" class="gf-form-input" ng-model='current.query' placeholder="metric name or tags query" ng-model-onblur ng-change="runQuery()"></input>
+				</div>
+				<div class="gf-form">
+					<span class="gf-form-label width-7">
+						Regex
+						<tip>Optional, if you want to extract part of a series name or metric node segment</tip>
+					</span>
+					<input type="text" class="gf-form-input" ng-model='current.regex' placeholder="/.*-(.*)-.*/" ng-model-onblur ng-change="runQuery()"></input>
+				</div>
+				<div class="gf-form">
+					<span class="gf-form-label width-7">All value</span>
+					<editor-checkbox class="width-13" text="Enable" model="current.includeAll" change="runQuery()"></editor-checkbox>
+				</div>
+				<div class="gf-form-inline" ng-show="current.includeAll">
+					<div class="gf-form">
+						<span class="gf-form-label width-7">All format</span>
+						<div class="gf-form-select-wrapper">
+							<select class="gf-form-input" ng-model="current.allFormat" ng-change="runQuery()" ng-options="f for f in ['glob', 'wildcard', 'regex wildcard', 'regex values', 'lucene', 'pipe']"></select>
+						</div>
 					</div>
 					</div>
-					<div class="tight-form">
-						<ul class="tight-form-list">
-							<li class="tight-form-item last">
-								<editor-checkbox text="Enable" model="current.useTags" change="runQuery()"></editor-checkbox>
-							</li>
-						</ul>
-						<div class="clearfix"></div>
+					<div class="gf-form max-width-30">
+						<span class="gf-form-label width-7">All value</span>
+						<input type="text" class="gf-form-input" ng-model='current.options[0].value'></input>
 					</div>
 					</div>
 				</div>
 				</div>
+				<div class="gf-form">
+					<span class="gf-form-label width-7">Update</span>
+					<editor-checkbox text="On Dashboard Load" model="current.refresh"></editor-checkbox>
+					<tip>Check if you want values to be updated on dashboard load, will slow down dashboard load time</tip>
+				</div>
 			</div>
 			</div>
 
 
-			<div class="editor-row">
-				<div class="tight-form-section">
-					<h5>Preview of values (shows max 20)</h5>
-					<div class="tight-form last">
-						<ul class="tight-form-list">
-							<li class="tight-form-item" ng-repeat="option in current.options | limitTo: 20">
-								{{option.text}}
-							</li>
-						</ul>
-						<div class="clearfix"></div>
+			<div class="gf-form-group" >
+				<h5 class="section-heading">Multi-value selection <tip>Enables multiple values to be selected at the same time</tip></h5>
+				<div class="gf-form">
+					<editor-checkbox text="Enable" model="current.multi" change="runQuery()"></editor-checkbox>
+					<span class="gf-form-label" ng-show="current.multi">Multi format</span>
+					<div class="gf-form-select-wrapper max-width-10" ng-show="current.multi">
+						<select class="gf-form-input" ng-model="current.multiFormat" ng-change="runQuery()" ng-options="f for f in ['glob', 'regex values', 'lucene', 'pipe']"></select>
 					</div>
 					</div>
 				</div>
 				</div>
 			</div>
 			</div>
+
+			<div class="gf-form-group" ng-if="current.type === 'query'">
+				<h5>Value groups/tags (Experimental feature)</h5>
+				<div class="gf-form">
+					<editor-checkbox text="Enable" model="current.useTags" change="runQuery()"></editor-checkbox>
+				</div>
+				<div class="gf-form last" ng-if="current.useTags">
+					<span class="gf-form-label width-10">Tags query</span>
+					<input type="text" class="gf-form-input" ng-model='current.tagsQuery' placeholder="metric name or tags query" ng-model-onblur></input>
+				</div>
+				<div class="gf-form" ng-if="current.useTags">
+					<li class="gf-form-label width-10">Tag values query</li>
+					<input type="text" class="gf-form-input" ng-model='current.tagValuesQuery' placeholder="apps.$tag.*" ng-model-onblur></input>
+				</div>
+			</div>
+
+			<div class="gf-form-group">
+				<h5>Preview of values (shows max 20)</h5>
+				<div class="gf-form">
+					<span class="gf-form-label" ng-repeat="option in current.options | limitTo: 20">
+						{{option.text}}
+					</span>
+				</div>
+			</div>
 		</div>
 		</div>
 
 
-		<div class="editor-row" style="margin-top: 20px">
+		<div class="gf-form-button-row p-y-0">
 			<button type="button" class="btn btn-success" ng-show="mode === 'edit'" ng-click="update();">Update</button>
 			<button type="button" class="btn btn-success" ng-show="mode === 'edit'" ng-click="update();">Update</button>
 			<button type="button" class="btn btn-success" ng-show="mode === 'new'" ng-click="add();">Add</button>
 			<button type="button" class="btn btn-success" ng-show="mode === 'new'" ng-click="add();">Add</button>
 		</div>
 		</div>
-
 	</div>
 	</div>
 </div>
 </div>
 
 

+ 8 - 0
public/app/headers/common.d.ts

@@ -39,4 +39,12 @@ declare module 'app/core/store' {
   export default store;
   export default store;
 }
 }
 
 
+declare module 'tether' {
+  var config: any;
+  export default config;
+}
 
 
+declare module 'tether-drop' {
+  var config: any;
+  export default config;
+}

+ 2 - 1
public/app/partials/bootstrap/tabset.html

@@ -1,5 +1,6 @@
 <div>
 <div>
-  <ul class="nav nav-{{type || 'tabs'}} nav-tabs-alt" ng-class="{'nav-stacked': vertical, 'nav-justified': justified}" ng-transclude></ul>
+	<ul class="nav nav-tabs" ng-class="{'nav-stacked': vertical, 'nav-justified': justified}" ng-transclude>
+	</ul>
   <div class="tab-content">
   <div class="tab-content">
     <div class="tab-pane"
     <div class="tab-pane"
          ng-repeat="tab in tabs"
          ng-repeat="tab in tabs"

+ 0 - 11
public/app/partials/colorpicker.html

@@ -1,11 +0,0 @@
-<div class="graph-legend-popover">
-	<a class="close" ng-click="dismiss();" href="">×</a>
-
-	<div class="editor-row">
-		<i ng-repeat="color in colors" class="pointer fa fa-circle"
-			ng-style="{color:color}"
-			ng-click="colorSelected(color);dismiss();">&nbsp;</i>
-	</div>
-
-</div>
-

+ 12 - 13
public/app/partials/edit_json.html

@@ -1,22 +1,21 @@
 <div ng-controller="JsonEditorCtrl">
 <div ng-controller="JsonEditorCtrl">
+	<div class="tabbed-view-header">
+		<h2 class="tabbed-view-title">
+			JSON
+		</h2>
 
 
-	<div class="gf-box-header">
-		<div class="gf-box-title">
-			<i class="fa fa-edit"></i>
-		  JSON
-		</div>
-
-		<button class="gf-box-header-close-btn" ng-click="dismiss();">
+		<button class="tabbed-view-close-btn" ng-click="dismiss()">
 			<i class="fa fa-remove"></i>
 			<i class="fa fa-remove"></i>
 		</button>
 		</button>
 	</div>
 	</div>
 
 
-	<div class="gf-box-body" style="height: 500px">
-		<textarea ng-model="json" rows="20" spellcheck="false" style="width: 100%;"></textarea>
-		<br>
-		<br>
+	<div class="tabbed-view-body">
+		<div class="gf-form">
+			<textarea class="gf-form-input" ng-model="json" rows="20" spellcheck="false"></textarea>
+		</div>
 
 
-		<button type="button" class="btn btn-success" ng-show="canUpdate" ng-click="update(); dismiss();">Update</button>
+		<div class="gf-form-button-row">
+			<button type="button" class="btn btn-success" ng-show="canUpdate" ng-click="update(); dismiss();">Update</button>
+		</div>
 	</div>
 	</div>
-
 </div>
 </div>

+ 8 - 0
public/app/partials/help_modal.html

@@ -32,6 +32,10 @@
 				<td><span class="label label-info">CTRL+S</span></td>
 				<td><span class="label label-info">CTRL+S</span></td>
 				<td>Save dashboard</td>
 				<td>Save dashboard</td>
 			</tr>
 			</tr>
+      <tr>
+        <td><span class="label label-info">CTRL+E</span></td>
+        <td>Export dashboard</td>
+      </tr>
 			<tr>
 			<tr>
 				<td><span class="label label-info">CTRL+H</span></td>
 				<td><span class="label label-info">CTRL+H</span></td>
 				<td>Hide row controls</td>
 				<td>Hide row controls</td>
@@ -40,6 +44,10 @@
 				<td><span class="label label-info">CTRL+Z</span></td>
 				<td><span class="label label-info">CTRL+Z</span></td>
 				<td>Zoom out</td>
 				<td>Zoom out</td>
 			</tr>
 			</tr>
+      <tr>
+        <td><span class="label label-info">CTRL+I</span></td>
+        <td>Quick snapshot</td>
+      </tr>
 			<tr>
 			<tr>
 				<td><span class="label label-info">CTRL+O</span></td>
 				<td><span class="label label-info">CTRL+O</span></td>
 				<td>Enable/Disable shared graph crosshair</td>
 				<td>Enable/Disable shared graph crosshair</td>

+ 1 - 5
public/app/partials/panelgeneral.html

@@ -8,7 +8,7 @@
 			<span class="gf-form-label width-6">Span</span>
 			<span class="gf-form-label width-6">Span</span>
 			<select class="gf-form-input gf-size-auto" ng-model="ctrl.panel.span" ng-options="f for f in [0,1,2,3,4,5,6,7,8,9,10,11,12]"></select>
 			<select class="gf-form-input gf-size-auto" ng-model="ctrl.panel.span" ng-options="f for f in [0,1,2,3,4,5,6,7,8,9,10,11,12]"></select>
 		</div>
 		</div>
-		<div class="gf-form max-width-21">
+		<div class="gf-form max-width-26">
 			<span class="gf-form-label width-8">Height</span>
 			<span class="gf-form-label width-8">Height</span>
 			<input type="text" class="gf-form-input max-width-6" ng-model='ctrl.panel.height' placeholder="100px"></input>
 			<input type="text" class="gf-form-input max-width-6" ng-model='ctrl.panel.height' placeholder="100px"></input>
 			<editor-checkbox text="Transparent" model="ctrl.panel.transparent"></editor-checkbox>
 			<editor-checkbox text="Transparent" model="ctrl.panel.transparent"></editor-checkbox>
@@ -31,10 +31,6 @@
 	</div>
 	</div>
 </div>
 </div>
 
 
-<br>
-<br>
-<br>
-
 <panel-links-editor panel="ctrl.panel"></panel-links-editor>
 <panel-links-editor panel="ctrl.panel"></panel-links-editor>
 
 
 
 

+ 20 - 47
public/app/partials/reset_password.html

@@ -1,11 +1,10 @@
-<div class="container">
+<div class="login-container container">
 
 
 	<div class="login-box">
 	<div class="login-box">
 
 
 		<div class="login-box-logo">
 		<div class="login-box-logo">
-			<a href="login">
-				<img src="img/logo_transparent_200x75.png">
-			</a>
+			<img class="logo-icon" src="public/img/grafana_icon.svg"></img><br>
+			<i class="icon-gf icon-gf-grafana_wordmark"></i>
 		</div>
 		</div>
 
 
 		<div class="login-inner-box">
 		<div class="login-inner-box">
@@ -15,76 +14,50 @@
 				</button>
 				</button>
 			</div>
 			</div>
 
 
-			<form name="sendResetForm" class="login-form" ng-show="mode === 'send'">
-				<div class="tight-form last">
-					<ul class="tight-form-list">
-						<li class="tight-form-item" style="width: 78px">
-							<strong>User</strong>
-						</li>
-						<li>
-							<input type="text" name="username" class="tight-form-input last" required ng-model='formModel.userOrEmail' placeholder="email or username" style="width: 253px">
-						</li>
-					</ul>
-					<div class="clearfix"></div>
+			<form name="sendResetForm" class="login-form gf-form-group" ng-show="mode === 'send'">
+				<div class="gf-form">
+						<span class="gf-form-label width-7">User</span>
+						<input type="text" name="username" class="gf-form-input max-width-14" required ng-model='formModel.userOrEmail' placeholder="email or username">
 				</div>
 				</div>
 
 
-				<div class="login-submit-button-row">
-					<button type="submit" class="btn" ng-click="sendResetEmail();" ng-class="{'btn-inverse': !sendResetForm.$valid, 'btn-primary': sendResetForm.$valid}">
+				<div class="gf-form-button-row">
+					<button type="submit" class="btn btn-large" ng-click="sendResetEmail();" ng-class="{'btn-inverse': !sendResetForm.$valid, 'btn-primary': sendResetForm.$valid}">
 						Send reset instructions
 						Send reset instructions
 					</button>
 					</button>
 				</div>
 				</div>
 			</form>
 			</form>
 
 
-			<h5 ng-if="mode === 'email-sent'" style="text-align: center; padding: 20px;">
+			<h5 style="text-align: center; padding: 20px;" ng-if="mode === 'email-sent'">
 				An email with a reset link as been sent to the email address, you should receive it shortly.
 				An email with a reset link as been sent to the email address, you should receive it shortly.
 			</h5>
 			</h5>
 
 
-			<form name="resetForm" class="login-form" ng-show="mode === 'reset'">
-				<div class="tight-form">
-					<ul class="tight-form-list">
-						<li class="tight-form-item" style="width: 125px">
-							<strong>New Password</strong>
-						</li>
-						<li>
-							<input type="password" name="NewPassword" class="tight-form-input last" required ng-minlength="4" ng-model='formModel.newPassword' placeholder="password" style="width: 207px" watch-change="formModel.newPassword = inputValue;">
-						</li>
-					</ul>
-					<div class="clearfix"></div>
+			<form name="resetForm" class="login-form gf-form-group" ng-show="mode === 'reset'">
+				<div class="gf-form">
+					<span class="gf-form-label width-10">New Password</span>
+					<input type="password" name="NewPassword" class="gf-form-input max-width-14" required ng-minlength="4" ng-model='formModel.newPassword' placeholder="password" watch-change="formModel.newPassword = inputValue;">
 				</div>
 				</div>
-				<div class="tight-form last">
-					<ul class="tight-form-list">
-						<li class="tight-form-item" style="width: 125px">
-							<strong>Confirm Password</strong>
-						</li>
-						<li>
-							<input type="password" name="ConfirmPassword" class="tight-form-input last" required ng-minlength="4" ng-model='formModel.confirmPassword' placeholder="confirm password" style="width: 207px">
-						</li>
-					</ul>
-					<div class="clearfix"></div>
+				<div class="gf-form">
+					<span class="gf-form-label width-10">Confirm Password</span>
+					<input type="password" name="ConfirmPassword" class="gf-form-input max-width-14" required ng-minlength="4" ng-model='formModel.confirmPassword' placeholder="confirm password">
 				</div>
 				</div>
 
 
 				<div style="margin-left: 141px; width: 207px;">
 				<div style="margin-left: 141px; width: 207px;">
 					<password-strength password="formModel.newPassword"></password-strength>
 					<password-strength password="formModel.newPassword"></password-strength>
 				</div>
 				</div>
 
 
-				<div class="login-submit-button-row">
+				<div class="gf-form-button-row">
 					<button type="submit" class="btn" ng-click="submitReset();" ng-class="{'btn-inverse': !resetForm.$valid, 'btn-primary': resetForm.$valid}">
 					<button type="submit" class="btn" ng-click="submitReset();" ng-class="{'btn-inverse': !resetForm.$valid, 'btn-primary': resetForm.$valid}">
 						Reset Password
 						Reset Password
 					</button>
 					</button>
 				</div>
 				</div>
 			</form>
 			</form>
-
-			<div class="clearfix"></div>
 		</div>
 		</div>
 
 
-		<div class="row" style="margin-top: 40px">
+		<div class="row" style="margin-top: 20px">
 			<div class="text-center">
 			<div class="text-center">
-				<a href="login">
-					Back to login
-				</a>
+				<a href="login">Back to login</a>
 			</div>
 			</div>
 		</div>
 		</div>
-
 	</div>
 	</div>
 </div>
 </div>
 
 

+ 33 - 50
public/app/partials/roweditor.html

@@ -1,64 +1,47 @@
-
-<div class="gf-box-header">
-	<div class="gf-box-title">
-		<i class="fa fa-th-list"></i>
+<div class="tabbed-view-header">
+	<h2 class="tabbed-view-title">
 		Row settings
 		Row settings
-	</div>
-
-	<div ng-model="editor.index" bs-tabs style="text-transform:capitalize;">
-		<div ng-repeat="tab in ['General']" data-title="{{tab}}">
-		</div>
-	</div>
+	</h2>
 
 
-	<button class="gf-box-header-close-btn" ng-click="dismiss();">
+	<button class="tabbed-view-close-btn" ng-click="dismiss();">
 		<i class="fa fa-remove"></i>
 		<i class="fa fa-remove"></i>
 	</button>
 	</button>
 </div>
 </div>
 
 
-<div class="gf-box-body">
+<div class="tabbed-view-body">
+	<div class="row">
+		<div class="col-md-8">
+			<div class="page-heading">
+				<h5>Row details</h5>
+			</div>
+			<div class="gf-form-group">
+				<div class="gf-form-inline">
 
 
-	<div class="editor-row">
-		<div class="section">
-			<h5>Row details</h5>
-			<div class="tight-form last">
-				<ul class="tight-form-list">
-					<li class="tight-form-item">
-						Title
-					</li>
-					<li>
-						<input type="text" class="input-xlarge tight-form-input" ng-model='row.title'></input>
-					</li>
-					<li class="tight-form-item">
-						Height
-					</li>
-					<li>
-						<input type="text" class="input-small tight-form-input" ng-model='row.height'></input>
-					</li>
-					<li class="tight-form-item last">
-						<label class="checkbox-label" for="row.showTitle">Show Title</label>
-						<input class="cr1" id="row.showTitle" type="checkbox" ng-model="row.showTitle" ng-checked="row.showTitle">
-						<label for="row.showTitle" class="cr1"></label>
-					</li>
-				</ul>
-				<div class="clearfix"></div>
+					<div class="gf-form">
+						<span class="gf-form-label width-6">Title</span>
+						<input type="text" class="gf-form-input max-width-14" ng-model='row.title'></input>
+					</div>
+					<div class="gf-form">
+						<span class="gf-form-label width-6">Height</span>
+						<input type="text" class="gf-form-input max-width-8" ng-model='row.height'></input>
+						<editor-checkbox text="Show Title" model="row.showTitle"></editor-checkbox>
+					</div>
+				</div>
 			</div>
 			</div>
 		</div>
 		</div>
-		<div class="section">
-			<h5>Templating options</h5>
-			<div class="tight-form last">
-				<ul class="tight-form-list">
-					<li class="tight-form-item">
-						Repeat Row
-					</li>
-					<li>
-						<select class="input-small tight-form-input last" ng-model="row.repeat" ng-options="f.name as f.name for f in dashboard.templating.list">
+		<div class="col-md-4">
+			<div class="page-heading">
+				<h5>Templating options</h5>
+			</div>
+			<div class="gf-form-group">
+				<div class="gf-form">
+					<span class="gf-form-label">Repeat Row</span>
+					<div class="gf-form-select-wrapper max-width-10">
+						<select class="gf-form-input" ng-model="row.repeat" ng-options="f.name as f.name for f in dashboard.templating.list">
 							<option value=""></option>
 							<option value=""></option>
-						</select>
-					</li>
-				</ul>
-				<div class="clearfix"></div>
+					</div>
+				</div>
 			</div>
 			</div>
 		</div>
 		</div>
 	</div>
 	</div>
-
 </div>
 </div>

+ 2 - 2
public/app/partials/valueSelectDropdown.html

@@ -1,5 +1,5 @@
 <div class="variable-link-wrapper">
 <div class="variable-link-wrapper">
-	<a ng-click="vm.show()" class="variable-value-link tight-form-item">
+	<a ng-click="vm.show()" class="variable-value-link">
 		{{vm.linkText}}
 		{{vm.linkText}}
 		<span ng-repeat="tag in vm.selectedTags" bs-tooltip='tag.valuesText' data-placement="bottom">
 		<span ng-repeat="tag in vm.selectedTags" bs-tooltip='tag.valuesText' data-placement="bottom">
 			<span class="label-tag"tag-color-from-name="tag.text">
 			<span class="label-tag"tag-color-from-name="tag.text">
@@ -10,7 +10,7 @@
 		<i class="fa fa-caret-down"></i>
 		<i class="fa fa-caret-down"></i>
 	</a>
 	</a>
 
 
-	<input type="text" class="tight-form-clear-input input-small" style="display: none" ng-keydown="vm.keyDown($event)" ng-model="vm.search.query" ng-change="vm.queryChanged()" ></input>
+	<input type="text" class="hidden-input input-small" style="display: none" ng-keydown="vm.keyDown($event)" ng-model="vm.search.query" ng-change="vm.queryChanged()" ></input>
 
 
 	<div class="variable-value-dropdown" ng-if="vm.dropdownVisible" ng-class="{'multi': vm.variable.multi, 'single': !vm.variable.multi}">
 	<div class="variable-value-dropdown" ng-if="vm.dropdownVisible" ng-class="{'multi': vm.variable.multi, 'single': !vm.variable.multi}">
 		<div class="variable-options-wrapper">
 		<div class="variable-options-wrapper">

+ 2 - 0
public/app/plugins/datasource/cloudwatch/annotation_query.d.ts

@@ -0,0 +1,2 @@
+declare var test: any;
+export default test;

+ 105 - 0
public/app/plugins/datasource/cloudwatch/annotation_query.js

@@ -0,0 +1,105 @@
+define([
+  'lodash',
+],
+function (_) {
+  'use strict';
+
+  function CloudWatchAnnotationQuery(datasource, annotation, $q, templateSrv) {
+    this.datasource = datasource;
+    this.annotation = annotation;
+    this.$q = $q;
+    this.templateSrv = templateSrv;
+  }
+
+  CloudWatchAnnotationQuery.prototype.process = function(from, to) {
+    var self = this;
+    var usePrefixMatch = this.annotation.prefixMatching;
+    var region = this.templateSrv.replace(this.annotation.region);
+    var namespace = this.templateSrv.replace(this.annotation.namespace);
+    var metricName = this.templateSrv.replace(this.annotation.metricName);
+    var dimensions = this.datasource.convertDimensionFormat(this.annotation.dimensions);
+    var statistics = _.map(this.annotation.statistics, function(s) { return self.templateSrv.replace(s); });
+    var defaultPeriod = usePrefixMatch ? '' : '300';
+    var period = this.annotation.period || defaultPeriod;
+    period = parseInt(period, 10);
+    var actionPrefix = this.annotation.actionPrefix || '';
+    var alarmNamePrefix = this.annotation.alarmNamePrefix || '';
+
+    var d = this.$q.defer();
+    var allQueryPromise;
+    if (usePrefixMatch) {
+      allQueryPromise = [
+        this.datasource.performDescribeAlarms(region, actionPrefix, alarmNamePrefix, [], '').then(function(alarms) {
+          alarms.MetricAlarms = self.filterAlarms(alarms, namespace, metricName, dimensions, statistics, period);
+          return alarms;
+        })
+      ];
+    } else {
+      if (!region || !namespace || !metricName || _.isEmpty(statistics)) { return this.$q.when([]); }
+
+      allQueryPromise = _.map(statistics, function(statistic) {
+        return self.datasource.performDescribeAlarmsForMetric(region, namespace, metricName, dimensions, statistic, period);
+      });
+    }
+    this.$q.all(allQueryPromise).then(function(alarms) {
+      var eventList = [];
+
+      var start = self.datasource.convertToCloudWatchTime(from, false);
+      var end = self.datasource.convertToCloudWatchTime(to, true);
+      _.chain(alarms)
+      .pluck('MetricAlarms')
+      .flatten()
+      .each(function(alarm) {
+        if (!alarm) {
+          d.resolve(eventList);
+          return;
+        }
+
+        self.datasource.performDescribeAlarmHistory(region, alarm.AlarmName, start, end).then(function(history) {
+          _.each(history.AlarmHistoryItems, function(h) {
+            var event = {
+              annotation: self.annotation,
+              time: Date.parse(h.Timestamp),
+              title: h.AlarmName,
+              tags: [h.HistoryItemType],
+              text: h.HistorySummary
+            };
+
+            eventList.push(event);
+          });
+
+          d.resolve(eventList);
+        });
+      });
+    });
+
+    return d.promise;
+  };
+
+  CloudWatchAnnotationQuery.prototype.filterAlarms = function(alarms, namespace, metricName, dimensions, statistics, period) {
+    return _.filter(alarms.MetricAlarms, function(alarm) {
+      if (!_.isEmpty(namespace) && alarm.Namespace !== namespace) {
+        return false;
+      }
+      if (!_.isEmpty(metricName) && alarm.MetricName !== metricName) {
+        return false;
+      }
+      var sd = function(d) {
+        return d.Name;
+      };
+      var isSameDimensions = JSON.stringify(_.sortBy(alarm.Dimensions, sd)) === JSON.stringify(_.sortBy(dimensions, sd));
+      if (!_.isEmpty(dimensions) && !isSameDimensions) {
+        return false;
+      }
+      if (!_.isEmpty(statistics) && !_.contains(statistics, alarm.Statistic)) {
+        return false;
+      }
+      if (!_.isNaN(period) && alarm.Period !== period) {
+        return false;
+      }
+      return true;
+    });
+  };
+
+  return CloudWatchAnnotationQuery;
+});

+ 21 - 58
public/app/plugins/datasource/cloudwatch/datasource.js

@@ -3,8 +3,9 @@ define([
   'lodash',
   'lodash',
   'moment',
   'moment',
   'app/core/utils/datemath',
   'app/core/utils/datemath',
+  './annotation_query',
 ],
 ],
-function (angular, _, moment, dateMath) {
+function (angular, _, moment, dateMath, CloudWatchAnnotationQuery) {
   'use strict';
   'use strict';
 
 
   /** @ngInject */
   /** @ngInject */
@@ -15,9 +16,10 @@ function (angular, _, moment, dateMath) {
     this.proxyUrl = instanceSettings.url;
     this.proxyUrl = instanceSettings.url;
     this.defaultRegion = instanceSettings.jsonData.defaultRegion;
     this.defaultRegion = instanceSettings.jsonData.defaultRegion;
 
 
+    var self = this;
     this.query = function(options) {
     this.query = function(options) {
-      var start = convertToCloudWatchTime(options.range.from, false);
-      var end = convertToCloudWatchTime(options.range.to, true);
+      var start = self.convertToCloudWatchTime(options.range.from, false);
+      var end = self.convertToCloudWatchTime(options.range.to, true);
 
 
       var queries = [];
       var queries = [];
       options = angular.copy(options);
       options = angular.copy(options);
@@ -30,7 +32,7 @@ function (angular, _, moment, dateMath) {
         query.region = templateSrv.replace(target.region, options.scopedVars);
         query.region = templateSrv.replace(target.region, options.scopedVars);
         query.namespace = templateSrv.replace(target.namespace, options.scopedVars);
         query.namespace = templateSrv.replace(target.namespace, options.scopedVars);
         query.metricName = templateSrv.replace(target.metricName, options.scopedVars);
         query.metricName = templateSrv.replace(target.metricName, options.scopedVars);
-        query.dimensions = convertDimensionFormat(target.dimensions, options.scopedVars);
+        query.dimensions = self.convertDimensionFormat(target.dimensions, options.scopedVars);
         query.statistics = target.statistics;
         query.statistics = target.statistics;
 
 
         var range = end - start;
         var range = end - start;
@@ -117,7 +119,7 @@ function (angular, _, moment, dateMath) {
         parameters: {
         parameters: {
           namespace: templateSrv.replace(namespace),
           namespace: templateSrv.replace(namespace),
           metricName: templateSrv.replace(metricName),
           metricName: templateSrv.replace(metricName),
-          dimensions: convertDimensionFormat(filterDimensions, {}),
+          dimensions: this.convertDimensionFormat(filterDimensions, {}),
         }
         }
       };
       };
 
 
@@ -206,6 +208,14 @@ function (angular, _, moment, dateMath) {
       return $q.when([]);
       return $q.when([]);
     };
     };
 
 
+    this.performDescribeAlarms = function(region, actionPrefix, alarmNamePrefix, alarmNames, stateValue) {
+      return this.awsRequest({
+        region: region,
+        action: 'DescribeAlarms',
+        parameters: { actionPrefix: actionPrefix, alarmNamePrefix: alarmNamePrefix, alarmNames: alarmNames, stateValue: stateValue }
+      });
+    };
+
     this.performDescribeAlarmsForMetric = function(region, namespace, metricName, dimensions, statistic, period) {
     this.performDescribeAlarmsForMetric = function(region, namespace, metricName, dimensions, statistic, period) {
       return this.awsRequest({
       return this.awsRequest({
         region: region,
         region: region,
@@ -223,55 +233,8 @@ function (angular, _, moment, dateMath) {
     };
     };
 
 
     this.annotationQuery = function(options) {
     this.annotationQuery = function(options) {
-      var annotation = options.annotation;
-      var region = templateSrv.replace(annotation.region);
-      var namespace = templateSrv.replace(annotation.namespace);
-      var metricName = templateSrv.replace(annotation.metricName);
-      var dimensions = convertDimensionFormat(annotation.dimensions);
-      var statistics = _.map(annotation.statistics, function(s) { return templateSrv.replace(s); });
-      var period = annotation.period || '300';
-      period = parseInt(period, 10);
-
-      if (!region || !namespace || !metricName || _.isEmpty(statistics)) { return $q.when([]); }
-
-      var d = $q.defer();
-      var self = this;
-      var allQueryPromise = _.map(statistics, function(statistic) {
-        return self.performDescribeAlarmsForMetric(region, namespace, metricName, dimensions, statistic, period);
-      });
-      $q.all(allQueryPromise).then(function(alarms) {
-        var eventList = [];
-
-        var start = convertToCloudWatchTime(options.range.from, false);
-        var end = convertToCloudWatchTime(options.range.to, true);
-        _.chain(alarms)
-        .pluck('MetricAlarms')
-        .flatten()
-        .each(function(alarm) {
-          if (!alarm) {
-            d.resolve(eventList);
-            return;
-          }
-
-          self.performDescribeAlarmHistory(region, alarm.AlarmName, start, end).then(function(history) {
-            _.each(history.AlarmHistoryItems, function(h) {
-              var event = {
-                annotation: annotation,
-                time: Date.parse(h.Timestamp),
-                title: h.AlarmName,
-                tags: [h.HistoryItemType],
-                text: h.HistorySummary
-              };
-
-              eventList.push(event);
-            });
-
-            d.resolve(eventList);
-          });
-        });
-      });
-
-      return d.promise;
+      var annotationQuery = new CloudWatchAnnotationQuery(this, options.annotation, $q, templateSrv);
+      return annotationQuery.process(options.range.from, options.range.to);
     };
     };
 
 
     this.testDatasource = function() {
     this.testDatasource = function() {
@@ -347,21 +310,21 @@ function (angular, _, moment, dateMath) {
       });
       });
     }
     }
 
 
-    function convertToCloudWatchTime(date, roundUp) {
+    this.convertToCloudWatchTime = function(date, roundUp) {
       if (_.isString(date)) {
       if (_.isString(date)) {
         date = dateMath.parse(date, roundUp);
         date = dateMath.parse(date, roundUp);
       }
       }
       return Math.round(date.valueOf() / 1000);
       return Math.round(date.valueOf() / 1000);
-    }
+    };
 
 
-    function convertDimensionFormat(dimensions, scopedVars) {
+    this.convertDimensionFormat = function(dimensions, scopedVars) {
       return _.map(dimensions, function(value, key) {
       return _.map(dimensions, function(value, key) {
         return {
         return {
           Name: templateSrv.replace(key, scopedVars),
           Name: templateSrv.replace(key, scopedVars),
           Value: templateSrv.replace(value, scopedVars)
           Value: templateSrv.replace(value, scopedVars)
         };
         };
       });
       });
-    }
+    };
 
 
   }
   }
 
 

+ 18 - 0
public/app/plugins/datasource/cloudwatch/partials/annotations.editor.html

@@ -1 +1,19 @@
 <cloudwatch-query-parameter target="ctrl.annotation" datasource="ctrl.datasource"></cloudwatch-query-parameter>
 <cloudwatch-query-parameter target="ctrl.annotation" datasource="ctrl.datasource"></cloudwatch-query-parameter>
+<div class="editor-row">
+	<div class="section">
+		<h5>Prefix matching</h5>
+		<div class="editor-option">
+			<editor-checkbox text="Enable" model="ctrl.annotation.prefixMatching"></editor-checkbox>
+		</div>
+
+		<div class="editor-option" ng-if="ctrl.annotation.prefixMatching">
+			<label class="small">Action</label>
+			<input type="text" class="input-small" ng-model='ctrl.annotation.actionPrefix'></input>
+		</div>
+
+		<div class="editor-option" ng-if="ctrl.annotation.prefixMatching">
+			<label class="small">Alarm Name</label>
+			<input type="text" class="input-small" ng-model='ctrl.annotation.alarmNamePrefix'></input>
+		</div>
+	</div>
+</div>

+ 81 - 0
public/app/plugins/datasource/cloudwatch/specs/annotation_query_specs.ts

@@ -0,0 +1,81 @@
+import "../datasource";
+import {describe, beforeEach, it, sinon, expect, angularMocks} from 'test/lib/common';
+import moment from 'moment';
+import helpers from 'test/specs/helpers';
+import {CloudWatchDatasource} from "../datasource";
+import CloudWatchAnnotationQuery from '../annotation_query';
+
+describe('CloudWatchAnnotationQuery', function() {
+  var ctx = new helpers.ServiceTestContext();
+  var instanceSettings = {
+    jsonData: {defaultRegion: 'us-east-1', access: 'proxy'},
+  };
+
+  beforeEach(angularMocks.module('grafana.core'));
+  beforeEach(angularMocks.module('grafana.services'));
+  beforeEach(angularMocks.module('grafana.controllers'));
+  beforeEach(ctx.providePhase(['templateSrv', 'backendSrv']));
+
+  beforeEach(angularMocks.inject(function($q, $rootScope, $httpBackend, $injector) {
+    ctx.$q = $q;
+    ctx.$httpBackend =  $httpBackend;
+    ctx.$rootScope = $rootScope;
+    ctx.ds = $injector.instantiate(CloudWatchDatasource, {instanceSettings: instanceSettings});
+  }));
+
+  describe('When performing annotationQuery', function() {
+    var parameter = {
+      annotation: {
+        region: 'us-east-1',
+        namespace: 'AWS/EC2',
+        metricName: 'CPUUtilization',
+        dimensions: {
+          InstanceId: 'i-12345678'
+        },
+        statistics: ['Average'],
+        period: 300
+      },
+      range: {
+        from: moment(1443438674760),
+        to: moment(1443460274760)
+      }
+    };
+    var alarmResponse = {
+      MetricAlarms: [
+        {
+          AlarmName: 'test_alarm_name'
+        }
+      ]
+    };
+    var historyResponse = {
+      AlarmHistoryItems: [
+        {
+          Timestamp: '2015-01-01T00:00:00.000Z',
+          HistoryItemType: 'StateUpdate',
+          AlarmName: 'test_alarm_name',
+          HistoryData: '{}',
+          HistorySummary: 'test_history_summary'
+        }
+      ]
+    };
+    beforeEach(function() {
+      ctx.backendSrv.datasourceRequest = function(params) {
+        switch (params.data.action) {
+        case 'DescribeAlarmsForMetric':
+          return ctx.$q.when({data: alarmResponse});
+        case 'DescribeAlarmHistory':
+          return ctx.$q.when({data: historyResponse});
+        }
+      };
+    });
+    it('should return annotation list', function(done) {
+      var annotationQuery = new CloudWatchAnnotationQuery(ctx.ds, parameter.annotation, ctx.$q, ctx.templateSrv);
+      annotationQuery.process(parameter.range.from, parameter.range.to).then(function(result) {
+        expect(result[0].title).to.be('test_alarm_name');
+        expect(result[0].text).to.be('test_history_summary');
+        done();
+      });
+      ctx.$rootScope.$apply();
+    });
+  });
+});

+ 0 - 55
public/app/plugins/datasource/cloudwatch/specs/datasource_specs.ts

@@ -187,59 +187,4 @@ describe('CloudWatchDatasource', function() {
       expect(scenario.request.data.action).to.be('ListMetrics');
       expect(scenario.request.data.action).to.be('ListMetrics');
     });
     });
   });
   });
-
-  describe('When performing annotationQuery', function() {
-    var parameter = {
-      annotation: {
-        region: 'us-east-1',
-        namespace: 'AWS/EC2',
-        metricName: 'CPUUtilization',
-        dimensions: {
-          InstanceId: 'i-12345678'
-        },
-        statistics: ['Average'],
-        period: 300
-      },
-      range: {
-        from: moment(1443438674760),
-        to: moment(1443460274760)
-      }
-    };
-    var alarmResponse = {
-      MetricAlarms: [
-        {
-          AlarmName: 'test_alarm_name'
-        }
-      ]
-    };
-    var historyResponse = {
-      AlarmHistoryItems: [
-        {
-          Timestamp: '2015-01-01T00:00:00.000Z',
-          HistoryItemType: 'StateUpdate',
-          AlarmName: 'test_alarm_name',
-          HistoryData: '{}',
-          HistorySummary: 'test_history_summary'
-        }
-      ]
-    };
-    beforeEach(function() {
-      ctx.backendSrv.datasourceRequest = function(params) {
-        switch (params.data.action) {
-        case 'DescribeAlarmsForMetric':
-          return ctx.$q.when({data: alarmResponse});
-        case 'DescribeAlarmHistory':
-          return ctx.$q.when({data: historyResponse});
-        }
-      };
-    });
-    it('should return annotation list', function(done) {
-      ctx.ds.annotationQuery(parameter).then(function(result) {
-        expect(result[0].title).to.be('test_alarm_name');
-        expect(result[0].text).to.be('test_history_summary');
-        done();
-      });
-      ctx.$rootScope.$apply();
-    });
-  });
 });
 });

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.