FlexFieldManager = function( options ) { let instance = this; instance.domain = options.domain; instance.category = options.category; instance.flexType = "standard"; if( options.flexType ) { instance.flexType = options.flexType; } instance.flexFieldEditor = null; instance.rootElement = null; instance.columnChooser = null; } FlexFieldManager.prototype.constructor = FlexFieldManager; FlexFieldManager.prototype.element = function() { let instance = this; if( instance.rootElement ) { return instance.rootElement; } let dataSource = Fse.Data.newDataSource( { object : "WRK.flexFields", keyField : "fieldId", objectParams : { domain : instance.domain }, paginate : false } ); dataSource.filter( [ "category", "=", instance.category ]) instance.dataGrid = $("
").dxDataGrid( { masterDetail : { enabled : false, template : function( container, options ) { instance.flexFieldEditor = new FlexFieldEditor( { data : options.data, onCancelled : function() { instance.dataGrid.collapseAll(-1); instance.flexFieldEditor = null; }, onSaved : function() { instance.dataGrid.collapseAll(-1); instance.flexFieldEditor = null; instance.dataGrid.refresh(); }, flexType : instance.flexType }); container.append( instance.flexFieldEditor.element() ); } }, remoteOperations : { filtering : true, }, columns : [ { dataField : "name", caption : "Code", width : 150, visible : false }, { dataField : "label", caption : "Field Label"}, { dataField : "type", caption : "Type", width : 100 }, { dataField : "sortRank", caption : "Order", dataType : "number", format : "fixedPoint", width : 50, alignment : "left" }, { dataField : "searchable", caption : "Searchable", width : 90 }, { dataField : "required", caption : "Required", width : 90 }, { dataField : "active", caption : "Active", width : 90 }, { type : "buttons", width : 60, showInColumnChooser : false, buttons : [ { icon : "edit", hint : "Edit field parameters", onClick : function( e ) { let createEditor = true; if( instance.flexFieldEditor ) { if( instance.flexFieldEditor.fieldId === e.row.data.fieldId ) { createEditor = false; } else if( instance.flexFieldEditor.isDirty() ) { alert( "Please save or cancel current field editing first." ); createEditor = false; } } if( createEditor ) { e.component.collapseAll(-1); e.component.expandRow( e.row.key ); } } }, { icon : "trash", hint : "Delete field", onClick : function( e ) { if( instance.flexFieldEditor ) { alert( "Please save or cancel current field editing first." ); return; } if( confirm( `Delete ${e.row.data.label } field?` ) ) { instance.deleteField( e.row.data ); } } } ] } ], dataSource : dataSource, showBorders : true, rowAlternationEnabled : true, height : 500, scrolling : { mode : "virtual" }, onRowClick : function( e ) { let createEditor = true; if( instance.flexFieldEditor ) { if( instance.flexFieldEditor.fieldId === e.data.fieldId ) { createEditor = false; } else if( instance.flexFieldEditor.isDirty() ) { alert( "Please save or cancel current field editing first." ); createEditor = false; } } if( createEditor ) { e.component.collapseAll(-1); e.component.expandRow( e.key ); } }, onToolbarPreparing : function( e ) { if( ! e.toolbarOptions.items ) { e.toolbarOptions.items = []; } e.toolbarOptions.items.push( { location : "after", widget : "dxButton", options : { icon : "plus", hint : "Add new field", onClick : function( ee ) { instance.addField(); } } }) e.toolbarOptions.items.push( { location : "after", widget : "dxButton", options : { hint : "Choose Columns", icon : "columnchooser", onClick : function( ) { if( ! instance.columnChooser ) { instance.columnChooser = new CustomDataGridColumnChooser( instance.dataGrid, function() { instance.dataGrid.refresh() } ); } instance.columnChooser.show(); } } }) } }).dxDataGrid( "instance" ); instance.rootElement = $("
").addClass( "FlexFieldManager" ) instance.rootElement.append( instance.dataGrid.element() ) return instance.rootElement; } FlexFieldManager.prototype.addField = function() { let instance = this; let form = null; let popup = $("
").dxPopup( { title : "Add Field", height : "auto", width : 500, contentTemplate : function( ) { let items = []; if ( instance.flexType == "form" ) { items.push( { dataField : "mappedField", label : { text : "CRM Mapping" }, editorType : "dxSelectBox", editorOptions : { showClearButton : true, disabled : true, placeholder : "select CRM field", displayExpr : "fieldLabel", valueExpr : "fieldName", // TODO: this search is not working, it may be something with the datasource searchEnabled : true, searchExpr : "fieldLabel", searchMode : "contains" } }) } items.push( { dataField : "label", label : { text : "Label" }, isRequired: true, editorOptions : { placeholder : "user facing label for this field" } }, { dataField : "type", label : { text : "Type" }, editorType : "dxSelectBox", editorOptions : { placeholder : "select field type", dataSource : { store : { type : "array", data : [ { type : "CHECKBOX" }, { type : "COMMENT" }, { type : "FILE" }, { type : "RADIO" }, { type : "SELECT" }, { type : "TEXT" }, { type : "TEXTAREA" } ], key : "type" } }, displayExpr : "type", valueExpr : "type" } }, { dataField : "defaultFieldOptions.format", label : { text : "Response Format" }, editorType : "dxSelectBox", editorOptions : { dataSource : { store : { type : "array", data : [ { text : "String", value : "string" }, { text : "Email", value : "email" }, { text : "Website", value : "url" }, { text : "Date", value : "date" }, { text : "Decimal", value : "decimal" }, { text : "Phone", value : "phone" } ], keyField : "value" } }, displayExpr : "text", valueExpr : "value" } } /*, { dataField : "name", label : { text : "Name" }, editorOptions : { maxLength : 50, placeholder : "AUTO or unique field name, letters and numbers only", onValueChanged : function( e ) { if( e.value && e.value != e.value.toUpperCase() ) { e.component.option( "value", e.value.toUpperCase() ) } } }, validationRules : [ { type : "required" }, { type : "pattern", pattern : "^[A-Z][A-Z0-9]*$", message : "Field name must start with a letter and contain only letters and numbers" }, { type : "async", validationCallback : function( params ) { let d = $.Deferred(); if( params.value == "AUTO" ) { d.resolve(); return d; } Fse.Ajax.performAction( { object : "WRK.validateFlexFieldName", data : { name : params.value, domain : instance.domain }, }).done( function( result ) { if( result.status ) { d.resolve(); } else { d.reject( result.message ); } }).fail( function() { d.reject( "Validation Error" ) }) return d; } } ] } */ ); items.push( { dataField : "required", label : { text : "Required" }, editorType : "dxSwitch" }) form = $("
").dxForm( { formData : { type : "TEXT", defaultFieldOptions : { format : "string" }, name : "AUTO" }, onFieldDataChanged : function( e ) { if( e.dataField == "mappedField" ) { let updateFormData = { type : "TEXT", defaultFieldOptions : { format : "string" } }; if( e.value ) { let mappedFieldEditor = e.component.getEditor( "mappedField" ); let selectedField = mappedFieldEditor.option( "selectedItem" ); updateFormData.type = selectedField.fieldType; if( selectedField.defaultFieldOptions ) { updateFormData.defaultFieldOptions = selectedField.defaultFieldOptions; } if( selectedField.formLabel ) { updateFormData.label = selectedField.formLabel; } } e.component.updateData( updateFormData ); } if( e.dataField == "type" ) { if( e.value == "TEXT" ) { e.component.getEditor( "defaultFieldOptions.format" ).option( { disabled : false } ); } else { e.component.getEditor( "defaultFieldOptions.format" ).option( { disabled : true } ); } } }, items : items }).dxForm("instance"); // this is being set here because when it was done with the initial field config the search didn't work correctly let contactMappingFieldsStore = Fse.Data.newDataSource( { object : "WRK.contactMappingFields", paginate : false, keyField : "fieldName" } ).store(); contactMappingFieldsStore.load().done( function( contactMappingFields ) { let editor = form.getEditor( "mappedField" ); editor.option( { dataSource : { store : { type : "array", data : contactMappingFields, key : "fieldName" } }, disabled : false }) }) return form.element(); }, hideOnOutsideClick : true, onHidden : function(e ) { e.component.element().remove(); e.component.dispose(); }, toolbarItems : [ { toolbar : "bottom", location : "after", widget : "dxButton", options : { text : "Add Field", type : "default", onClick : function( e ) { let vr = form.validate(); let okayToSave = $.Deferred(); if( vr.status === "pending" ) { vr.complete.then( function( res ) { if( res.isValid ) { okayToSave.resolve(); } else { console.log( res ); } }) } else if( vr.isValid ) { okayToSave.resolve(); }; okayToSave.done( function() { let saveData = form.option( "formData" ); saveData.fieldId = 0; saveData.domain = instance.domain; saveData.category = instance.category; console.log( "Saving" ); Fse.Ajax.performAction( { object : "WRK.saveFlexField", data : saveData, }).done( function( result ) { console.log( "Saved" ); popup.hide(); instance.dataGrid.refresh().done( function() { instance.dataGrid.collapseAll( -1 ); instance.dataGrid.expandRow( result.fieldId ); }); }) }) } } } ] }).dxPopup( "instance" ); popup.element().appendTo( $("body") ); popup.show(); } FlexFieldManager.prototype.deleteField = function( data) { let instance = this; Fse.Ajax.performAction( { object : "WRK.deleteFlexField", data : { fieldId : data.fieldId, name : data.name, domain : instance.domain }, }).done( function( result ) { console.log( "deleted" ); instance.dataGrid.refresh() }) }