浏览代码

ux(getting started): progress on getting started panel and persited help flag states, #6466

Torkel Ödegaard 9 年之前
父节点
当前提交
fb57bf77da

+ 3 - 0
pkg/api/api.go

@@ -113,6 +113,9 @@ func Register(r *macaron.Macaron) {
 
 			r.Put("/password", bind(m.ChangeUserPasswordCommand{}), wrap(ChangeUserPassword))
 			r.Get("/quotas", wrap(GetUserQuotas))
+			r.Put("/helpflags/:id", wrap(SetHelpFlag))
+			// For dev purpose
+			r.Get("/helpflags/clear", wrap(ClearHelpFlags))
 
 			r.Get("/preferences", wrap(GetUserPreferences))
 			r.Put("/preferences", bind(dtos.UpdatePrefsCmd{}), wrap(UpdateUserPreferences))

+ 14 - 13
pkg/api/dtos/models.go

@@ -22,19 +22,20 @@ type LoginCommand struct {
 }
 
 type CurrentUser struct {
-	IsSignedIn     bool       `json:"isSignedIn"`
-	Id             int64      `json:"id"`
-	Login          string     `json:"login"`
-	Email          string     `json:"email"`
-	Name           string     `json:"name"`
-	LightTheme     bool       `json:"lightTheme"`
-	OrgId          int64      `json:"orgId"`
-	OrgName        string     `json:"orgName"`
-	OrgRole        m.RoleType `json:"orgRole"`
-	IsGrafanaAdmin bool       `json:"isGrafanaAdmin"`
-	GravatarUrl    string     `json:"gravatarUrl"`
-	Timezone       string     `json:"timezone"`
-	Locale         string     `json:"locale"`
+	IsSignedIn     bool         `json:"isSignedIn"`
+	Id             int64        `json:"id"`
+	Login          string       `json:"login"`
+	Email          string       `json:"email"`
+	Name           string       `json:"name"`
+	LightTheme     bool         `json:"lightTheme"`
+	OrgId          int64        `json:"orgId"`
+	OrgName        string       `json:"orgName"`
+	OrgRole        m.RoleType   `json:"orgRole"`
+	IsGrafanaAdmin bool         `json:"isGrafanaAdmin"`
+	GravatarUrl    string       `json:"gravatarUrl"`
+	Timezone       string       `json:"timezone"`
+	Locale         string       `json:"locale"`
+	HelpFlags1     m.HelpFlags1 `json:"helpFlags1"`
 }
 
 type DashboardMeta struct {

+ 1 - 0
pkg/api/index.go

@@ -58,6 +58,7 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
 			LightTheme:     prefs.Theme == "light",
 			Timezone:       prefs.Timezone,
 			Locale:         locale,
+			HelpFlags1:     c.HelpFlags1,
 		},
 		Settings:                settings,
 		AppUrl:                  appUrl,

+ 31 - 0
pkg/api/user.go

@@ -180,3 +180,34 @@ func SearchUsers(c *middleware.Context) Response {
 
 	return Json(200, query.Result)
 }
+
+func SetHelpFlag(c *middleware.Context) Response {
+	flag := c.ParamsInt64(":id")
+
+	bitmask := &c.HelpFlags1
+	bitmask.AddFlag(m.HelpFlags1(flag))
+
+	cmd := m.SetUserHelpFlagCommand{
+		UserId:     c.UserId,
+		HelpFlags1: *bitmask,
+	}
+
+	if err := bus.Dispatch(&cmd); err != nil {
+		return ApiError(500, "Failed to update help flag", err)
+	}
+
+	return Json(200, &util.DynMap{"message": "Help flag set", "helpFlags1": cmd.HelpFlags1})
+}
+
+func ClearHelpFlags(c *middleware.Context) Response {
+	cmd := m.SetUserHelpFlagCommand{
+		UserId:     c.UserId,
+		HelpFlags1: m.HelpFlags1(0),
+	}
+
+	if err := bus.Dispatch(&cmd); err != nil {
+		return ApiError(500, "Failed to update help flag", err)
+	}
+
+	return Json(200, &util.DynMap{"message": "Help flag set", "helpFlags1": cmd.HelpFlags1})
+}

+ 18 - 0
pkg/models/helpflags.go

@@ -0,0 +1,18 @@
+package models
+
+type HelpFlags1 uint64
+
+const (
+	HelpFlagGettingStartedPanelDismissed HelpFlags1 = 1 << iota
+	HelpFlagDashboardHelp1
+)
+
+func (f HelpFlags1) HasFlag(flag HelpFlags1) bool { return f&flag != 0 }
+func (f *HelpFlags1) AddFlag(flag HelpFlags1)     { *f |= flag }
+func (f *HelpFlags1) ClearFlag(flag HelpFlags1)   { *f &= ^flag }
+func (f *HelpFlags1) ToggleFlag(flag HelpFlags1)  { *f ^= flag }
+
+type SetUserHelpFlagCommand struct {
+	HelpFlags1 HelpFlags1
+	UserId     int64
+}

+ 2 - 0
pkg/models/user.go

@@ -22,6 +22,7 @@ type User struct {
 	Company       string
 	EmailVerified bool
 	Theme         string
+	HelpFlags1    HelpFlags1
 
 	IsAdmin bool
 	OrgId   int64
@@ -144,6 +145,7 @@ type SignedInUser struct {
 	Email          string
 	ApiKeyId       int64
 	IsGrafanaAdmin bool
+	HelpFlags1     HelpFlags1
 }
 
 type UserProfileDTO struct {

+ 4 - 0
pkg/services/sqlstore/migrations/user_mig.go

@@ -88,4 +88,8 @@ func addUserMigrations(mg *Migrator) {
 	}))
 
 	mg.AddMigration("Drop old table user_v1", NewDropTableMigration("user_v1"))
+
+	mg.AddMigration("Add column help_flags1 to user table", NewAddColumnMigration(userV2, &Column{
+		Name: "help_flags1", Type: DB_BigInt, Nullable: false, Default: "0",
+	}))
 }

