You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@atlas.apache.org by kb...@apache.org on 2020/07/22 10:10:32 UTC

[atlas] branch branch-2.0 updated: ATLAS-3897: UI: Normalize list of propagated classifications

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

kbhatt pushed a commit to branch branch-2.0
in repository https://gitbox.apache.org/repos/asf/atlas.git


The following commit(s) were added to refs/heads/branch-2.0 by this push:
     new 6f3fc5a  ATLAS-3897: UI: Normalize list of propagated classifications
6f3fc5a is described below

commit 6f3fc5ac3c9b14b6ecfea00c71effe2564504119
Author: kevalbhatt <kb...@apache.org>
AuthorDate: Tue Jul 21 16:22:05 2020 +0530

    ATLAS-3897: UI: Normalize list of propagated classifications
    
    (cherry picked from commit ec314fdeb871a479194af5b543a72df76472cb4c)
---
 dashboardv2/public/css/scss/form.scss              |   5 +
 dashboardv2/public/js/router/Router.js             |  34 ++++---
 .../tag/TagDetailTableLayoutView_tmpl.html         |   3 +-
 dashboardv2/public/js/utils/Helper.js              |   2 +-
 .../js/views/detail_page/DetailPageLayoutView.js   |  70 ++++++++++----
 .../js/views/tag/TagDetailTableLayoutView.js       | 104 ++++++++++++++++++---
 dashboardv3/public/css/scss/form.scss              |   5 +
 dashboardv3/public/js/main.js                      |   6 +-
 dashboardv3/public/js/router/Router.js             |  18 +++-
 .../tag/TagDetailTableLayoutView_tmpl.html         |   3 +-
 dashboardv3/public/js/utils/Helper.js              |   2 +-
 .../js/views/detail_page/DetailPageLayoutView.js   |  70 ++++++++++----
 .../js/views/tag/TagDetailTableLayoutView.js       | 104 ++++++++++++++++++---
 13 files changed, 348 insertions(+), 78 deletions(-)

