You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@atlas.apache.org by sa...@apache.org on 2019/10/10 18:39:27 UTC

[atlas] 01/02: ATLAS:3456 : Allows user to add label in entity detail page.

This is an automated email from the ASF dual-hosted git repository.

sarath pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/atlas.git

commit 715845523fed2e96bdbeba31a8f68db1d80c79f1
Author: sameer79 <fi...@yahoo.co.in>
AuthorDate: Thu Oct 10 11:25:04 2019 -0700

    ATLAS:3456 : Allows user to add label in entity detail page.
---
 dashboardv2/public/css/scss/common.scss            |  13 ++-
 dashboardv2/public/css/scss/panel.scss             |  16 +--
 dashboardv2/public/js/models/VEntity.js            |  10 +-
 .../detail_page/DetailPageLayoutView_tmpl.html     |   1 +
 .../entity/EntityLabelDefineView_tmpl.html         |  97 +++++++++++++++++
 .../public/js/templates/site/Statistics_tmpl.html  |  10 +-
 dashboardv2/public/js/utils/UrlLinks.js            |   5 +-
 .../js/views/detail_page/DetailPageLayoutView.js   |  12 ++-
 .../js/views/entity/EntityDetailTableLayoutView.js |   2 +-
 .../js/views/entity/EntityLabelDefineView.js       | 120 +++++++++++++++++++++
 .../public/js/views/entity/EntityUserDefineView.js |   2 +-
 dashboardv3/public/css/scss/common.scss            |  13 ++-
 dashboardv3/public/css/scss/panel.scss             |  16 +--
 dashboardv3/public/js/models/VEntity.js            |  10 +-
 .../detail_page/DetailPageLayoutView_tmpl.html     |   1 +
 .../entity/EntityLabelDefineView_tmpl.html         |  97 +++++++++++++++++
 .../public/js/templates/site/Statistics_tmpl.html  |  10 +-
 dashboardv3/public/js/utils/UrlLinks.js            |   5 +-
 .../js/views/detail_page/DetailPageLayoutView.js   |  10 +-
 .../js/views/entity/EntityDetailTableLayoutView.js |   2 +-
 .../js/views/entity/EntityLabelDefineView.js       | 120 +++++++++++++++++++++
 .../public/js/views/entity/EntityUserDefineView.js |   2 +-
 22 files changed, 533 insertions(+), 41 deletions(-)

diff --git a/dashboardv2/public/css/scss/common.scss b/dashboardv2/public/css/scss/common.scss
index b24c3c3..6fbef88 100644
--- a/dashboardv2/public/css/scss/common.scss
+++ b/dashboardv2/public/css/scss/common.scss
@@ -228,16 +228,18 @@ pre {
         .custom-col-0{
             text-align: center;
             vertical-align: middle;
+            line-height: 31px;
             width: 2%;
         }
 
         .custom-col-1{
-            width: 43%;
+            width: 40%;
         }
 
         .custom-col-2{
             text-align: center;
-            width: 10%;
+            width: 12%;
+            margin-left: 2%;
         }
     }
 }
@@ -245,3 +247,10 @@ pre {
 .errorMsg {
     color: $red;
 }
+
+.badge-default {
+    background-color: $color_havelock_blue_approx;
+    color: $white;
+    font-size: 12px;
+    font-weight: normal;
+}
diff --git a/dashboardv2/public/css/scss/panel.scss b/dashboardv2/public/css/scss/panel.scss
index e6dd4bb..9a97835 100644
--- a/dashboardv2/public/css/scss/panel.scss
+++ b/dashboardv2/public/css/scss/panel.scss
@@ -31,7 +31,7 @@
 
         .panel-title {
             font-weight: bold;
-            padding-top: 8px;
+            padding-top: 6px;
         }
     }
 
@@ -102,26 +102,26 @@
         }
 
         i.ec-icon:before {
-            content: "\f078"
+            content: "\f054"
         }
 
         &.collapsed,
         &[aria-expanded="false"] {
             i.ec-icon:before {
-                content: "\f078"
+                content: "\f054"
             }
         }
 
         &[aria-expanded="true"] {
             i.ec-icon:before {
-                content: "\f077"
+                content: "\f078"
             }
         }
     }
 }
 
 .panel-default.custom-panel>.panel-heading {
-    color: $black;
+    color: $color_jungle_green_approx;
     cursor: pointer;
     border-bottom: none;
     display: inline-block;
@@ -129,12 +129,12 @@
     .panel-title {
         font-weight: normal;
         a:hover {
-            color: $black;
+            color: $color_jungle_green_approx;
             opacity: 0.7;
         }
     }
     .btn-group {
-        margin-top: 3px;
+        margin-top: 4px;
     }
 }
 
@@ -151,5 +151,5 @@
     border-top: none;
 }
 .panel-default.custom-panel>.panel-heading > .btn-group > button {
-    color: $black;
+    color: $color_jungle_green_approx;
 }