+ 20 - 1
pkg/services/sqlstore/user.go

@@ -28,6 +28,7 @@ func init() {
 	bus.AddHandler("sql", DeleteUser)
 	bus.AddHandler("sql", SetUsingOrg)
 	bus.AddHandler("sql", UpdateUserPermissions)
+	bus.AddHandler("sql", SetUserHelpFlag)
 }
 
 func getOrgIdForNewUser(cmd *m.CreateUserCommand, sess *session) (int64, error) {
@@ -207,7 +208,7 @@ func GetUserByEmail(query *m.GetUserByEmailQuery) error {
 	if err != nil {
 		return err
 	} else if has == false {
-		return  m.ErrUserNotFound
+		return m.ErrUserNotFound
 	}
 
 	query.Result = user
@@ -308,6 +309,7 @@ func GetSignedInUser(query *m.GetSignedInUserQuery) error {
 	                u.email        as email,
 	                u.login        as login,
 									u.name         as name,
+									u.help_flags1  as help_flags1,
 	                org.name       as org_name,
 	                org_user.role  as org_role,
 	                org.id         as org_id
@@ -380,3 +382,20 @@ func UpdateUserPermissions(cmd *m.UpdateUserPermissionsCommand) error {
 		return err
 	})
 }
+
+func SetUserHelpFlag(cmd *m.SetUserHelpFlagCommand) error {
+	return inTransaction2(func(sess *session) error {
+
+		user := m.User{
+			Id:         cmd.UserId,
+			HelpFlags1: cmd.HelpFlags1,
+			Updated:    time.Now(),
+		}
+
+		if _, err := sess.Id(cmd.UserId).Cols("help_flags1").Update(&user); err != nil {
+			return err
+		}
+
+		return nil
+	})
+}

+ 3 - 1
public/app/core/services/backend_srv.ts

@@ -74,7 +74,9 @@ export class BackendSrv {
     return this.$http(options).then(results => {
       if (options.method !== 'GET') {
         if (results && results.data.message) {
-          this.alertSrv.set(results.data.message, '', 'success', 3000);
+          if (options.showSuccessAlert !== false) {
+            this.alertSrv.set(results.data.message, '', 'success', 3000);
+          }
         }
       }
       return results.data;

+ 1 - 0
public/app/core/services/context_srv.ts

@@ -10,6 +10,7 @@ export class User {
   isSignedIn: any;
   orgRole: any;
   timezone: string;
+  helpFlags1: number;
 
   constructor() {
     if (config.bootData.user) {

+ 0 - 3
public/app/features/dashboard/row/add_panel.ts

@@ -84,11 +84,8 @@ export class AddPanelCtrl {
     var panel = {
       id: null,
       title: config.new_panel_title,
-      error: false,
       span: span < defaultSpan && span > 0 ? span : defaultSpan,
-      editable: true,
       type: panelPluginInfo.id,
-      isNew: true,
     };
 
     this.rowCtrl.closeDropView();

+ 0 - 1
public/app/features/dashboard/row/row_model.ts

@@ -19,7 +19,6 @@ export class DashboardRow {
     showTitle: false,
     titleSize: 'h6',
     height: 250,
-    isNew: false,
     repeat: null,
     repeatRowId: null,
     repeatIteration: null,

+ 17 - 6
public/app/plugins/panel/gettingstarted/module.html

@@ -2,23 +2,34 @@
   <div class="dashlist-section">
     <h6 class="dashlist-section-header">
       Getting Started with Grafana
-      <button class="dashlist-CTA-close-btn"><i class="fa fa-remove"></i></button>
+      <button class="dashlist-CTA-close-btn" ng-click="ctrl.dismiss()">
+        <i class="fa fa-remove"></i>
+      </button>
     </h6>
-    <ul class="progress-tracker progress-tracker--text progress-tracker--center">
-      <li class="progress-step is-complete">
+    <ul class="progress-tracker progress-tracker--text progress-tracker--center" ng-if="ctrl.checksDone">
+      <li class="progress-step completed">
         <span class="progress-marker"><i class="icon-gf icon-gf-check gettingstarted-icon-success"></i></span>
         <span class="progress-text"><span class="gettingstarted-blurb-success">Install Grafana</span></span>
       </li>
-      <li class="progress-step is-active">
+      <li class="progress-step active" ng-if="!ctrl.hasDatasources">
         <span class="progress-marker"><i class="icon-gf icon-gf-datasources gettingstarted-icon-active"></i></span>
         <span class="progress-text">
           <a href="#" class="gettingstarted-blurb">Create your first data source.</a>
           <button class="btn btn-success btn-small">Add Data Source</button>
         </span>
       </li>
-      <li class="progress-step">
+      <li class="progress-step completed" ng-if="ctrl.hasDatasources">
+        <span class="progress-marker"><i class="icon-gf icon-gf-check gettingstarted-icon-success"></i></span>
+        <span class="progress-text">
+          <span class="gettingstarted-blurb-success">Create your first data source.</span>
+        </span>
+      </li>
+      <li class="progress-step active" ng-if="ctrl.hasDatasources">
         <span class="progress-marker"><i class="icon-gf icon-gf-dashboard gettingstarted-icon-upcoming"></i></span>
-        <span class="progress-text"><a href="#" class="gettingstarted-blurb-upcoming">Create your first dashboard.</a></span>
+        <span class="progress-text">
+          <a href="#" class="gettingstarted-blurb-upcoming">Create your first dashboard.</a>
+          <button class="btn btn-success btn-small">Add Data Source</button>
+        </span>
       </li>
       <li class="progress-step">
         <span class="progress-marker"><i class="icon-gf icon-gf-users gettingstarted-icon-upcoming"></i></span>

+ 33 - 4
public/app/plugins/panel/gettingstarted/module.ts

@@ -2,14 +2,43 @@
 
 import {PanelCtrl} from 'app/plugins/sdk';
 
-class GettingstartedPanelCtrl extends PanelCtrl {
+import {contextSrv} from 'app/core/core';
+
+class GettingStartedPanelCtrl extends PanelCtrl {
   static templateUrl = 'public/app/plugins/panel/gettingstarted/module.html';
+  hasDatasources: boolean;
+  checksDone: boolean;
 
-  /** @ngInject */
-  constructor($scope, $injector) {
+  /** @ngInject **/
+  constructor($scope, $injector, private backendSrv, private datasourceSrv) {
     super($scope, $injector);
+
+    /* tslint:disable */
+    if (contextSrv.user.helpFlags1 & 1) {
+      this.row.removePanel(this.panel, false);
+      return;
+    }
+    /* tslint:enable */
+
+    var datasources = datasourceSrv.getMetricSources().filter(item => {
+      return item.meta.builtIn === false;
+    });
+
+    this.hasDatasources = datasources.length > 0;
+    this.checksDone = true;
   }
 
+  dismiss() {
+    this.row.removePanel(this.panel, false);
+
+    this.backendSrv.request({
+      method: 'PUT',
+      url: '/api/user/helpflags/1',
+      showSuccessAlert: false,
+    }).then(res => {
+      contextSrv.user.helpFlags1 = res.helpFlags1;
+    });
+  }
 }
 
-export {GettingstartedPanelCtrl, GettingstartedPanelCtrl as PanelCtrl}
+export {GettingStartedPanelCtrl, GettingStartedPanelCtrl as PanelCtrl}

+ 10 - 2
public/dashboards/home.json

@@ -9,6 +9,7 @@
   "sharedCrosshair": false,
   "rows": [
    {
+      "title": "Row title",
       "collapse": false,
       "editable": true,
       "height": "25px",
@@ -24,9 +25,16 @@
           "title": "",
           "transparent": true,
           "type": "text"
+        },
+        {
+          "id": 8,
+          "links": [],
+          "span": 12,
+          "title": "",
+          "transparent": false,
+          "type": "gettingstarted"
         }
-      ],
-      "title": "New row"
+     ]
    },
    {
       "collapse": false,

+ 7 - 55
public/sass/components/_panel_gettingstarted.scss

@@ -1,11 +1,3 @@
-ul.gettingstarted-flex-container {
-  display: flex;
-  justify-content: space-around;
-  flex-direction: row;
-  padding:  20px;
-  list-style-type: none;
-}
-
 .gettingstarted-flex-item {
   align-items: center;
   display: flex;
@@ -19,14 +11,14 @@ ul.gettingstarted-flex-container {
   text-align: center;
 }
 
-a.gettingstarted-blurb{
+.gettingstarted-blurb {
   @extend .gettingstarted-blurb-copy;
   color: $text-color;
   display: block;
-}
 
-a.gettingstarted-blurb:hover{
-  text-decoration: underline;
+  &:hover{
+    text-decoration: underline;
+  }
 }
 
 .gettingstarted-blurb-success {
@@ -35,27 +27,11 @@ a.gettingstarted-blurb:hover{
   text-decoration: line-through;
 }
 
-a.gettingstarted-blurb-upcoming {
+.gettingstarted-blurb-upcoming {
   @extend .gettingstarted-blurb-copy;
   color: $text-color-weak;
 }
 
-.gettingstarted-icon-container {
-  height: 50px;
-}
-
-.gettingstarted-icon-active {
-  color: $brand-primary;
-  -webkit-text-fill-color: transparent;
-  background: $brand-gradient;
-  -webkit-background-clip: text;
-  text-decoration:none;
-  font-size: 35px;
-  vertical-align: sub;
-  animation: iconPulse 500ms forwards 1s;
-  will-change: zoom;
-}
-
 .gettingstarted-icon-upcoming {
   color: $text-color-weak;
   text-decoration:none;
@@ -70,7 +46,6 @@ a.gettingstarted-blurb-upcoming {
   vertical-align: sub;
 }
 
-
 .dashlist-CTA-close-btn {
   float: right;
   padding: 0;
@@ -86,25 +61,6 @@ a.gettingstarted-blurb-upcoming {
   }
 }
 
-@keyframes iconPulse {
-  from {
-    zoom: 100%;
-  }
-
-  50% {
-    zoom: 102%;
-  }
-
-  to {
-    zoom: 100%;
-  }
-}
-
-
-// ----- Progress Tracker -----
-
-// ----- Variables -----
-
 // Colours
 $progress-color-dark:       $panel-bg !default;
 $progress-color:            $panel-bg !default;
@@ -240,17 +196,16 @@ $ripple-color:              rgba(0, 0, 0, 0.3) !default;
 
 // States
   .progress-step {
-
     // Inactive - Default state
     @include progress-state($progress-color, null, #fff, $progress-color-grey-light, $progress-color-grey-dark);
 
     // Active state
-    &.is-active {
+    &.active {
       @include progress-state($progress-color);
     }
 
     // Complete state
-    &.is-complete {
+    &.completed {
       @include progress-state($progress-color-dark, $path-color: $progress-color-grey);
     }
 
@@ -258,11 +213,8 @@ $ripple-color:              rgba(0, 0, 0, 0.3) !default;
     &:hover {
       @include progress-state($progress-color-light);
     }
-
   }
 
-
-
 // ----- Modifiers -----
 
 // Center align markers and text