diff --git a/dashboardv2/public/css/scss/form.scss b/dashboardv2/public/css/scss/form.scss
index 853b103..68761e1 100644
--- a/dashboardv2/public/css/scss/form.scss
+++ b/dashboardv2/public/css/scss/form.scss
@@ -369,6 +369,11 @@ button {
         >span {
             padding: 5px;
 
+            &.active {
+                color: $white;
+                background-color: $tag_color;
+            }
+
             &:hover {
                 @include btn-action-hover-effect('default');
             }
diff --git a/dashboardv2/public/js/router/Router.js b/dashboardv2/public/js/router/Router.js
index 5e94597..8e3afa0 100644
--- a/dashboardv2/public/js/router/Router.js
+++ b/dashboardv2/public/js/router/Router.js
@@ -106,11 +106,18 @@ define([
         renderViewIfNotExists: function(options) {
             var view = options.view,
                 render = options.render,
+                viewName = options.viewName,
                 manualRender = options.manualRender;
             if (!view.currentView) {
-                if (render) view.show(options.render());
+                if (render) view.show(options.render(options));
+            } else if (manualRender && viewName) {
+                if (viewName === view.currentView._viewName) {
+                    options.manualRender(options);
+                } else {
+                    if (render) view.show(options.render(options));
+                }
             } else {
-                if (manualRender) options.manualRender();
+                if (manualRender) options.manualRender(options);
             }
         },
 
@@ -149,13 +156,7 @@ define([
         detailPage: function(id) {
             var that = this;
             if (id) {
-                require([
-                    'views/site/Header',
-                    'views/detail_page/DetailPageLayoutView',
-                    'views/site/SideNavLayoutView',
-                    'collection/VEntityList'
-                ], function(Header, DetailPageLayoutView, SideNavLayoutView, VEntityList) {
-                    this.entityCollection = new VEntityList([], {});
+                require(["views/site/Header", "views/detail_page/DetailPageLayoutView", "views/site/SideNavLayoutView"], function(Header, DetailPageLayoutView, SideNavLayoutView) {
                     var paramObj = Utils.getUrlState.getQueryParams(),
                         options = _.extend({}, that.preFetchedCollectionLists, that.sharedObj, that.ventObj);
                     that.renderViewIfNotExists(that.getHeaderOptions(Header));
@@ -168,9 +169,18 @@ define([
                             return new SideNavLayoutView(options);
                         }
                     });
-                    App.rNContent.show(new DetailPageLayoutView(_.extend({ 'collection': this.entityCollection, 'id': id, 'value': paramObj }, options)));
-                    this.entityCollection.url = UrlLinks.entitiesApiUrl({ guid: id, minExtInfo: true });
-                    this.entityCollection.fetch({ reset: true });
+
+                    var dOptions = _.extend({ id: id, value: paramObj }, options);
+                    that.renderViewIfNotExists({
+                        view: App.rNContent,
+                        viewName: "DetailPageLayoutView",
+                        manualRender: function() {
+                            this.view.currentView.manualRender(dOptions);
+                        },
+                        render: function() {
+                            return new DetailPageLayoutView(dOptions);
+                        }
+                    });
                 });
             }
         },
diff --git a/dashboardv2/public/js/templates/tag/TagDetailTableLayoutView_tmpl.html b/dashboardv2/public/js/templates/tag/TagDetailTableLayoutView_tmpl.html
index b84a0d0..c161a2c 100644
--- a/dashboardv2/public/js/templates/tag/TagDetailTableLayoutView_tmpl.html
+++ b/dashboardv2/public/js/templates/tag/TagDetailTableLayoutView_tmpl.html
@@ -16,11 +16,12 @@
 -->
 <div class="position-relative">
     <div class="tableOverlay"></div>
-    <div class="inline-content-fr">
+    <div class="inline-content-fr clearfix" style="margin-bottom: 10px;">
         <div class="inline">
             <label class="checkbox-inline btn">
                 <input type="checkbox" data-id="checkPropagtedTag" class="input" checked="true" name="queryType" value="text" name="check" value="1" /> Show Propagated Classifications</label>
         </div>
+        <div class="pull-left" style="width: 300px;"><select data-id="tagList"></select></div>
     </div>
     <div id="r_tagTableLayoutView"></div>
 </div>
\ No newline at end of file
diff --git a/dashboardv2/public/js/utils/Helper.js b/dashboardv2/public/js/utils/Helper.js
index 931c45d..ead6e2f 100644
--- a/dashboardv2/public/js/utils/Helper.js
+++ b/dashboardv2/public/js/utils/Helper.js
@@ -346,7 +346,7 @@ define(['require',
     });
     if ($('body').tooltip) {
         $('body').tooltip({
-            selector: '[title]:not(".select2-selection__choice")',
+            selector: '[title]:not(".select2-selection__choice,.select2-selection__rendered")',
             placement: function() {
                 return this.$element.attr("data-placement") || "bottom";
             },
diff --git a/dashboardv2/public/js/views/detail_page/DetailPageLayoutView.js b/dashboardv2/public/js/views/detail_page/DetailPageLayoutView.js
index fe823fa..7f8d849 100644
--- a/dashboardv2/public/js/views/detail_page/DetailPageLayoutView.js
+++ b/dashboardv2/public/js/views/detail_page/DetailPageLayoutView.js
@@ -24,8 +24,9 @@ define(['require',
     'utils/Globals',
     'utils/Enums',
     'utils/Messages',
-    'utils/UrlLinks'
-], function(require, Backbone, DetailPageLayoutViewTmpl, Utils, CommonViewFunction, Globals, Enums, Messages, UrlLinks) {
+    'utils/UrlLinks',
+    'collection/VEntityList'
+], function(require, Backbone, DetailPageLayoutViewTmpl, Utils, CommonViewFunction, Globals, Enums, Messages, UrlLinks, VEntityList) {
     'use strict';
 
     var DetailPageLayoutView = Backbone.Marionette.LayoutView.extend(
@@ -52,6 +53,7 @@ define(['require',
             /** ui selector cache */
             ui: {
                 tagClick: '[data-id="tagClick"]',
+                pTagCountClick: '[data-id="pTagCountClick"]',
                 termClick: '[data-id="termClick"]',
                 propagatedTagDiv: '[data-id="propagatedTagDiv"]',
                 title: '[data-id="title"]',
@@ -84,6 +86,14 @@ define(['require',
                         });
                     }
                 };
+                events["click " + this.ui.pTagCountClick] = function(e) {
+                    var tag = $(e.currentTarget).parent().children().first().text();
+                    Utils.setUrl({
+                        url: '#!/detailPage/' + this.id + '?tabActive=classification&filter=' + tag,
+                        mergeBrowserUrl: false,
+                        trigger: true
+                    });
+                };
                 events["click " + this.ui.termClick] = function(e) {
                     if (e.target.nodeName.toLocaleLowerCase() != "i") {
                         Utils.setUrl({
@@ -119,6 +129,9 @@ define(['require',
             initialize: function(options) {
                 _.extend(this, _.pick(options, 'value', 'collection', 'id', 'entityDefCollection', 'typeHeaders', 'enumDefCollection', 'classificationDefCollection', 'glossaryCollection', 'businessMetadataDefCollection', 'searchVent'));
                 $('body').addClass("detail-page");
+                this.collection = new VEntityList([], {});
+                this.collection.url = UrlLinks.entitiesApiUrl({ guid: this.id, minExtInfo: true });
+                this.fetchCollection();
             },
             bindEvents: function() {
                 var that = this;
@@ -215,8 +228,34 @@ define(['require',
                                 this.ui.description.hide();
                             }
                         }
+                        var tags = {
+                            'self': [],
+                            'propagated': [],
+                            'propagatedMap': {},
+                            'combineMap': {}
+                        };
                         if (collectionJSON.classifications) {
-                            this.generateTag(collectionJSON.classifications);
+                            var tagObject = collectionJSON.classifications;
+                            _.each(tagObject, function(val) {
+                                var typeName = val.typeName;
+                                if (val.entityGuid === that.id) {
+                                    tags['self'].push(val)
+                                } else {
+                                    tags['propagated'].push(val);
+                                    if (tags.propagatedMap[typeName]) {
+                                        tags.propagatedMap[typeName]["count"] += tags.propagatedMap[typeName]["count"];
+                                    } else {
+                                        tags.propagatedMap[typeName] = val;
+                                        tags.propagatedMap[typeName]["count"] = 1;
+                                    }
+                                }
+                                if (tags.combineMap[typeName] === undefined) {
+                                    tags.combineMap[typeName] = val;
+                                }
+                            });
+                            tags.self = _.sortBy(tags.self, "typeName");
+                            tags.propagated = _.sortBy(tags.propagated, "typeName");
+                            this.generateTag(tags);
                         } else {
                             this.generateTag([]);
                         }
@@ -243,6 +282,7 @@ define(['require',
                         guid: this.id,
                         entityName: this.name,
                         typeHeaders: this.typeHeaders,
+                        tags: tags,
                         entityDefCollection: this.entityDefCollection,
                         fetchCollection: this.fetchCollection.bind(that),
                         enumDefCollection: this.enumDefCollection,
@@ -340,6 +380,13 @@ define(['require',
                 Utils.showTitleLoader(this.$('.page-title .fontLoader'), this.$('.entityDetail'));
                 this.$('.fontLoader-relative').addClass('show'); // to show tab loader
             },
+            manualRender: function(options) {
+                if (this.id !== options.id) {
+                    _.extend(this, _.pick(options, 'value', 'id'));
+                    this.collection.url = UrlLinks.entitiesApiUrl({ guid: this.id, minExtInfo: true });
+                    this.fetchCollection();
+                }
+            },
             onShow: function() {
                 if (this.value && this.value.tabActive) {
                     this.$('.nav.nav-tabs').find('[role="' + this.value.tabActive + '"]').addClass('active').siblings().removeClass('active');
@@ -416,27 +463,18 @@ define(['require',
             generateTag: function(tagObject) {
                 var that = this,
                     tagData = "",
-                    propagatedTagListData = "",
-                    tag = {
-                        'self': [],
-                        'propagated': []
-                    };
-                _.each(tagObject, function(val) {
-                    val.entityGuid === that.id ? tag['self'].push(val) : tag['propagated'].push(val);
-                });
-                _.each(tag.self, function(val) {
+                    propagatedTagListData = "";
+                _.each(tagObject.self, function(val) {
                     tagData += '<span class="btn btn-action btn-sm btn-icon btn-blue" data-id="tagClick"><span>' + val.typeName + '</span><i class="fa fa-close" data-id="deleteTag" data-type="tag" title="Remove Classification"></i></span>';
                 });
-                _.each(tag.propagated, function(val) {
-                    var crossButton = '<i class="fa fa-close" data-id="deleteTag" data-entityguid="' + val.entityGuid + '" data-type="tag" title="Remove Classification"></i>';
-                    propagatedTagListData += '<span class="btn btn-action btn-sm btn-icon btn-blue" data-id="tagClick"><span>' + val.typeName + '</span>' + ((that.id !== val.entityGuid && val.entityStatus === "DELETED") ? crossButton : "") + '</span>';
+                _.each(tagObject.propagatedMap, function(val, key) {
+                    propagatedTagListData += '<span class="btn btn-action btn-sm btn-icon btn-blue"><span data-id="tagClick">' + val.typeName + '</span>' + (val.count > 1 ? '<span class="active" data-id="pTagCountClick">(' + val.count + ')</span>' : "") + '</span>';
                 });
                 propagatedTagListData !== "" ? this.ui.propagatedTagDiv.show() : this.ui.propagatedTagDiv.hide();
                 this.ui.tagList.find("span.btn").remove();
                 this.ui.propagatedTagList.find("span.btn").remove();
                 this.ui.tagList.prepend(tagData);
                 this.ui.propagatedTagList.html(propagatedTagListData);
-
             },
             generateTerm: function(data) {
                 var that = this,
diff --git a/dashboardv2/public/js/views/tag/TagDetailTableLayoutView.js b/dashboardv2/public/js/views/tag/TagDetailTableLayoutView.js
index 7422363..6771182 100644
--- a/dashboardv2/public/js/views/tag/TagDetailTableLayoutView.js
+++ b/dashboardv2/public/js/views/tag/TagDetailTableLayoutView.js
@@ -42,6 +42,7 @@ define(['require',
             /** ui selector cache */
             ui: {
                 detailValue: "[data-id='detailValue']",
+                tagList: "[data-id='tagList']",
                 addTag: "[data-id='addTag']",
                 deleteTag: "[data-id='delete']",
                 editTag: "[data-id='edit']",
@@ -75,12 +76,20 @@ define(['require',
              * @constructs
              */
             initialize: function(options) {
-                _.extend(this, _.pick(options, 'entity', 'guid', 'entityName', 'fetchCollection', 'enumDefCollection', 'classificationDefCollection'));
+                _.extend(this, _.pick(options, 'entity', 'guid', 'tags', 'entityName', 'fetchCollection', 'enumDefCollection', 'classificationDefCollection'));
                 this.collectionObject = this.entity;
                 this.tagCollection = new VTagList();
-                var that = this,
-                    tags = _.toArray(this.collectionObject.classifications);
-                this.tagCollection.fullCollection.reset(tags);
+                this.allTags = _.sortBy(_.toArray(this.collectionObject.classifications), "typeName");
+                var paramObj = Utils.getUrlState.getQueryParams();
+                this.value = { tabActive: "classification", showPC: "true", filter: "all" };
+                if (paramObj) {
+                    if (paramObj.showPC) {
+                        this.value.showPC = paramObj.showPC;
+                    }
+                    if (paramObj.filter) {
+                        this.value.filter = paramObj.filter;
+                    }
+                }
                 this.commonTableOptions = {
                     collection: this.tagCollection,
                     includeFilter: false,
@@ -99,8 +108,81 @@ define(['require',
             },
             bindEvents: function() {},
             onRender: function() {
+                this.ui.checkPropagtedTag.prop("checked", this.value.showPC === "true");
+                this.renderFilter();
+                if (this.tagNotFound === undefined && this.value.filter === "all") {
+                    this.updateCollection(this.value.filter);
+                }
                 this.renderTableLayoutView();
             },
+            updateCollection: function(value) {
+                var newList = null
+                if (this.value.showPC === "true") {
+                    if (value === "all") {
+                        newList = this.allTags;
+                    } else {
+                        newList = _.filter(this.allTags, function(obj) {
+                            if (obj.typeName === value) {
+                                return true;
+                            }
+                        });
+                    }
+                } else {
+                    if (value === "all") {
+                        newList = this.tags.self;
+                    } else {
+                        newList = _.filter(this.tags.self, function(obj) {
+                            if (obj.typeName === value) {
+                                return true;
+                            }
+                        });
+                    }
+                }
+                this.tagCollection.fullCollection.reset(newList);
+                if (value) {
+                    this.value["filter"] = value;
+                    this.triggetUrl();
+                }
+            },
+            triggetUrl: function() {
+                var paramObj = Utils.getUrlState.getQueryParams();
+                if (paramObj && paramObj.tabActive === "classification") {
+                    Utils.setUrl({
+                        url: '#!/detailPage/' + this.guid,
+                        mergeBrowserUrl: false,
+                        urlParams: this.value,
+                        trigger: false
+                    });
+                }
+            },
+            renderFilter: function() {
+                var tagName = "<option value='all' selected>All</option>",
+                    that = this;
+                if (this.value.showPC === "false") {
+                    _.each(this.tags.self, function(val) {
+                        var typeName = val.typeName;
+                        tagName += '<option value="' + typeName + '">' + typeName + '</option>';
+                    });
+                } else {
+                    _.each(this.tags.combineMap, function(val, key) {
+                        tagName += '<option value="' + key + '">' + key + '</option>';
+                    });
+                }
+                this.ui.tagList.html(tagName);
+                if (this.value.filter && this.ui.tagList.val() !== this.value.filter) {
+                    if (this.ui.tagList.find("option[value='" + this.value.filter + "']").length > 0) {
+                        this.ui.tagList.val(this.value.filter);
+                        this.updateCollection(this.value.filter);
+                    } else {
+                        this.tagNotFound = true;
+                        this.updateCollection("all");
+                    }
+                }
+                this.ui.tagList.select2({ placeholder: "Search for tag" }).on("change", function() {
+                    var value = that.ui.tagList.val();
+                    that.updateCollection(value);
+                });
+            },
             renderTableLayoutView: function() {
                 var that = this;
                 require(['utils/TableLayout'], function(TableLayout) {
@@ -124,7 +206,7 @@ define(['require',
                             formatter: _.extend({}, Backgrid.CellFormatter.prototype, {
                                 fromRaw: function(rawValue, model) {
                                     if (that.guid !== model.get('entityGuid')) {
-                                        var purgeEntityBtn = (Enums.isEntityPurged[model.get('entityStatus')]) ? ' title="Entity not available" disabled' : ' title="Propagated From" data-id="propagatedFromClick"',
+                                        var purgeEntityBtn = (Enums.isEntityPurged[model.get('entityStatus')]) ? ' title="Entity not available" disabled' : ' data-id="propagatedFromClick"',
                                             propagtedFrom = ' <span class="btn btn-action btn-sm btn-icon btn-blue" data-guid=' + model.get('entityGuid') + purgeEntityBtn + '><span> Propagated From </span></span>';
                                         return '<a title="" href="#!/tag/tagAttribute/' + model.get('typeName') + '">' + model.get('typeName') + '</a>' + propagtedFrom;
                                     } else {
@@ -248,17 +330,17 @@ define(['require',
             },
             onCheckPropagtedTag: function(e) {
                 var that = this,
-                    tags = _.toArray(that.collectionObject.classifications),
                     unPropagatedTags = [];
                 e.stopPropagation();
                 if (e.target.checked) {
-                    that.tagCollection.fullCollection.reset(tags);
+                    that.tagCollection.fullCollection.reset(this.allTags);
                 } else {
-                    unPropagatedTags = _.filter(tags, function(val) {
-                        return that.guid === val.entityGuid;
-                    });
-                    that.tagCollection.fullCollection.reset(unPropagatedTags);
+                    that.tagCollection.fullCollection.reset(this.tags.self);
                 }
+                this.value.showPC = "" + e.target.checked;
+                this.value.filter = "all";
+                this.triggetUrl();
+                this.renderFilter(e.target.checked);
             }
         });
     return TagDetailTableLayoutView;
diff --git a/dashboardv3/public/css/scss/form.scss b/dashboardv3/public/css/scss/form.scss
index 6fa9a07..d1ccafc 100644
--- a/dashboardv3/public/css/scss/form.scss
+++ b/dashboardv3/public/css/scss/form.scss
@@ -393,6 +393,11 @@ button {
         >span {
             padding: 5px;
 
+            &.active {
+                color: $white;
+                background-color: $tag_color;
+            }
+
             &:hover {
                 @include btn-action-hover-effect('default');
             }
diff --git a/dashboardv3/public/js/main.js b/dashboardv3/public/js/main.js
index 94cec01..d5c6787 100644
--- a/dashboardv3/public/js/main.js
+++ b/dashboardv3/public/js/main.js
@@ -51,17 +51,17 @@ require.config({
     'deps': ['marionette'],
 
     onNodeCreated: function(node, config, moduleName, url) {
-        console.log("module " + moduleName + " is about to be loaded");
+        //console.log("module " + moduleName + " is about to be loaded");
         ++modulesLoadCount;
         showModuleLoader();
         node.addEventListener("load", function() {
-            console.log("module " + moduleName + " has been loaded");
+            //console.log("module " + moduleName + " has been loaded");
             --modulesLoadCount;
             hideModuleLoader();
         });
 
         node.addEventListener("error", function() {
-            console.log("module " + moduleName + " could not be loaded");
+            //console.log("module " + moduleName + " could not be loaded");
             --modulesLoadCount;
             hideModuleLoader();
         });
diff --git a/dashboardv3/public/js/router/Router.js b/dashboardv3/public/js/router/Router.js
index 11afd88..69f39f5 100644
--- a/dashboardv3/public/js/router/Router.js
+++ b/dashboardv3/public/js/router/Router.js
@@ -177,8 +177,7 @@ define([
         detailPage: function(id) {
             var that = this;
             if (id) {
-                require(["views/site/Header", "views/detail_page/DetailPageLayoutView", "collection/VEntityList", "views/site/SideNavLayoutView"], function(Header, DetailPageLayoutView, VEntityList, SideNavLayoutView) {
-                    this.entityCollection = new VEntityList([], {});
+                require(["views/site/Header", "views/detail_page/DetailPageLayoutView", "views/site/SideNavLayoutView"], function(Header, DetailPageLayoutView, SideNavLayoutView) {
                     var paramObj = Utils.getUrlState.getQueryParams(),
                         options = _.extend({}, that.preFetchedCollectionLists, that.sharedObj, that.ventObj);
                     that.renderViewIfNotExists(that.getHeaderOptions(Header));
@@ -191,9 +190,18 @@ define([
                             return new SideNavLayoutView(options);
                         }
                     });
-                    App.rContent.show(new DetailPageLayoutView(_.extend({ collection: this.entityCollection, id: id, value: paramObj }, options)));
-                    this.entityCollection.url = UrlLinks.entitiesApiUrl({ guid: id, minExtInfo: true });
-                    this.entityCollection.fetch({ reset: true });
+
+                    var dOptions = _.extend({ id: id, value: paramObj }, options);
+                    that.renderViewIfNotExists({
+                        view: App.rContent,
+                        viewName: "DetailPageLayoutView",
+                        manualRender: function() {
+                            this.view.currentView.manualRender(dOptions);
+                        },
+                        render: function() {
+                            return new DetailPageLayoutView(dOptions);
+                        }
+                    });
                 });
             }
         },
diff --git a/dashboardv3/public/js/templates/tag/TagDetailTableLayoutView_tmpl.html b/dashboardv3/public/js/templates/tag/TagDetailTableLayoutView_tmpl.html
index b84a0d0..c161a2c 100644
--- a/dashboardv3/public/js/templates/tag/TagDetailTableLayoutView_tmpl.html
+++ b/dashboardv3/public/js/templates/tag/TagDetailTableLayoutView_tmpl.html
@@ -16,11 +16,12 @@
 -->
 <div class="position-relative">
     <div class="tableOverlay"></div>
-    <div class="inline-content-fr">
+    <div class="inline-content-fr clearfix" style="margin-bottom: 10px;">
         <div class="inline">
             <label class="checkbox-inline btn">
                 <input type="checkbox" data-id="checkPropagtedTag" class="input" checked="true" name="queryType" value="text" name="check" value="1" /> Show Propagated Classifications</label>
         </div>
+        <div class="pull-left" style="width: 300px;"><select data-id="tagList"></select></div>
     </div>
     <div id="r_tagTableLayoutView"></div>
 </div>
\ No newline at end of file
diff --git a/dashboardv3/public/js/utils/Helper.js b/dashboardv3/public/js/utils/Helper.js
index 8f97a84..d21556e 100644
--- a/dashboardv3/public/js/utils/Helper.js
+++ b/dashboardv3/public/js/utils/Helper.js
@@ -366,7 +366,7 @@ define(['require',
     });
     if ($('body').tooltip) {
         $('body').tooltip({
-            selector: '[title]:not(".select2-selection__choice")',
+            selector: '[title]:not(".select2-selection__choice,.select2-selection__rendered")',
             placement: function() {
                 return this.$element.attr("data-placement") || "bottom";
             },
diff --git a/dashboardv3/public/js/views/detail_page/DetailPageLayoutView.js b/dashboardv3/public/js/views/detail_page/DetailPageLayoutView.js
index 41cf400..2a95974 100644
--- a/dashboardv3/public/js/views/detail_page/DetailPageLayoutView.js
+++ b/dashboardv3/public/js/views/detail_page/DetailPageLayoutView.js
@@ -24,8 +24,9 @@ define(['require',
     'utils/Globals',
     'utils/Enums',
     'utils/Messages',
-    'utils/UrlLinks'
-], function(require, Backbone, DetailPageLayoutViewTmpl, Utils, CommonViewFunction, Globals, Enums, Messages, UrlLinks) {
+    'utils/UrlLinks',
+    'collection/VEntityList'
+], function(require, Backbone, DetailPageLayoutViewTmpl, Utils, CommonViewFunction, Globals, Enums, Messages, UrlLinks, VEntityList) {
     'use strict';
 
     var DetailPageLayoutView = Backbone.Marionette.LayoutView.extend(
@@ -52,6 +53,7 @@ define(['require',
             /** ui selector cache */
             ui: {
                 tagClick: '[data-id="tagClick"]',
+                pTagCountClick: '[data-id="pTagCountClick"]',
                 termClick: '[data-id="termClick"]',
                 propagatedTagDiv: '[data-id="propagatedTagDiv"]',
                 title: '[data-id="title"]',
@@ -85,6 +87,14 @@ define(['require',
                         });
                     }
                 };
+                events["click " + this.ui.pTagCountClick] = function(e) {
+                    var tag = $(e.currentTarget).parent().children().first().text();
+                    Utils.setUrl({
+                        url: '#!/detailPage/' + this.id + '?tabActive=classification&filter=' + tag,
+                        mergeBrowserUrl: false,
+                        trigger: true
+                    });
+                };
                 events["click " + this.ui.termClick] = function(e) {
                     if (e.target.nodeName.toLocaleLowerCase() != "i") {
                         Utils.setUrl({
@@ -123,6 +133,9 @@ define(['require',
             initialize: function(options) {
                 _.extend(this, _.pick(options, 'value', 'collection', 'id', 'entityDefCollection', 'typeHeaders', 'enumDefCollection', 'classificationDefCollection', 'glossaryCollection', 'businessMetadataDefCollection', 'searchVent'));
                 $('body').addClass("detail-page");
+                this.collection = new VEntityList([], {});
+                this.collection.url = UrlLinks.entitiesApiUrl({ guid: this.id, minExtInfo: true });
+                this.fetchCollection();
             },
             bindEvents: function() {
                 var that = this;
@@ -219,8 +232,34 @@ define(['require',
                                 this.ui.description.hide();
                             }
                         }
+                        var tags = {
+                            'self': [],
+                            'propagated': [],
+                            'propagatedMap': {},
+                            'combineMap': {}
+                        };
                         if (collectionJSON.classifications) {
-                            this.generateTag(collectionJSON.classifications);
+                            var tagObject = collectionJSON.classifications;
+                            _.each(tagObject, function(val) {
+                                var typeName = val.typeName;
+                                if (val.entityGuid === that.id) {
+                                    tags['self'].push(val)
+                                } else {
+                                    tags['propagated'].push(val);
+                                    if (tags.propagatedMap[typeName]) {
+                                        tags.propagatedMap[typeName]["count"] += tags.propagatedMap[typeName]["count"];
+                                    } else {
+                                        tags.propagatedMap[typeName] = val;
+                                        tags.propagatedMap[typeName]["count"] = 1;
+                                    }
+                                }
+                                if (tags.combineMap[typeName] === undefined) {
+                                    tags.combineMap[typeName] = val;
+                                }
+                            });
+                            tags.self = _.sortBy(tags.self, "typeName");
+                            tags.propagated = _.sortBy(tags.propagated, "typeName");
+                            this.generateTag(tags);
                         } else {
                             this.generateTag([]);
                         }
@@ -247,6 +286,7 @@ define(['require',
                         guid: this.id,
                         entityName: this.name,
                         typeHeaders: this.typeHeaders,
+                        tags: tags,
                         entityDefCollection: this.entityDefCollection,
                         fetchCollection: this.fetchCollection.bind(that),
                         enumDefCollection: this.enumDefCollection,
@@ -344,6 +384,13 @@ define(['require',
                 Utils.showTitleLoader(this.$('.page-title .fontLoader'), this.$('.entityDetail'));
                 this.$('.fontLoader-relative').addClass('show'); // to show tab loader
             },
+            manualRender: function(options) {
+                if (this.id !== options.id) {
+                    _.extend(this, _.pick(options, 'value', 'id'));
+                    this.collection.url = UrlLinks.entitiesApiUrl({ guid: this.id, minExtInfo: true });
+                    this.fetchCollection();
+                }
+            },
             onShow: function() {
                 if (this.value && this.value.tabActive) {
                     this.$('.nav.nav-tabs').find('[role="' + this.value.tabActive + '"]').addClass('active').siblings().removeClass('active');
@@ -420,27 +467,18 @@ define(['require',
             generateTag: function(tagObject) {
                 var that = this,
                     tagData = "",
-                    propagatedTagListData = "",
-                    tag = {
-                        'self': [],
-                        'propagated': []
-                    };
-                _.each(tagObject, function(val) {
-                    val.entityGuid === that.id ? tag['self'].push(val) : tag['propagated'].push(val);
-                });
-                _.each(tag.self, function(val) {
+                    propagatedTagListData = "";
+                _.each(tagObject.self, function(val) {
                     tagData += '<span class="btn btn-action btn-sm btn-icon btn-blue" data-id="tagClick"><span>' + val.typeName + '</span><i class="fa fa-close" data-id="deleteTag" data-type="tag" title="Remove Classification"></i></span>';
                 });
-                _.each(tag.propagated, function(val) {
-                    var crossButton = '<i class="fa fa-close" data-id="deleteTag" data-entityguid="' + val.entityGuid + '" data-type="tag" title="Remove Classification"></i>';
-                    propagatedTagListData += '<span class="btn btn-action btn-sm btn-icon btn-blue" data-id="tagClick"><span>' + val.typeName + '</span>' + ((that.id !== val.entityGuid && val.entityStatus === "DELETED") ? crossButton : "") + '</span>';
+                _.each(tagObject.propagatedMap, function(val, key) {
+                    propagatedTagListData += '<span class="btn btn-action btn-sm btn-icon btn-blue"><span data-id="tagClick">' + val.typeName + '</span>' + (val.count > 1 ? '<span class="active" data-id="pTagCountClick">(' + val.count + ')</span>' : "") + '</span>';
                 });
                 propagatedTagListData !== "" ? this.ui.propagatedTagDiv.show() : this.ui.propagatedTagDiv.hide();
                 this.ui.tagList.find("span.btn").remove();
                 this.ui.propagatedTagList.find("span.btn").remove();
                 this.ui.tagList.prepend(tagData);
                 this.ui.propagatedTagList.html(propagatedTagListData);
-
             },
             generateTerm: function(data) {
                 var that = this,
diff --git a/dashboardv3/public/js/views/tag/TagDetailTableLayoutView.js b/dashboardv3/public/js/views/tag/TagDetailTableLayoutView.js
index fd84dbb..c396338 100644
--- a/dashboardv3/public/js/views/tag/TagDetailTableLayoutView.js
+++ b/dashboardv3/public/js/views/tag/TagDetailTableLayoutView.js
@@ -42,6 +42,7 @@ define(['require',
             /** ui selector cache */
             ui: {
                 detailValue: "[data-id='detailValue']",
+                tagList: "[data-id='tagList']",
                 addTag: "[data-id='addTag']",
                 deleteTag: "[data-id='delete']",
                 editTag: "[data-id='edit']",
@@ -75,12 +76,20 @@ define(['require',
              * @constructs
              */
             initialize: function(options) {
-                _.extend(this, _.pick(options, 'entity', 'guid', 'entityName', 'fetchCollection', 'enumDefCollection', 'classificationDefCollection', 'searchVent'));
+                _.extend(this, _.pick(options, 'entity', 'guid', 'tags', 'entityName', 'fetchCollection', 'enumDefCollection', 'classificationDefCollection', 'searchVent'));
                 this.collectionObject = this.entity;
                 this.tagCollection = new VTagList();
-                var that = this,
-                    tags = _.toArray(this.collectionObject.classifications);
-                this.tagCollection.fullCollection.reset(tags);
+                this.allTags = _.sortBy(_.toArray(this.collectionObject.classifications), "typeName");
+                var paramObj = Utils.getUrlState.getQueryParams();
+                this.value = { tabActive: "classification", showPC: "true", filter: "all" };
+                if (paramObj) {
+                    if (paramObj.showPC) {
+                        this.value.showPC = paramObj.showPC;
+                    }
+                    if (paramObj.filter) {
+                        this.value.filter = paramObj.filter;
+                    }
+                }
                 this.commonTableOptions = {
                     collection: this.tagCollection,
                     includeFilter: false,
@@ -99,8 +108,81 @@ define(['require',
             },
             bindEvents: function() {},
             onRender: function() {
+                this.ui.checkPropagtedTag.prop("checked", this.value.showPC === "true");
+                this.renderFilter();
+                if (this.tagNotFound === undefined && this.value.filter === "all") {
+                    this.updateCollection(this.value.filter);
+                }
                 this.renderTableLayoutView();
             },
+            updateCollection: function(value) {
+                var newList = null
+                if (this.value.showPC === "true") {
+                    if (value === "all") {
+                        newList = this.allTags;
+                    } else {
+                        newList = _.filter(this.allTags, function(obj) {
+                            if (obj.typeName === value) {
+                                return true;
+                            }
+                        });
+                    }
+                } else {
+                    if (value === "all") {
+                        newList = this.tags.self;
+                    } else {
+                        newList = _.filter(this.tags.self, function(obj) {
+                            if (obj.typeName === value) {
+                                return true;
+                            }
+                        });
+                    }
+                }
+                this.tagCollection.fullCollection.reset(newList);
+                if (value) {
+                    this.value["filter"] = value;
+                    this.triggetUrl();
+                }
+            },
+            triggetUrl: function() {
+                var paramObj = Utils.getUrlState.getQueryParams();
+                if (paramObj && paramObj.tabActive === "classification") {
+                    Utils.setUrl({
+                        url: '#!/detailPage/' + this.guid,
+                        mergeBrowserUrl: false,
+                        urlParams: this.value,
+                        trigger: false
+                    });
+                }
+            },
+            renderFilter: function() {
+                var tagName = "<option value='all' selected>All</option>",
+                    that = this;
+                if (this.value.showPC === "false") {
+                    _.each(this.tags.self, function(val) {
+                        var typeName = val.typeName;
+                        tagName += '<option value="' + typeName + '">' + typeName + '</option>';
+                    });
+                } else {
+                    _.each(this.tags.combineMap, function(val, key) {
+                        tagName += '<option value="' + key + '">' + key + '</option>';
+                    });
+                }
+                this.ui.tagList.html(tagName);
+                if (this.value.filter && this.ui.tagList.val() !== this.value.filter) {
+                    if (this.ui.tagList.find("option[value='" + this.value.filter + "']").length > 0) {
+                        this.ui.tagList.val(this.value.filter);
+                        this.updateCollection(this.value.filter);
+                    } else {
+                        this.tagNotFound = true;
+                        this.updateCollection("all");
+                    }
+                }
+                this.ui.tagList.select2({ placeholder: "Search for tag" }).on("change", function() {
+                    var value = that.ui.tagList.val();
+                    that.updateCollection(value);
+                });
+            },
             renderTableLayoutView: function() {
                 var that = this;
                 require(['utils/TableLayout'], function(TableLayout) {
@@ -124,7 +206,7 @@ define(['require',
                             formatter: _.extend({}, Backgrid.CellFormatter.prototype, {
                                 fromRaw: function(rawValue, model) {
                                     if (that.guid !== model.get('entityGuid')) {
-                                        var purgeEntityBtn = (Enums.isEntityPurged[model.get('entityStatus')]) ? ' title="Entity not available" disabled' : ' title="Propagated From" data-id="propagatedFromClick"',
+                                        var purgeEntityBtn = (Enums.isEntityPurged[model.get('entityStatus')]) ? ' title="Entity not available" disabled' : ' data-id="propagatedFromClick"',
                                             propagtedFrom = ' <span class="btn btn-action btn-sm btn-icon btn-blue" data-guid=' + model.get('entityGuid') + purgeEntityBtn + '><span> Propagated From </span></span>';
                                         return '<a title="" href="#!/tag/tagAttribute/' + model.get('typeName') + '">' + model.get('typeName') + '</a>' + propagtedFrom;
                                     } else {
@@ -251,17 +333,17 @@ define(['require',
             },
             onCheckPropagtedTag: function(e) {
                 var that = this,
-                    tags = _.toArray(that.collectionObject.classifications),
                     unPropagatedTags = [];
                 e.stopPropagation();
                 if (e.target.checked) {
-                    that.tagCollection.fullCollection.reset(tags);
+                    that.tagCollection.fullCollection.reset(this.allTags);
                 } else {
-                    unPropagatedTags = _.filter(tags, function(val) {
-                        return that.guid === val.entityGuid;
-                    });
-                    that.tagCollection.fullCollection.reset(unPropagatedTags);
+                    that.tagCollection.fullCollection.reset(this.tags.self);
                 }
+                this.value.showPC = "" + e.target.checked;
+                this.value.filter = "all";
+                this.triggetUrl();
+                this.renderFilter(e.target.checked);
             }
         });
     return TagDetailTableLayoutView;