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
commit bad7a89e605beca3ca84f9aa38f00a0a09698693 Author: kevalbhatt <[email protected]> AuthorDate: Fri Feb 14 17:34:53 2020 +0530 ATLAS-3554 : UI: changes to display/add/update/delete namespace attributes for entities in entity details page (cherry picked from commit 70ebb22829a4dae8f06031b045f4848a4c22fc05) --- dashboardv3/public/css/scss/common.scss | 1 + dashboardv3/public/css/scss/panel.scss | 2 +- dashboardv3/public/js/models/VEntity.js | 10 +- dashboardv3/public/js/modules/Helpers.js | 6 + .../detail_page/DetailPageLayoutView_tmpl.html | 1 + .../entity/EntityNameSpaceItemView_tmpl.html | 40 +++ .../templates/entity/EntityNameSpaceView_tmpl.html | 44 +++ .../entity/EntityUserDefineItemView_tmpl.html | 2 +- dashboardv3/public/js/utils/UrlLinks.js | 5 + .../js/views/detail_page/DetailPageLayoutView.js | 13 +- .../js/views/entity/EntityNameSpaceItemView.js | 297 +++++++++++++++++++++ .../public/js/views/entity/EntityNameSpaceView.js | 251 +++++++++++++++++ 12 files changed, 667 insertions(+), 5 deletions(-) diff --git a/dashboardv3/public/css/scss/common.scss b/dashboardv3/public/css/scss/common.scss index aa20b70..016c8bf 100644 --- a/dashboardv3/public/css/scss/common.scss +++ b/dashboardv3/public/css/scss/common.scss @@ -248,6 +248,7 @@ pre { .custom-col-1 { width: 39%; + margin-bottom: 10px; } .custom-col-2 { diff --git a/dashboardv3/public/css/scss/panel.scss b/dashboardv3/public/css/scss/panel.scss index 9d38a3e..66adc26 100644 --- a/dashboardv3/public/css/scss/panel.scss +++ b/dashboardv3/public/css/scss/panel.scss @@ -159,9 +159,9 @@ .panel-default.custom-panel>.panel-actions { float: right; margin-top: 15px; + margin-right: 10px; button { - margin-right: 10px; margin-top: -4px; } } diff --git a/dashboardv3/public/js/models/VEntity.js b/dashboardv3/public/js/models/VEntity.js index 84a185f..e9ea1f3 100644 --- a/dashboardv3/public/js/models/VEntity.js +++ b/dashboardv3/public/js/models/VEntity.js @@ -107,7 +107,15 @@ define(['require', deleteNameSpace: function(options) { var url = UrlLinks.nameSpaceUpdateUrl(options.typeName); return this.constructor.nonCrudOperation.call(this, url, 'DELETE', options); - } + }, + saveNamespaceEntity: function(guid, options) { + var url = UrlLinks.entitiesNamespaceApiUrl(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/modules/Helpers.js b/dashboardv3/public/js/modules/Helpers.js index 2f36a0d..7ef5f25 100644 --- a/dashboardv3/public/js/modules/Helpers.js +++ b/dashboardv3/public/js/modules/Helpers.js @@ -89,6 +89,12 @@ define(['require', case '>=': return (v1 >= v2) ? options.fn(this) : options.inverse(this); break; + case 'isEmpty': + return (_.isEmpty(v1)) ? options.fn(this) : options.inverse(this); + break; + case 'has': + return (_.has(v1, v2)) ? options.fn(this) : options.inverse(this); + break; default: return options.inverse(this); break; diff --git a/dashboardv3/public/js/templates/detail_page/DetailPageLayoutView_tmpl.html b/dashboardv3/public/js/templates/detail_page/DetailPageLayoutView_tmpl.html index 7c5a43e..1eb68e3 100644 --- a/dashboardv3/public/js/templates/detail_page/DetailPageLayoutView_tmpl.html +++ b/dashboardv3/public/js/templates/detail_page/DetailPageLayoutView_tmpl.html @@ -79,6 +79,7 @@ <div class="col-md-6"> <div id="r_entityUserDefineView"></div> <div id="r_entityLabelDefineView"></div> + <div id="r_entityNameSpaceView"></div> </div> </div> </div> diff --git a/dashboardv3/public/js/templates/entity/EntityNameSpaceItemView_tmpl.html b/dashboardv3/public/js/templates/entity/EntityNameSpaceItemView_tmpl.html new file mode 100644 index 0000000..d030ee6 --- /dev/null +++ b/dashboardv3/public/js/templates/entity/EntityNameSpaceItemView_tmpl.html @@ -0,0 +1,40 @@ +<!-- + * 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. +--> +{{#ifCond model "has" "isNew"}} +<td class="custom-col-1"> + {{{callmyfunction getNamespaceDroupdown nameSpaceCollection}}} +</td> +<td class="custom-col-0"> : </td> +<td class="custom-col-1" data-id="value"> + <input type="text" data-key disabled class="form-control"> +</td> +<td class="custom-col-2 btn-group"> + <button class="btn btn-default btn-sm" data-id="deleteItem"> + <i class="fa fa-minus"> </i> + </button> + <button class="btn btn-default btn-sm" data-id="addItem"> + <i class="fa fa-plus"> </i> + </button> +</td> +{{else}} +<hr /> +<ul class="namespace-tree-parent"> + <li>{{model.__internal_UI_nameSpaceName}}</li> + <li class="namespace-tree-child" data-id="namespaceTreeChild"> + </li> +</ul> +{{/ifCond}} \ No newline at end of file diff --git a/dashboardv3/public/js/templates/entity/EntityNameSpaceView_tmpl.html b/dashboardv3/public/js/templates/entity/EntityNameSpaceView_tmpl.html new file mode 100644 index 0000000..6b622d7 --- /dev/null +++ b/dashboardv3/public/js/templates/entity/EntityNameSpaceView_tmpl.html @@ -0,0 +1,44 @@ +<!-- + * 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 panel-default custom-panel expand_collapse_panel-icon" data-id="namespace"> + <div class="panel-heading" data-toggle="collapse" href="#namespaceCollapse" aria-expanded="false" style="width: 70%;"> + <h4 class="panel-title"> + <a>Namespaces</a> + </h4> + <div class="btn-group pull-left"> + <button type="button" title="Collapse"><i class="ec-icon fa"></i></button> + </div> + </div> + <div class="panel-actions"> + <button class="btn btn-action btn-sm" title="" data-id="addNameSpace">Add</button> + <button class="btn btn-action btn-sm" style="display: none;" data-id="saveNameSpace">Save</button> + <button class="btn btn-action btn-sm" style="display: none;" data-id="cancel">Cancel</button> + </div> + <div id="namespaceCollapse" class="panel-collapse collapse"> + <div class="panel-body"> + <div data-id="namespaceTree"></div> + <div class="editBox" style="display: none;"> + <div class="form-group"> + <a href="javascript:void(0)" class="btn btn-action btn-sm" data-id="addItem" data-type="addAttrButton">Add New Attribute</a> + </div> + <table class="custom-table"> + <tbody data-id="itemView"></tbody> + </table> + </div> + </div> + </div> +</div> \ No newline at end of file diff --git a/dashboardv3/public/js/templates/entity/EntityUserDefineItemView_tmpl.html b/dashboardv3/public/js/templates/entity/EntityUserDefineItemView_tmpl.html index 439943d..74dcd91 100644 --- a/dashboardv3/public/js/templates/entity/EntityUserDefineItemView_tmpl.html +++ b/dashboardv3/public/js/templates/entity/EntityUserDefineItemView_tmpl.html @@ -26,7 +26,7 @@ <textarea placeholder="value" data-type="value" data-index={{@index}} class="form-control" class="form-control">{{value}}</textarea> <p class="errorMsg"></p> </td> - <td class="custom-col-2"> + <td class="custom-col-2 btn-group"> <button class="btn btn-default btn-sm" title="" data-index={{@index}} data-id="deleteItem"> <i class="fa fa-minus"> </i> </button> diff --git a/dashboardv3/public/js/utils/UrlLinks.js b/dashboardv3/public/js/utils/UrlLinks.js index 6a09d31..2bbe679 100644 --- a/dashboardv3/public/js/utils/UrlLinks.js +++ b/dashboardv3/public/js/utils/UrlLinks.js @@ -99,6 +99,11 @@ define(['require', 'utils/Enums', 'utils/Utils', 'underscore'], function(require return this.baseUrlV2 + '/entity/bulk/classification'; } }, + entitiesNamespaceApiUrl: function(guid) { + if (guid) { + return this.baseUrlV2 + '/entity/guid/' + guid + '/namespaces?isOverwrite=true'; + } + }, entityCollectionaudit: function(guid) { return this.baseUrlV2 + '/entity/' + guid + '/audit'; }, diff --git a/dashboardv3/public/js/views/detail_page/DetailPageLayoutView.js b/dashboardv3/public/js/views/detail_page/DetailPageLayoutView.js index ccb2317..c2511b9 100644 --- a/dashboardv3/public/js/views/detail_page/DetailPageLayoutView.js +++ b/dashboardv3/public/js/views/detail_page/DetailPageLayoutView.js @@ -46,7 +46,8 @@ define(['require', RProfileLayoutView: "#r_profileLayoutView", RRelationshipLayoutView: "#r_relationshipLayoutView", REntityUserDefineView: "#r_entityUserDefineView", - REntityLabelDefineView: "#r_entityLabelDefineView" + REntityLabelDefineView: "#r_entityLabelDefineView", + REntityNameSpaceView: "#r_entityNameSpaceView" }, /** ui selector cache */ ui: { @@ -120,7 +121,7 @@ define(['require', * @constructs */ initialize: function(options) { - _.extend(this, _.pick(options, 'value', 'collection', 'id', 'entityDefCollection', 'typeHeaders', 'enumDefCollection', 'classificationDefCollection', 'glossaryCollection', 'searchVent')); + _.extend(this, _.pick(options, 'value', 'collection', 'id', 'entityDefCollection', 'typeHeaders', 'enumDefCollection', 'classificationDefCollection', 'glossaryCollection', 'nameSpaceCollection', 'searchVent')); $('body').addClass("detail-page"); }, bindEvents: function() { @@ -244,6 +245,7 @@ define(['require', enumDefCollection: this.enumDefCollection, classificationDefCollection: this.classificationDefCollection, glossaryCollection: this.glossaryCollection, + nameSpaceCollection: this.nameSpaceCollection, searchVent: this.searchVent, attributeDefs: (function() { return that.getEntityDef(collectionJSON); @@ -253,6 +255,7 @@ define(['require', this.renderEntityDetailTableLayoutView(obj); this.renderEntityUserDefineView(obj); this.renderEntityLabelDefineView(obj); + this.renderEntityNameSpaceView(obj); this.renderRelationshipLayoutView(obj); this.renderAuditTableLayoutView(obj); this.renderTagTableLayoutView(obj); @@ -512,6 +515,12 @@ define(['require', that.REntityLabelDefineView.show(new EntityLabelDefineView(obj)); }); }, + renderEntityNameSpaceView: function(obj) { + var that = this; + require(['views/entity/EntityNameSpaceView'], function(EntityNameSpaceView) { + that.REntityNameSpaceView.show(new EntityNameSpaceView(obj)); + }); + }, renderTagTableLayoutView: function(obj) { var that = this; require(['views/tag/TagDetailTableLayoutView'], function(TagDetailTableLayoutView) { diff --git a/dashboardv3/public/js/views/entity/EntityNameSpaceItemView.js b/dashboardv3/public/js/views/entity/EntityNameSpaceItemView.js new file mode 100644 index 0000000..9982e14 --- /dev/null +++ b/dashboardv3/public/js/views/entity/EntityNameSpaceItemView.js @@ -0,0 +1,297 @@ +/* + * 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/EntityNameSpaceItemView_tmpl', + 'moment', + 'daterangepicker' +], function(require, Backbone, EntityNameSpaceItemViewTmpl, moment) { + 'use strict'; + + return Backbone.Marionette.ItemView.extend({ + _viewName: 'EntityNameSpaceItemView', + + template: EntityNameSpaceItemViewTmpl, + + templateHelpers: function() { + return { + editMode: this.editMode, + entity: this.entity, + getValue: this.getValue.bind(this), + getNamespaceDroupdown: this.getNamespaceDroupdown.bind(this), + nameSpaceCollection: this.nameSpaceCollection.fullCollection.toJSON(), + model: this.model.toJSON() + } + }, + tagName: 'tr', + className: "custom-tr", + + /** Layout sub regions */ + regions: {}, + + /** ui selector cache */ + ui: { + keyEl: "[data-id='key']", + valueEl: "[data-type='value']", + addItem: "[data-id='addItem']", + deleteItem: "[data-id='deleteItem']", + editMode: "[data-id='editMode']" + }, + /** ui events hash */ + events: function() { + var events = {}; + events["click " + this.ui.deleteItem] = 'onDeleteItem'; + events["change " + this.ui.keyEl] = 'onAttrChange'; + return events; + }, + + /** + * intialize a new EntityNameSpaceItemView Layout + * @constructs + */ + initialize: function(options) { + _.extend(this, options); + }, + onRender: function() { + var that = this; + this.ui.keyEl.val(""); + this.ui.keyEl.select2({ placeholder: "Select Attribute" }); + + if (this.editMode && (!this.model.has("isNew"))) { + this.getEditNamespaceEl(); + } + this.initializeElement(); + this.bindEvent(); + }, + initializeElement: function() {}, + bindEvent: function() { + var that = this; + if (this.editMode) { + this.listenTo(this.model.collection, 'destroy unset:attr', function() { + if (this.model.has("isNew")) { + this.render(); + } + }); + this.listenTo(this.model.collection, 'selected:attr', function(value, model) { + if (model.cid !== this.model.cid && this.model.has("isNew")) { + var $select2el = that.$el.find('.custom-col-1:first-child>select[data-id="key"]'); + $select2el.find('option[value="' + value + '"]').remove(); + var select2val = $select2el.select2("val"); + $select2el.select2({ placeholder: "Select Attribute" }); + if (this.model.keys().length <= 2) { + $select2el.val("").trigger("change", true); + } + } + }); + this.$el.off("change", ".custom-col-1[data-id='value']>[data-key]").on("change", ".custom-col-1[data-id='value']>[data-key]", function(e) { + var key = $(this).data("key"), + namespace = $(this).data("namespace"), + typeName = $(this).data("typename"), + multi = $(this).data("multi"), + updateObj = that.model.toJSON(); + if (_.isUndefinedNull(updateObj[key])) { + updateObj[key] = { value: null, typeName: typeName }; + } + updateObj[key].value = multi ? $(this).select2("val") : e.currentTarget.value; + if (!that.model.has("__internal_UI_nameSpaceName")) { + updateObj["__internal_UI_nameSpaceName"] = namespace; + } + if (typeName === "date") { + updateObj[key].value = new Date(updateObj[key].value).getTime() + } + that.model.set(updateObj); + }); + this.$el.on('keypress', '.select2_only_number .select2-search__field', function() { + $(this).val($(this).val().replace(/[^\d].+/, "")); + if ((event.which < 48 || event.which > 57)) { + event.preventDefault(); + } + }); + } + }, + getAttrElement: function(options) { + var that = this, + returnEL = "N/A"; + if (options) { + var key = options.key, + typeName = options.val.typeName || "", + val = options.val.value, + isMultiValued = typeName && typeName.indexOf("array<") === 0, + namespace = options.namespace, + allowOnlyNum = false; + var elType = isMultiValued ? "select" : "input"; + if (!_.isEmpty(val)) { + val = _.escape(val); + } + if (typeName === "boolean") { + val = String(val); + } + if (typeName === "date" && _.isNumber(val)) { + val = moment(val).format("MM/DD/YYYY"); + } + if (typeName.indexOf("string") > -1) { + returnEL = '<' + elType + ' type="text" data-key="' + key + '" data-namespace="' + namespace + '" data-typename="' + typeName + '" data-multi="' + isMultiValued + '" multiple="' + isMultiValued + '" placeholder="Enter String" class="form-control" ' + (!_.isUndefinedNull(val) ? 'value="' + val + '"' : "") + '></' + elType + '>'; + } else if (typeName === "boolean") { + returnEL = '<select data-key="' + key + '" data-namespace="' + namespace + '" data-typename="' + typeName + '" class="form-control"><option value="">--Select Value--</option><option value="true" ' + (!_.isUndefinedNull(val) && val == "true" ? "selected" : "") + '>true</option><option value="false" ' + (!_.isUndefinedNull(val) && val == "false" ? "selected" : "") + '>false</option></select>'; + } else if (typeName === "date") { + returnEL = '<input type="text" data-key="' + key + '" data-namespace="' + namespace + '" data-typename="' + typeName + '" data-type="date" class="form-control" ' + (!_.isUndefinedNull(val) ? 'value="' + val + '"' : "") + '>' + setTimeout(function() { + var dateObj = { "singleDatePicker": true, "showDropdowns": true }; + that.$el.find('input[data-type="date"]').daterangepicker(dateObj); + }, 0); + } else if (typeName === "byte" || typeName === "short" || typeName.indexOf("int") > -1 || typeName.indexOf("float") > -1 || typeName === "double" || typeName === "long") { + allowOnlyNum = true; + returnEL = '<' + elType + ' data-key="' + key + '" data-namespace="' + namespace + '" data-typename="' + typeName + '" type="number" data-multi="' + isMultiValued + '" multiple="' + isMultiValued + '" placeholder="Enter Number" class="form-control" ' + (!_.isUndefinedNull(val) ? 'value="' + val + '"' : "") + '></' + elType + '>'; + } else if (typeName) { + var foundEnumType = this.enumDefCollection.fullCollection.find({ name: typeName }); + if (foundEnumType) { + var enumOptions = ""; + _.forEach(foundEnumType.get("elementDefs"), function(obj) { + enumOptions += '<option value="' + obj.value + '">' + obj.value + '</option>' + }); + returnEL = '<select data-key="' + key + '" data-namespace="' + namespace + '" data-typename="' + typeName + '">' + enumOptions + '</select>'; + } + setTimeout(function() { + var selectEl = that.$el.find('.custom-col-1[data-id="value"] select[data-key="' + key + '"]'); + selectEl.val((val || "")); + selectEl.select2(); + }, 0); + } + if (isMultiValued) { + setTimeout(function() { + var selectEl = that.$el.find('.custom-col-1[data-id="value"] select[data-key="' + key + '"][data-multi="true"]'); + var data = val && val.split(",") || []; + if (allowOnlyNum) { + selectEl.parent().addClass("select2_only_number"); + } + selectEl.select2({ tags: true, multiple: true, data: data }); + selectEl.val(data).trigger("change"); + }, 0); + } + } + return returnEL; + }, + onAttrChange: function(e, manual) { + var key = e.currentTarget.value.split(":"); + if (key.length && key.length === 3) { + var valEl = $(e.currentTarget).parent().siblings(".custom-col-1"), + hasModalData = this.model.get(key[1]); + if (!hasModalData) { + var tempObj = { + "__internal_UI_nameSpaceName": key[0] + }; + if (this.model.has("isNew")) { + tempObj["isNew"] = true; + } + tempObj[key[1]] = null; + this.model.clear({ silent: true }).set(tempObj) + } + valEl.html(this.getAttrElement({ namespace: key[0], key: key[1], val: hasModalData ? hasModalData : { typeName: key[2] } })); + if (manual === undefined) { + this.model.collection.trigger("selected:attr", e.currentTarget.value, this.model); + } + } + }, + getValue: function(value, key, namespaceName) { + var typeName = value.typeName, + value = value.value; + if (typeName === "date") { + return moment(value).format("MM/DD/YYYY"); + } else { + return value; + } + }, + getNamespaceDroupdown: function(nameSpaceCollection) { + var optgroup = ""; + var that = this; + var model = that.model.omit(["isNew", "__internal_UI_nameSpaceName"]), + keys = _.keys(model), + isSelected = false, + selectdVal = null; + if (keys.length === 1) { + isSelected = true; + } + _.each(nameSpaceCollection, function(obj) { + var options = ""; + if (obj.attributeDefs.length) { + _.each(obj.attributeDefs, function(attrObj) { + var entityNamespace = that.model.collection.filter({ __internal_UI_nameSpaceName: obj.name }), + hasAttr = false; + if (entityNamespace) { + var found = entityNamespace.find(function(obj) { + return obj.attributes.hasOwnProperty(attrObj.name); + }); + if (found) { + hasAttr = true; + } + } + if ((isSelected && keys[0] === attrObj.name) || !(hasAttr) && attrObj.options.applicableEntityTypes.indexOf('"' + that.entity.typeName + '"') > -1) { + var value = obj.name + ":" + attrObj.name + ":" + attrObj.typeName; + if (isSelected && keys[0] === attrObj.name) { selectdVal = value }; + options += '<option value="' + value + '">' + attrObj.name + ' (' + _.escape(attrObj.typeName) + ')</option>'; + } + }); + if (options.length) { + optgroup += '<optgroup label="' + obj.name + '">' + options + '</optgroup>'; + } + } + }); + + setTimeout(function() { + if (selectdVal) { + that.$el.find('.custom-col-1:first-child>select[data-id="key"]').val(selectdVal).trigger("change", true); + } else { + that.$el.find('.custom-col-1:first-child>select[data-id="key"]').val("").trigger("change", true); + } + }, 0); + return '<select data-id="key">' + optgroup + '</select>'; + }, + getEditNamespaceEl: function() { + var that = this, + trs = ""; + _.each(this.model.attributes, function(val, key) { + if (key !== "__internal_UI_nameSpaceName" && key !== "isNew") { + var td = '<td class="custom-col-1" data-key=' + key + '>' + key + '</td><td class="custom-col-0">:</td><td class="custom-col-1" data-id="value">' + that.getAttrElement({ namespace: that.model.get("__internal_UI_nameSpaceName"), key: key, val: val }) + '</td>'; + + td += '<td class="custom-col-2 btn-group">' + + '<button class="btn btn-default btn-sm" data-key="' + key + '" data-id="deleteItem">' + + '<i class="fa fa-times"> </i>' + + '</button></td>'; + trs += "<tr>" + td + "</tr>"; + } + }) + this.$("[data-id='namespaceTreeChild']").html("<table class='custom-table'>" + trs + "</table>"); + }, + onDeleteItem: function(e) { + var key = $(e.currentTarget).data("key"); + if (this.model.has(key)) { + if (this.model.keys().length === 2) { + this.model.destroy(); + } else { + this.model.unset(key); + if (!this.model.has("isNew")) { + this.$el.find("tr>td:first-child[data-key='" + key + "']").parent().remove() + } + this.model.collection.trigger("unset:attr"); + } + } else { + this.model.destroy(); + } + } + }); +}); \ No newline at end of file diff --git a/dashboardv3/public/js/views/entity/EntityNameSpaceView.js b/dashboardv3/public/js/views/entity/EntityNameSpaceView.js new file mode 100644 index 0000000..bbec0f7 --- /dev/null +++ b/dashboardv3/public/js/views/entity/EntityNameSpaceView.js @@ -0,0 +1,251 @@ +/** + * 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/EntityNameSpaceView_tmpl", + "views/entity/EntityNameSpaceItemView", + "models/VEntity", + "utils/Utils", + "utils/Enums", + "utils/Messages", + "utils/CommonViewFunction", + 'moment' +], function(require, Backbone, EntityNameSpaceViewTmpl, EntityNameSpaceItemView, VEntity, Utils, Enums, Messages, CommonViewFunction, moment) { + "use strict"; + + return Backbone.Marionette.CompositeView.extend({ + _viewName: "EntityNameSpaceView", + template: EntityNameSpaceViewTmpl, + childView: EntityNameSpaceItemView, + childViewContainer: "[data-id='itemView']", + childViewOptions: function() { + return { + editMode: this.editMode, + entity: this.entity, + nameSpaceCollection: this.nameSpaceCollection, + enumDefCollection: this.enumDefCollection + }; + }, + /** ui selector cache */ + ui: { + addItem: "[data-id='addItem']", + addNameSpace: "[data-id='addNameSpace']", + saveNameSpace: "[data-id='saveNameSpace']", + namespaceTree: "[data-id='namespaceTree']", + cancel: "[data-id='cancel']" + }, + events: function() { + var events = {}; + events["click " + this.ui.addItem] = 'createNameElement'; + events["click " + this.ui.addNameSpace] = "onAddNameSpace"; + events["click " + this.ui.saveNameSpace] = "onSaveNameSpace"; + events["click " + this.ui.cancel] = "onCancel"; + return events; + }, + initialize: function(options) { + var that = this; + _.extend(this, _.pick(options, "entity", "nameSpaceCollection", "enumDefCollection", "guid", "fetchCollection")); + this.editMode - false; + this.$("editBox").hide(); + var nameSpaceSet = {}; + this.treeData = []; + this.actualCollection = new Backbone.Collection( + _.map(this.entity.namespaceAttributes, function(val, key) { + var foundNameSpace = that.nameSpaceCollection.fullCollection.find({ name: key }); + var tempData = { + text: key, + name: key, + parent: "#", + icon: "fa fa-folder-o", + children: [] + } + if (foundNameSpace) { + var attributeDefs = foundNameSpace.get("attributeDefs"); + _.each(val, function(aVal, aKey) { + var foundAttr = _.find(attributeDefs, function(o) { + return o.name === aKey + }); + if (foundAttr) { + var treVal = aKey + " : " + (foundAttr.typeName === "date" ? moment(aVal).format("MM/DD/YYYY") : aVal); + tempData.children.push({ + text: treVal, + name: treVal, + icon: "fa fa-file-o", + children: [] + }) + val[aKey] = { value: aVal, typeName: foundAttr.typeName }; + } + }) + } + that.treeData.push(tempData); + return _.extend({}, val, { __internal_UI_nameSpaceName: key }); + })); + this.collection = new Backbone.Collection(); + this.entityModel = new VEntity(); + }, + updateToActualData: function(options) { + var silent = options && options.silent || false; + this.collection.reset(this.actualCollection.toJSON(), { silent: silent }); + }, + onAddNameSpace: function() { + this.ui.addNameSpace.hide(); + this.ui.saveNameSpace.show(); + this.ui.cancel.show(); + this.editMode = true; + this.ui.namespaceTree.hide(); + this.$(".editBox").show(); + this.updateToActualData({ silent: true }); + if (this.collection.length === 0) { + this.createNameElement(); + } else { + this.collection.trigger("reset"); + } + this.panelOpenClose(); + }, + onCancel: function() { + this.ui.cancel.hide(); + this.ui.saveNameSpace.hide(); + this.ui.addNameSpace.show(); + this.editMode = false; + this.ui.namespaceTree.show(); + this.$(".editBox").hide(); + this.updateToActualData(); + this.panelOpenClose(); + }, + panelOpenClose: function() { + var collection = this.editMode ? this.collection : this.actualCollection; + if (collection && collection.length === 0) { + this.$el.find(".panel-heading").addClass("collapsed"); + this.$el.find(".panel-collapse.collapse").removeClass("in"); + this.ui.addNameSpace.text("Add"); + this.ui.addNameSpace.attr("data-original-title", "Add"); + } else { + this.ui.addNameSpace.text("Edit"); + this.ui.addNameSpace.attr("data-original-title", "Edit All"); + this.$el.find(".panel-heading").removeClass("collapsed"); + this.$el.find(".panel-collapse.collapse").addClass("in"); + } + }, + validate: function() { + var validation = true; + this.$el.find('.custom-col-1[data-id="value"] [data-key]').each(function(el) { + var val = $(this).val(), + elIsSelect2 = $(this).hasClass("select2-hidden-accessible"); + if (_.isString(val)) { + val = val.trim(); + } + if (_.isEmpty(val)) { + if (validation) { + validation = false; + } + if (elIsSelect2) { + $(this).siblings(".select2").find(".select2-selection").attr("style", "border-color : red !important"); + } else { + $(this).css("borderColor", "red"); + } + } else { + if (elIsSelect2) { + $(this).siblings(".select2").find(".select2-selection").attr("style", ""); + } else { + $(this).css("borderColor", ""); + } + } + }); + return validation; + }, + onSaveNameSpace: function() { + var that = this; + if (!this.validate()) { + return; + } + this.entityModel.saveNamespaceEntity(this.guid, { + data: JSON.stringify(this.generateData()), + type: "POST", + success: function(data) { + Utils.notifySuccess({ + content: "One or more namespace attributes" + Messages.getAbbreviationMsg(false, 'editSuccessMessage') + }); + that.entity.namespaceAttributes = data; + this.editMode = false; + that.fetchCollection(); + that.onCancel(); + }, + complete: function(model, response) { + //that.hideLoader(); + } + }); + }, + generateData: function() { + var finalObj = {}; + this.collection.forEach(function(model) { + if (!model.has("addAttrButton")) { + var nameSpaceName = model.get("__internal_UI_nameSpaceName"), + modelObj = model.toJSON(); + _.each(modelObj, function(o, k) { + if (k === "isNew" && k === "__internal_UI_nameSpaceName") { + delete modelObj[k]; + return; + } + if (_.isObject(o) && o.value !== undefined) { + modelObj[k] = o.value; + } + }) + if (nameSpaceName !== undefined) { + if (finalObj[nameSpaceName]) { + finalObj[nameSpaceName] = _.extend(finalObj[nameSpaceName], modelObj); + } else { + finalObj[nameSpaceName] = modelObj; + } + } + } + }); + if (_.isEmpty(finalObj)) { + this.actualCollection.forEach(function(model) { + var nameSpaceName = model.get("__internal_UI_nameSpaceName"); + if (nameSpaceName) { + finalObj[nameSpaceName] = {}; + } + }) + } + return finalObj; + }, + createNameElement: function(options) { + var modelObj = { isNew: true }; + this.collection.unshift(modelObj); + }, + generateTree: function() { + this.ui.namespaceTree.jstree({ + plugins: ["core", "sort", "changed", "wholerow", "conditionalselect"], + conditionalselect: function(node) { + return false; + }, + state: { opened: true, selected: false }, + core: { + multiple: false, + data: this.treeData + } + }) + }, + onRender: function() { + this.panelOpenClose(); + this.generateTree(); + } + }); +}); \ No newline at end of file