diff --git a/dashboardv2/public/js/models/VEntity.js b/dashboardv2/public/js/models/VEntity.js
index deeb6b0..984039d 100644
--- a/dashboardv2/public/js/models/VEntity.js
+++ b/dashboardv2/public/js/models/VEntity.js
@@ -87,7 +87,15 @@ define(['require',
                 dataType: 'json'
             }, options);
             return this.constructor.nonCrudOperation.call(this, url, "", options);
+        },
+        saveEntityLabels: function(guid, options) {
+            var url = UrlLinks.entityLabelsAPIUrl(guid);
+            options = _.extend({
+                contentType: 'application/json',
+                dataType: 'json'
+            }, options);
+            return this.constructor.nonCrudOperation.call(this, url, "POST", options);
         }
     }, {});
     return VEntity;
-});
\ No newline at end of file
+});
diff --git a/dashboardv2/public/js/templates/detail_page/DetailPageLayoutView_tmpl.html b/dashboardv2/public/js/templates/detail_page/DetailPageLayoutView_tmpl.html
index c6f88c6..cbd222c 100644
--- a/dashboardv2/public/js/templates/detail_page/DetailPageLayoutView_tmpl.html
+++ b/dashboardv2/public/js/templates/detail_page/DetailPageLayoutView_tmpl.html
@@ -75,6 +75,7 @@
                 </div>
                 <div class="col-md-6">
                     <div id="r_entityUserDefineView"></div>
+                    <div id="r_entityLabelDefineView"></div>
                 </div>
             </div>
         </div>
diff --git a/dashboardv2/public/js/templates/entity/EntityLabelDefineView_tmpl.html b/dashboardv2/public/js/templates/entity/EntityLabelDefineView_tmpl.html
new file mode 100644
index 0000000..06d7e0e
--- /dev/null
+++ b/dashboardv2/public/js/templates/entity/EntityLabelDefineView_tmpl.html
@@ -0,0 +1,97 @@
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+
+<div class="panel-group" id="accordion">
+
+    <div class="panel panel-default custom-panel expand_collapse_panel-icon" data-id="userDefineLabel">
+        {{#ifCond labels.length "===" 0}}
+            <div class="panel-heading collapsed" data-toggle="collapse" href="#collapse3" aria-expanded="false" style="width: 70%">
+                <h4 class="panel-title">
+                    <a>Labels </a>
+                </h4>
+                <div class="btn-group pull-left">
+                    <button type="button" title="Collapse"><i class="ec-icon fa"></i></button>
+                </div>
+            </div>
+        {{else}}
+            <div class="panel-heading" data-toggle="collapse" href="#collapse3" aria-expanded="true" style="width: 70%">
+                <h4 class="panel-title">
+                    <a>Labels </a>
+                </h4>
+                <div class="btn-group pull-left">
+                    <button type="button" title="Collapse"><i class="ec-icon fa"></i></button>
+                </div>
+            </div>
+
+        {{/ifCond}}
+
+        {{#ifCond readOnlyEntity "===" false}}
+            <div class="panel-actions">
+                    {{#ifCond  swapItem "!==" true}}
+                        <button class="btn btn-action btn-sm"  data-id="addLabels" {{#ifCond labels.length "===" 0}} data-original-title="Add User-Defined Labels" {{else}} data-original-title="Edit User-Defined Labels"  {{/ifCond}}>
+                            {{#ifCond labels.length "===" 0}} Add {{else}} Edit {{/ifCond}}
+                        </button>
+                    {{/ifCond}}
+                    {{#ifCond saveLabels "===" true}}
+                        <button class="btn btn-action btn-sm"  data-id="saveLabels"  data-original-title="Save User-Defined Labels">Save</button>
+                    {{/ifCond}}
+            </div>
+        {{/ifCond}}
+
+        <div id="collapse3" {{#ifCond swapItem "===" false}} class="panel-collapse collapse" {{else}} class="panel-collapse collapse in" {{/ifCond}}>
+            {{#ifCond labels.length "===" 0}}
+                <div class="panel-body">
+                        <div class="row">
+                            <div class="col-md-12">
+                                {{#ifCond swapItem "===" true}}
+                                    <select class="form-control" data-id="addLabelOptions" multiple="multiple"></select>
+                                {{else}}
+                                <div class="badge-container">
+                                        {{#each labels}}
+                                            <label class="label badge-default">{{this}}</label>
+                                        {{/each}}
+                                </div>
+                                {{/ifCond}}
+                            </div>
+                        </div>
+                </div>
+            {{/ifCond}}
+        </div>
+
+        {{#ifCond labels.length ">" 0}}
+            <div id="collapse3" class="panel-collapse collapse in" >
+                <div class="panel-body">
+                        <div class="row">
+                            <div class="col-md-12">
+                                {{#ifCond swapItem "===" true}}
+                                    <select class="form-control" data-id="addLabelOptions" multiple="multiple"></select>
+                                {{else}}
+                                <div class="badge-container">
+                                        {{#each labels}}
+                                            <label class="label badge-default">{{this}}</label>
+                                        {{/each}}
+                                </div>
+                                {{/ifCond}}
+                            </div>
+                        </div>
+                </div>
+            </div>
+        {{/ifCond}}
+
+
+    </div>
+</div>
diff --git a/dashboardv2/public/js/templates/site/Statistics_tmpl.html b/dashboardv2/public/js/templates/site/Statistics_tmpl.html
index 436767c..f5c6c8b 100644
--- a/dashboardv2/public/js/templates/site/Statistics_tmpl.html
+++ b/dashboardv2/public/js/templates/site/Statistics_tmpl.html
@@ -15,12 +15,12 @@
  * limitations under the License.
 -->
 <div class="panel-group server-stats-container statsContainer hide" id="accordion">
-    <div class="panel panel-default expand_collapse_panel-icon" data-id="entity">
+    <div class="panel panel-default custom-panel expand_collapse_panel-icon" data-id="entity">
         <div class="panel-heading" data-toggle="collapse" href="#collapse1" aria-expanded="true">
             <h4 class="panel-title">
                 <a>Entities <span class="count">(0)</span></a>
             </h4>
-            <div class="btn-group pull-right">
+            <div class="btn-group pull-left">
                 <button type="button" title="Collapse"><i class="ec-icon fa"></i></button>
             </div>
         </div>
@@ -36,12 +36,12 @@
             </div>
         </div>
     </div>
-    <div class="panel panel-default expand_collapse_panel-icon" data-id="stats">
+    <div class="panel panel-default custom-panel expand_collapse_panel-icon" data-id="stats">
         <div class="panel-heading" data-toggle="collapse" href="#collapse3" aria-expanded="true">
             <h4 class="panel-title">
                 <a>Server Statistics </a>
             </h4>
-            <div class="btn-group pull-right">
+            <div class="btn-group pull-left">
                 <button type="button" title="Collapse"><i class="ec-icon fa"></i></button>
             </div>
         </div>
@@ -90,4 +90,4 @@
 </div>
 <div class="fontLoader-relative statsLoader show">
     <i class="fa fa-refresh fa-spin-custom"></i>
-</div>
\ No newline at end of file
+</div>
diff --git a/dashboardv2/public/js/utils/UrlLinks.js b/dashboardv2/public/js/utils/UrlLinks.js
index f6e1795..dbb1a0a 100644
--- a/dashboardv2/public/js/utils/UrlLinks.js
+++ b/dashboardv2/public/js/utils/UrlLinks.js
@@ -78,6 +78,9 @@ define(['require', 'utils/Enums', 'utils/Utils', 'underscore'], function(require
                 return entitiesUrl += '?minExtInfo=' + (minExtInfo);
             }
         },
+        entityLabelsAPIUrl: function(guid) {
+            return this.entitiesApiUrl({ guid: guid }) + "/labels";
+        },
         entityHeaderApiUrl: function(guid) {
             return this.entitiesApiUrl({ guid: guid }) + "/header"
         },
@@ -221,4 +224,4 @@ define(['require', 'utils/Enums', 'utils/Utils', 'underscore'], function(require
     });
 
     return UrlLinks;
-});
\ No newline at end of file
+});
diff --git a/dashboardv2/public/js/views/detail_page/DetailPageLayoutView.js b/dashboardv2/public/js/views/detail_page/DetailPageLayoutView.js
index ffd36ac..771fac3 100644
--- a/dashboardv2/public/js/views/detail_page/DetailPageLayoutView.js
+++ b/dashboardv2/public/js/views/detail_page/DetailPageLayoutView.js
@@ -47,6 +47,7 @@ define(['require',
                 RProfileLayoutView: "#r_profileLayoutView",
                 RRelationshipLayoutView: "#r_relationshipLayoutView",
                 REntityUserDefineView: "#r_entityUserDefineView",
+                REntityLabelDefineView: "#r_entityLabelDefineView"
             },
             /** ui selector cache */
             ui: {
@@ -196,7 +197,7 @@ define(['require',
                                 if (collectionJSON.isIncomplete === true) {
                                     this.$(".isIncomplete").addClass("show");
                                 }
-                                this.ui.entityIcon.attr('title', _.escape(collectionJSON.typeName)).html('<img src="' + Utils.getEntityIconPath({ entityData: entityData }) + '"/><i class="fa fa-refresh fa-spin-custom"></i>').find("img").on('error', function() {
+                                this.ui.entityIcon.attr('title', _.escape(collectionJSON.typeName)).html('<img src="' + Utils.getEntityIconPath({ entityData: entityData }) + '"/><i class="fa fa-hourglass-half"></i>').find("img").on('error', function() {
                                     this.src = Utils.getEntityIconPath({ entityData: entityData, errorUrl: this.src });
                                 });
                             } else {
@@ -248,6 +249,7 @@ define(['require',
                     }
                     this.renderEntityDetailTableLayoutView(obj);
                     this.renderEntityUserDefineView(obj);
+                    this.renderEntityLabelDefineView(obj);
                     this.renderRelationshipLayoutView(obj);
                     this.renderAuditTableLayoutView(obj);
                     this.renderTagTableLayoutView(obj);
@@ -425,7 +427,7 @@ define(['require',
                 _.each(data, function(val) {
                     // if (val.relationshipStatus == "ACTIVE") {
                     termData += '<span class="btn btn-action btn-sm btn-icon btn-blue" data-id="termClick"><span title=' + _.escape(val.displayText) + '>' + _.escape(val.displayText) + '</span><i class="' + (val.relationshipStatus == "ACTIVE" ? 'fa fa-close' : "") + '" data-id="deleteTerm" data-guid="' + val.guid + '" data-type="term" title="Remove Term"></i></span>';
-                    // } 
+                    // }
                 });
                 this.ui.termList.find("span.btn").remove();
                 this.ui.termList.prepend(termData);
@@ -495,6 +497,12 @@ define(['require',
                     that.REntityUserDefineView.show(new EntityUserDefineView(obj));
                 });
             },
+            renderEntityLabelDefineView: function(obj) {
+                var that = this;
+                require(['views/entity/EntityLabelDefineView'], function(EntityLabelDefineView) {
+                    that.REntityLabelDefineView.show(new EntityLabelDefineView(obj));
+                });
+            },
             renderTagTableLayoutView: function(obj) {
                 var that = this;
                 require(['views/tag/TagDetailTableLayoutView'], function(TagDetailTableLayoutView) {
diff --git a/dashboardv2/public/js/views/entity/EntityDetailTableLayoutView.js b/dashboardv2/public/js/views/entity/EntityDetailTableLayoutView.js
index 6572292..381d99e 100644
--- a/dashboardv2/public/js/views/entity/EntityDetailTableLayoutView.js
+++ b/dashboardv2/public/js/views/entity/EntityDetailTableLayoutView.js
@@ -83,4 +83,4 @@ define(['require',
             }
         });
     return EntityDetailTableLayoutView;
-});
+});
\ No newline at end of file
diff --git a/dashboardv2/public/js/views/entity/EntityLabelDefineView.js b/dashboardv2/public/js/views/entity/EntityLabelDefineView.js
new file mode 100644
index 0000000..d69b277
--- /dev/null
+++ b/dashboardv2/public/js/views/entity/EntityLabelDefineView.js
@@ -0,0 +1,120 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+define(['require',
+'backbone',
+'hbs!tmpl/entity/EntityLabelDefineView_tmpl',
+'models/VEntity',
+'utils/Utils',
+'utils/Messages',
+'utils/Enums'
+], function(require, Backbone, EntityLabelDefineView_tmpl, VEntity, Utils, Messages, Enums) {
+'use strict';
+
+    return Backbone.Marionette.LayoutView.extend({
+        _viewName: 'REntityLabelDefineView',
+        template: EntityLabelDefineView_tmpl,
+        templateHelpers: function() {
+            return {
+                swapItem: this.swapItem,
+                labels: this.labels,
+                saveLabels: this.saveLabels,
+                readOnlyEntity: this.readOnlyEntity
+            };
+        },
+        ui: {
+            addLabelOptions: "[data-id='addLabelOptions']",
+            addLabels: "[data-id='addLabels']",
+            saveLabels: "[data-id='saveLabels']"
+        },
+        events: function() {
+            var events = {};
+            events["change " + this.ui.addLabelOptions] = 'onChangeLabelChange';
+            events["click " + this.ui.addLabels] = 'handleBtnClick';
+            events["click " + this.ui.saveLabels] = 'saveUserDefinedLabels';
+            return events;
+        },
+        initialize: function(options) {
+            var self = this;
+            _.extend(this, _.pick(options, 'entity'));
+            this.swapItem = false, this.saveLabels = false;
+            this.readOnlyEntity = Enums.entityStateReadOnly[this.entity.status]
+            this.entityModel = new VEntity(this.entity);
+            this.labels = this.entity.labels || [];
+        },
+        onRender: function() {
+            this.populateLabelOptions();
+        },
+        bindEvents: function () {
+        },
+        populateLabelOptions: function() {
+            var that = this,
+            str = this.labels.map(function (label) {
+                return "<option selected > "+ label +" </option>";
+            });
+            this.ui.addLabelOptions.html(str);
+            this.ui.addLabelOptions.select2({
+                placeholder: "Select Label",
+                allowClear: true,
+                tags: true,
+                multiple: true
+            });
+        },
+        onChangeLabelChange: function () {
+            this.labels = this.ui.addLabelOptions.val();
+        },
+        handleBtnClick: function () {
+            this.swapItem = !this.swapItem;
+            this.saveLabels = this.swapItem === true ? true : false;
+            this.render();
+        },
+        saveUserDefinedLabels: function() {
+            var that = this;
+            var entityJson = that.entityModel.toJSON();
+            var payload = this.labels;
+            that.entityModel.saveEntityLabels(entityJson.guid ,{
+                data: JSON.stringify(payload),
+                type: 'POST',
+                success: function() {
+                    var msg = entityJson.labels === undefined ? 'addSuccessMessage' : 'editSuccessMessage';
+                    if (payload.length === 0) {
+                        that.entityModel.unset('labels');
+                    } else {
+                        that.entityModel.set('labels', payload);
+                    }
+                    Utils.notifySuccess({
+                        content: "User-defined labels " + Messages[msg]
+                    });
+                    that.swapItem = false;
+                    that.saveLabels = false;
+                    that.render();
+                },
+                error: function (e) {
+                    that.ui.saveLabels && that.ui.saveLabels.length > 0 &&  that.ui.saveLabels[0].setAttribute("disabled", false);
+                    Utils.notifySuccess({
+                        content: e.message
+                    });
+                },
+                complete: function () {
+                    that.ui.saveLabels && that.ui.saveLabels.length > 0 && that.ui.saveLabels[0].setAttribute("disabled", false);
+                    that.render();
+                }
+            });
+        }
+    });
+});
diff --git a/dashboardv2/public/js/views/entity/EntityUserDefineView.js b/dashboardv2/public/js/views/entity/EntityUserDefineView.js
index 588703f..a23a8fa 100644
--- a/dashboardv2/public/js/views/entity/EntityUserDefineView.js
+++ b/dashboardv2/public/js/views/entity/EntityUserDefineView.js
@@ -81,7 +81,7 @@ define(['require',
                 okText: 'Save',
                 okCloses: false,
                 cancelText: "Cancel",
-                mainClass: 'modal-lg',
+                mainClass: 'modal-md',
                 allowCancel: true,
             };
            this.setAttributeModal(modalObj);
diff --git a/dashboardv3/public/css/scss/common.scss b/dashboardv3/public/css/scss/common.scss
index dfe0e4f..24231d1 100644
--- a/dashboardv3/public/css/scss/common.scss
+++ b/dashboardv3/public/css/scss/common.scss
@@ -227,16 +227,18 @@ pre {
         .custom-col-0{
             text-align: center;
             vertical-align: middle;
+            line-height: 31px;
             width: 2%;
         }
 
         .custom-col-1{
-            width: 43%;
+            width: 40%;
         }
 
         .custom-col-2{
             text-align: center;
-            width: 10%;
+            width: 12%;
+            margin-left: 2%;
         }
     }
 }
@@ -244,3 +246,10 @@ pre {
 .errorMsg {
     color: $red;
 }
+
+.badge-default {
+    background-color: $color_havelock_blue_approx;
+    color: $white;
+    font-size: 12px;
+    font-weight: normal;
+}
diff --git a/dashboardv3/public/css/scss/panel.scss b/dashboardv3/public/css/scss/panel.scss
index 931a9a6..6635eb7 100644
--- a/dashboardv3/public/css/scss/panel.scss
+++ b/dashboardv3/public/css/scss/panel.scss
@@ -37,7 +37,7 @@
 
         .panel-title {
             font-weight: bold;
-            padding-top: 8px;
+            padding-top: 6px;
         }
     }
 
@@ -117,19 +117,19 @@
             }
 
             i.ec-icon:before {
-                content: "\f078"
+                content: "\f054"
             }
 
             &.collapsed,
             &[aria-expanded="false"] {
                 i.ec-icon:before {
-                    content: "\f078"
+                    content: "\f054"
                 }
             }
 
             &[aria-expanded="true"] {
                 i.ec-icon:before {
-                    content: "\f077"
+                    content: "\f078"
                 }
             }
         }
@@ -137,7 +137,7 @@
 }
 
 .panel-default.custom-panel>.panel-heading {
-    color: $black;
+    color: $color_jungle_green_approx;
     cursor: pointer;
     border-bottom: none;
     display: inline-block;
@@ -145,12 +145,12 @@
     .panel-title {
         font-weight: normal;
         a:hover {
-            color: $black;
+            color: $color_jungle_green_approx;
             opacity: 0.7;
         }
     }
     .btn-group {
-        margin-top: 3px;
+        margin-top: 4px;
     }
 }
 
@@ -167,5 +167,5 @@
     border-top: none;
 }
 .panel-default.custom-panel>.panel-heading > .btn-group > button {
-    color: $black;
+    color: $color_jungle_green_approx;
 }
diff --git a/dashboardv3/public/js/models/VEntity.js b/dashboardv3/public/js/models/VEntity.js
index deeb6b0..984039d 100644
--- a/dashboardv3/public/js/models/VEntity.js
+++ b/dashboardv3/public/js/models/VEntity.js
@@ -87,7 +87,15 @@ define(['require',
                 dataType: 'json'
             }, options);
             return this.constructor.nonCrudOperation.call(this, url, "", options);
+        },
+        saveEntityLabels: function(guid, options) {
+            var url = UrlLinks.entityLabelsAPIUrl(guid);
+            options = _.extend({
+                contentType: 'application/json',
+                dataType: 'json'
+            }, options);
+            return this.constructor.nonCrudOperation.call(this, url, "POST", options);
         }
     }, {});
     return VEntity;
-});
\ No newline at end of file
+});
diff --git a/dashboardv3/public/js/templates/detail_page/DetailPageLayoutView_tmpl.html b/dashboardv3/public/js/templates/detail_page/DetailPageLayoutView_tmpl.html
index f2e6ca7..25f04d9 100644
--- a/dashboardv3/public/js/templates/detail_page/DetailPageLayoutView_tmpl.html
+++ b/dashboardv3/public/js/templates/detail_page/DetailPageLayoutView_tmpl.html
@@ -78,6 +78,7 @@
                 </div>
                 <div class="col-md-6">
                     <div id="r_entityUserDefineView"></div>
+                    <div id="r_entityLabelDefineView"></div>
                 </div>
             </div>
         </div>
diff --git a/dashboardv3/public/js/templates/entity/EntityLabelDefineView_tmpl.html b/dashboardv3/public/js/templates/entity/EntityLabelDefineView_tmpl.html
new file mode 100644
index 0000000..06d7e0e
--- /dev/null
+++ b/dashboardv3/public/js/templates/entity/EntityLabelDefineView_tmpl.html
@@ -0,0 +1,97 @@
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+
+<div class="panel-group" id="accordion">
+
+    <div class="panel panel-default custom-panel expand_collapse_panel-icon" data-id="userDefineLabel">
+        {{#ifCond labels.length "===" 0}}
+            <div class="panel-heading collapsed" data-toggle="collapse" href="#collapse3" aria-expanded="false" style="width: 70%">
+                <h4 class="panel-title">
+                    <a>Labels </a>
+                </h4>
+                <div class="btn-group pull-left">
+                    <button type="button" title="Collapse"><i class="ec-icon fa"></i></button>
+                </div>
+            </div>
+        {{else}}
+            <div class="panel-heading" data-toggle="collapse" href="#collapse3" aria-expanded="true" style="width: 70%">
+                <h4 class="panel-title">
+                    <a>Labels </a>
+                </h4>
+                <div class="btn-group pull-left">
+                    <button type="button" title="Collapse"><i class="ec-icon fa"></i></button>
+                </div>
+            </div>
+
+        {{/ifCond}}
+
+        {{#ifCond readOnlyEntity "===" false}}
+            <div class="panel-actions">
+                    {{#ifCond  swapItem "!==" true}}
+                        <button class="btn btn-action btn-sm"  data-id="addLabels" {{#ifCond labels.length "===" 0}} data-original-title="Add User-Defined Labels" {{else}} data-original-title="Edit User-Defined Labels"  {{/ifCond}}>
+                            {{#ifCond labels.length "===" 0}} Add {{else}} Edit {{/ifCond}}
+                        </button>
+                    {{/ifCond}}
+                    {{#ifCond saveLabels "===" true}}
+                        <button class="btn btn-action btn-sm"  data-id="saveLabels"  data-original-title="Save User-Defined Labels">Save</button>
+                    {{/ifCond}}
+            </div>
+        {{/ifCond}}
+
+        <div id="collapse3" {{#ifCond swapItem "===" false}} class="panel-collapse collapse" {{else}} class="panel-collapse collapse in" {{/ifCond}}>
+            {{#ifCond labels.length "===" 0}}
+                <div class="panel-body">
+                        <div class="row">
+                            <div class="col-md-12">
+                                {{#ifCond swapItem "===" true}}
+                                    <select class="form-control" data-id="addLabelOptions" multiple="multiple"></select>
+                                {{else}}
+                                <div class="badge-container">
+                                        {{#each labels}}
+                                            <label class="label badge-default">{{this}}</label>
+                                        {{/each}}
+                                </div>
+                                {{/ifCond}}
+                            </div>
+                        </div>
+                </div>
+            {{/ifCond}}
+        </div>
+
+        {{#ifCond labels.length ">" 0}}
+            <div id="collapse3" class="panel-collapse collapse in" >
+                <div class="panel-body">
+                        <div class="row">
+                            <div class="col-md-12">
+                                {{#ifCond swapItem "===" true}}
+                                    <select class="form-control" data-id="addLabelOptions" multiple="multiple"></select>
+                                {{else}}
+                                <div class="badge-container">
+                                        {{#each labels}}
+                                            <label class="label badge-default">{{this}}</label>
+                                        {{/each}}
+                                </div>
+                                {{/ifCond}}
+                            </div>
+                        </div>
+                </div>
+            </div>
+        {{/ifCond}}
+
+
+    </div>
+</div>
diff --git a/dashboardv3/public/js/templates/site/Statistics_tmpl.html b/dashboardv3/public/js/templates/site/Statistics_tmpl.html
index 436767c..f5c6c8b 100644
--- a/dashboardv3/public/js/templates/site/Statistics_tmpl.html
+++ b/dashboardv3/public/js/templates/site/Statistics_tmpl.html
@@ -15,12 +15,12 @@
  * limitations under the License.
 -->
 <div class="panel-group server-stats-container statsContainer hide" id="accordion">
-    <div class="panel panel-default expand_collapse_panel-icon" data-id="entity">
+    <div class="panel panel-default custom-panel expand_collapse_panel-icon" data-id="entity">
         <div class="panel-heading" data-toggle="collapse" href="#collapse1" aria-expanded="true">
             <h4 class="panel-title">
                 <a>Entities <span class="count">(0)</span></a>
             </h4>
-            <div class="btn-group pull-right">
+            <div class="btn-group pull-left">
                 <button type="button" title="Collapse"><i class="ec-icon fa"></i></button>
             </div>
         </div>
@@ -36,12 +36,12 @@
             </div>
         </div>
     </div>
-    <div class="panel panel-default expand_collapse_panel-icon" data-id="stats">
+    <div class="panel panel-default custom-panel expand_collapse_panel-icon" data-id="stats">
         <div class="panel-heading" data-toggle="collapse" href="#collapse3" aria-expanded="true">
             <h4 class="panel-title">
                 <a>Server Statistics </a>
             </h4>
-            <div class="btn-group pull-right">
+            <div class="btn-group pull-left">
                 <button type="button" title="Collapse"><i class="ec-icon fa"></i></button>
             </div>
         </div>
@@ -90,4 +90,4 @@
 </div>
 <div class="fontLoader-relative statsLoader show">
     <i class="fa fa-refresh fa-spin-custom"></i>
-</div>
\ No newline at end of file
+</div>
diff --git a/dashboardv3/public/js/utils/UrlLinks.js b/dashboardv3/public/js/utils/UrlLinks.js
index d619c52..c58c58f 100644
--- a/dashboardv3/public/js/utils/UrlLinks.js
+++ b/dashboardv3/public/js/utils/UrlLinks.js
@@ -79,6 +79,9 @@ define(['require', 'utils/Enums', 'utils/Utils', 'underscore'], function(require
                 return entitiesUrl += '?minExtInfo=' + (minExtInfo);
             }
         },
+        entityLabelsAPIUrl: function(guid) {
+            return this.entitiesApiUrl({ guid: guid }) + "/labels";
+        },
         entityHeaderApiUrl: function(guid) {
             return this.entitiesApiUrl({ guid: guid }) + "/header"
         },
@@ -222,4 +225,4 @@ define(['require', 'utils/Enums', 'utils/Utils', 'underscore'], function(require
     });
 
     return UrlLinks;
-});
\ No newline at end of file
+});
diff --git a/dashboardv3/public/js/views/detail_page/DetailPageLayoutView.js b/dashboardv3/public/js/views/detail_page/DetailPageLayoutView.js
index 31671ac..18031ec 100644
--- a/dashboardv3/public/js/views/detail_page/DetailPageLayoutView.js
+++ b/dashboardv3/public/js/views/detail_page/DetailPageLayoutView.js
@@ -47,6 +47,7 @@ define(['require',
                 RProfileLayoutView: "#r_profileLayoutView",
                 RRelationshipLayoutView: "#r_relationshipLayoutView",
                 REntityUserDefineView: "#r_entityUserDefineView",
+                REntityLabelDefineView: "#r_entityLabelDefineView"
             },
             /** ui selector cache */
             ui: {
@@ -201,7 +202,7 @@ define(['require',
                                 if (collectionJSON.isIncomplete === true) {
                                     this.$(".isIncomplete").addClass("show");
                                 }
-                                this.ui.entityIcon.attr('title', _.escape(collectionJSON.typeName)).html('<img src="' + Utils.getEntityIconPath({ entityData: entityData }) + '"/><i class="fa fa-refresh fa-spin-custom"></i>').find("img").on('error', function() {
+                                this.ui.entityIcon.attr('title', _.escape(collectionJSON.typeName)).html('<img src="' + Utils.getEntityIconPath({ entityData: entityData }) + '"/><i class="fa fa-hourglass-half"></i>').find("img").on('error', function() {
                                     this.src = Utils.getEntityIconPath({ entityData: entityData, errorUrl: this.src });
                                 });
                             } else {
@@ -254,6 +255,7 @@ define(['require',
                     }
                     this.renderEntityDetailTableLayoutView(obj);
                     this.renderEntityUserDefineView(obj);
+                    this.renderEntityLabelDefineView(obj);
                     this.renderRelationshipLayoutView(obj);
                     this.renderAuditTableLayoutView(obj);
                     this.renderTagTableLayoutView(obj);
@@ -507,6 +509,12 @@ define(['require',
                     that.REntityUserDefineView.show(new EntityUserDefineView(obj));
                 });
             },
+            renderEntityLabelDefineView: function(obj) {
+                var that = this;
+                require(['views/entity/EntityLabelDefineView'], function(EntityLabelDefineView) {
+                    that.REntityLabelDefineView.show(new EntityLabelDefineView(obj));
+                });
+            },
             renderTagTableLayoutView: function(obj) {
                 var that = this;
                 require(['views/tag/TagDetailTableLayoutView'], function(TagDetailTableLayoutView) {
diff --git a/dashboardv3/public/js/views/entity/EntityDetailTableLayoutView.js b/dashboardv3/public/js/views/entity/EntityDetailTableLayoutView.js
index 6572292..381d99e 100644
--- a/dashboardv3/public/js/views/entity/EntityDetailTableLayoutView.js
+++ b/dashboardv3/public/js/views/entity/EntityDetailTableLayoutView.js
@@ -83,4 +83,4 @@ define(['require',
             }
         });
     return EntityDetailTableLayoutView;
-});
+});
\ No newline at end of file
diff --git a/dashboardv3/public/js/views/entity/EntityLabelDefineView.js b/dashboardv3/public/js/views/entity/EntityLabelDefineView.js
new file mode 100644
index 0000000..d69b277
--- /dev/null
+++ b/dashboardv3/public/js/views/entity/EntityLabelDefineView.js
@@ -0,0 +1,120 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+define(['require',
+'backbone',
+'hbs!tmpl/entity/EntityLabelDefineView_tmpl',
+'models/VEntity',
+'utils/Utils',
+'utils/Messages',
+'utils/Enums'
+], function(require, Backbone, EntityLabelDefineView_tmpl, VEntity, Utils, Messages, Enums) {
+'use strict';
+
+    return Backbone.Marionette.LayoutView.extend({
+        _viewName: 'REntityLabelDefineView',
+        template: EntityLabelDefineView_tmpl,
+        templateHelpers: function() {
+            return {
+                swapItem: this.swapItem,
+                labels: this.labels,
+                saveLabels: this.saveLabels,
+                readOnlyEntity: this.readOnlyEntity
+            };
+        },
+        ui: {
+            addLabelOptions: "[data-id='addLabelOptions']",
+            addLabels: "[data-id='addLabels']",
+            saveLabels: "[data-id='saveLabels']"
+        },
+        events: function() {
+            var events = {};
+            events["change " + this.ui.addLabelOptions] = 'onChangeLabelChange';
+            events["click " + this.ui.addLabels] = 'handleBtnClick';
+            events["click " + this.ui.saveLabels] = 'saveUserDefinedLabels';
+            return events;
+        },
+        initialize: function(options) {
+            var self = this;
+            _.extend(this, _.pick(options, 'entity'));
+            this.swapItem = false, this.saveLabels = false;
+            this.readOnlyEntity = Enums.entityStateReadOnly[this.entity.status]
+            this.entityModel = new VEntity(this.entity);
+            this.labels = this.entity.labels || [];
+        },
+        onRender: function() {
+            this.populateLabelOptions();
+        },
+        bindEvents: function () {
+        },
+        populateLabelOptions: function() {
+            var that = this,
+            str = this.labels.map(function (label) {
+                return "<option selected > "+ label +" </option>";
+            });
+            this.ui.addLabelOptions.html(str);
+            this.ui.addLabelOptions.select2({
+                placeholder: "Select Label",
+                allowClear: true,
+                tags: true,
+                multiple: true
+            });
+        },
+        onChangeLabelChange: function () {
+            this.labels = this.ui.addLabelOptions.val();
+        },
+        handleBtnClick: function () {
+            this.swapItem = !this.swapItem;
+            this.saveLabels = this.swapItem === true ? true : false;
+            this.render();
+        },
+        saveUserDefinedLabels: function() {
+            var that = this;
+            var entityJson = that.entityModel.toJSON();
+            var payload = this.labels;
+            that.entityModel.saveEntityLabels(entityJson.guid ,{
+                data: JSON.stringify(payload),
+                type: 'POST',
+                success: function() {
+                    var msg = entityJson.labels === undefined ? 'addSuccessMessage' : 'editSuccessMessage';
+                    if (payload.length === 0) {
+                        that.entityModel.unset('labels');
+                    } else {
+                        that.entityModel.set('labels', payload);
+                    }
+                    Utils.notifySuccess({
+                        content: "User-defined labels " + Messages[msg]
+                    });
+                    that.swapItem = false;
+                    that.saveLabels = false;
+                    that.render();
+                },
+                error: function (e) {
+                    that.ui.saveLabels && that.ui.saveLabels.length > 0 &&  that.ui.saveLabels[0].setAttribute("disabled", false);
+                    Utils.notifySuccess({
+                        content: e.message
+                    });
+                },
+                complete: function () {
+                    that.ui.saveLabels && that.ui.saveLabels.length > 0 && that.ui.saveLabels[0].setAttribute("disabled", false);
+                    that.render();
+                }
+            });
+        }
+    });
+});
diff --git a/dashboardv3/public/js/views/entity/EntityUserDefineView.js b/dashboardv3/public/js/views/entity/EntityUserDefineView.js
index 588703f..a23a8fa 100644
--- a/dashboardv3/public/js/views/entity/EntityUserDefineView.js
+++ b/dashboardv3/public/js/views/entity/EntityUserDefineView.js
@@ -81,7 +81,7 @@ define(['require',
                 okText: 'Save',
                 okCloses: false,
                 cancelText: "Cancel",
-                mainClass: 'modal-lg',
+                mainClass: 'modal-md',
                 allowCancel: true,
             };
            this.setAttributeModal(modalObj);