SalesCallSummaryWidget = function( widgetOptions ) { let configDefaults = this.getDefaultConfig(); this.widgetOptions = widgetOptions; this.widgetOptions.config = $.extend( true, {}, configDefaults, this.widgetOptions.config ); // keeps track of the active filterPreferences this.activeFilterPreferences = {}; this.readyPromise = $.Deferred(); this.rootElement = null; this.topLeftElement = null; this.topRightElement = null; this.otherMetricsElement = null; this.chartMetricsElement = null; this.otherMetricsDataGrid = null; this.init(); } SalesCallSummaryWidget.prototype.constructor = SalesCallSummaryWidget; SalesCallSummaryWidget.prototype.element = function() { return this.rootElement; } SalesCallSummaryWidget.prototype.ready = function() { return this.readyPromise; } SalesCallSummaryWidget.prototype.getDefaultSize = function() { return { width : 2, height : 1 } } SalesCallSummaryWidget.prototype.init = function() { let instance = this; this.createUI(); let objectParams = { groupType : "none" } let configForDataSource = $.extend( true, {}, instance.widgetOptions.config ); const globalFilters = { territoryPath : "territoryPath", salesRepId : "salesRepId", salesRepOwnerType : "staffType", mfr_id : "mfr_id", clientSegPath : "clientSegPath" }; for( let p in globalFilters ) { let gp = globalFilters[p]; if( instance.widgetOptions.filterPreferences.hasOwnProperty( gp ) && instance.widgetOptions.filterPreferences[gp] ) { if( configForDataSource[p] == null ) { configForDataSource[p] = instance.widgetOptions.filterPreferences[gp]; instance.activeFilterPreferences[p] = configForDataSource[p]; // indicate that this widget parameter has an active filter preference - used when displaying the edit page } } } let configFields = [ "territoryPath","salesRepId","interactionDateStart","interactionDateEnd","mfr_id","partnerType","salesRepOwnerType","excludeDeletedSalesReps","clientSegPath", "interactionMethod"]; configFields.forEach( function( dataField ) { if( configForDataSource[dataField] ) { objectParams[dataField] = configForDataSource[dataField]; } }) let ds = Fse.Data.newDataSource( { object : "CRM.interactionSummary", paginate : false, objectParams : objectParams } ); ds.load().done( function( data ) { instance.readyPromise.resolve(); function createText(x, y, fontSize, textAnchor, color, content) { const text = document.createElementNS('http://www.w3.org/2000/svg', 'text'); text.setAttribute('x', x); text.setAttribute('y', y); text.setAttribute('fill', color ); text.setAttribute('text-anchor', textAnchor); text.setAttribute('font-size', fontSize); text.setAttribute('font-weight', "bold" ); text.textContent = content; $( text ).on( "click", function() { instance.viewInteractions() } ); return text; } let totalCallsFormatted = DevExpress.localization.formatNumber( data[0].totalCalls, "fixedPoint" ); let totalPresentationsFormatted = DevExpress.localization.formatNumber( data[0].totalPresentations, "fixedPoint" ); instance.topLeftElement.text( `${totalCallsFormatted} Total Sales Calls` ).on( "click", function() { instance.viewInteractions(); }); instance.topRightElement.text( `${totalPresentationsFormatted} Item Presentations` ).on( "click", function() { instance.viewInteractions(); }); let otherMetrics = []; instance.metrics().forEach( function( metric ) { if( metric.visible == undefined ) { metric.visible = true; } if( ! metric.visible ) { return; } const value = data[0][metric.dataField]; if( metric.type == 'chart' ) { let dataSource = [ { argument : metric.dataField, val : value, tooltipValue : metric.tooltip ? data[0][metric.tooltip.dataField] : null }, { argument : `${metric.dataField}_gap`, val : 1.0 - value } ]; $("
").dxPieChart({ type: 'doughnut', palette: 'Soft Pastel', size : { width : 100, height : 120 }, startAngle : 270, dataSource : dataSource, title: { text : metric.title, verticalAlignment : "bottom", placeholderSize : 25, font : { size : "13px" } }, centerTemplate : function (pie, container) { const text = createText(20, 80, 12, 'start', metric.color, DevExpress.localization.formatNumber( value, "percent" )); container.appendChild(text); }, customizePoint : function( pointInfo ) { console.log( pointInfo ); if( pointInfo.argument.match( /_gap$/ )) { return { color : "#DADCE0" }; } }, onPointClick : function( e ) { instance.viewInteractions(); }, series: [{ color : metric.color, argumentField: 'argument', valueField : "val" }], tooltip : { enabled : metric.tooltip ? true : false, contentTemplate: function( pointInfo, container ) { if( ! pointInfo.argument.match( /_gap$/) ) { let value = pointInfo.point.data.tooltipValue; let formattedValue = DevExpress.localization.formatNumber( value, { type : "fixedPoint" } ) container.append( `${formattedValue} ${metric.tooltip.label}` ); } } } }).appendTo( instance.chartMetricsElement ); } else { otherMetrics.push( { text : metric.title, value : value }) } }) instance.otherMetricsDataGrid.option( "dataSource", otherMetrics ); }) } SalesCallSummaryWidget.prototype.createUI = function() { let instance = this; instance.rootElement = $("
").addClass( "SalesCallWidgetSummary" ).css( { "xmax-width" : "800px" } ); // width 800 let widgetTop = $("
").css( { "display" : "grid", "grid-template-columns" : "50% 50%" } ).appendTo( instance.rootElement ); instance.topLeftElement = $("
").css( { "text-align": "center", "font-size" : "18px", "color" : "white", "background-color" : "#9EA3A7", "padding-bottom" : "5px", "padding-top": "5px" }).appendTo( widgetTop ); instance.topRightElement = $("
").css( { "text-align": "center", "font-size" : "18px", "color" : "white", "background-color" : "#9EA3A7", "padding-bottom" : "5px", "padding-top": "5px" }).appendTo( widgetTop ); let widgetLayout = $("
").css( { "display" : "grid", "grid-template-columns" : "200px auto", "column-gap" : "15px" }).appendTo( instance.rootElement ); instance.otherMetricsElement = $("
").css( { "padding-top" : "10px" } ).appendTo( widgetLayout ); let gridCss = { "display" : "grid", "grid-template-columns" : "100px 100px 100px 100px 100px", "column-gap" : "15px", "padding" : "10px" }; let flexCss = { "display" : "flex", "flex-direction" : "row", "justify-content" : "space-evenly", "padding" : "10px" } instance.chartMetricsElement = $("
") .css( flexCss ).appendTo( widgetLayout ); instance.otherMetricsDataGrid = instance.otherMetricsElement.dxDataGrid( { showColumnHeaders : false, showBorders : false, showColumnLines : false, showRowLines : true, dataSource : [], onCellClick : function( e ) { instance.viewInteractions(); }, columns : [ { dataField : "text" }, { dataField : "value", dataType : "number", width : 50, cellTemplate : function( container, options ) { if( options.rowType != "data" ) return; container.css( { "font-weight" : "bold" } ).append( options.displayValue ) } } ] }).dxDataGrid( "instance" ); } SalesCallSummaryWidget.prototype.metrics = function() { let config = this.widgetOptions.config; let metrics = [ { dataField : "callsPerWeek", title : "Avg Calls/Week", visible : config.showCallsPerWeek }, { dataField : "callsPerWeekPerRep", title : "Avg Call/Rep/Week", visible : config.showCallsPerWeePerRep }, { dataField : "avgItemsPerCall", title : "Avg Items/Call", visible : config.showAvgItemsPerCall }, { dataField : "avgCallsToClose", title : "Avg Calls to Close", visible : config.showAvgCallsToClose }, // for testing { visible : false, dataField : "uniqueAccounts", title : "UA" }, { visible : false, dataField : "newAccounts", title : "NA" }, { dataField : "closeRate", color : "#5ad45a", title : "Close Rate", type : "chart", visible : config.showCloseRate, }, { dataField : "efficiencyRate", color : "#776bcd", title : "Efficiency", type : "chart", visible : config.showEfficiencyRate }, { dataField : "uniqueAccountsPct", color : "#f99600", title : "Uniq Accts", type : "chart", tooltip : { dataField : "uniqueAccounts", label : "Uniq Accts" }, visible : config.showUniqueAcctsPct}, { dataField : "newAccountsPct", color : "#27aeef", title : "New Accts", type : "chart", tooltip : { dataField : "newAccounts", label : "New Accts" }, visible : config.showNewAcctsPct }, { dataField : "objectiveCallsPct", color : "#db4425", title : "Obj Related", type : "chart", visible : config.showObjectiveCallsPct } ]; return metrics; } SalesCallSummaryWidget.prototype.getDefaultConfig = function() { let interactionDateRange = Fse.CRM.dateRanges.rangeByKey( "F,TY" ); if( Fse.Portal.appConfiguration.STP.ownerType === "BRO" ) { interactionDateRange = Fse.CRM.dateRanges.rangeByKey( "N,TY" ); } const defaults = { salesRepId : null, territoryPath : null, interactionDateStart : interactionDateRange.startDate, interactionDateEnd : interactionDateRange.endDate, interactionDateRange : interactionDateRange.rangeKey, partnerType : null, salesRepOwnerType : null, showCallsPerWeek : true, showCallsPerWeekPerRep : true, showAvgItemsPerCall : true, showAvgCallsToClose : true, showCloseRate : true, showEfficiencyRate : true, showUniqueQcctsPct : true, showObjectiveCallsPct : true, excludedDeletedSalesReps : false, clientSegPath : null, interactionMethod : null }; if( Fse.Portal.appConfiguration.STP.ownerType === "BRO" ) { defaults.mfr_id = null; } return defaults; } SalesCallSummaryWidget.prototype.viewInteractions = function() { let instance = this; let itemParams = instance.widgetOptions.config; let dashboard = instance.widgetOptions.dashboard; let analyzerParams = { disposition : 'X', interactionDateStart : itemParams.interactionDateStart, interactionDateEnd : itemParams.interactionDateEnd, interactionDateRange : itemParams.interactionDateRange, excludeDirectMarketing : true }; if( itemParams.territoryPath ) { analyzerParams.territoryPath = itemParams.territoryPath; } if( itemParams.salesRepId ) { analyzerParams.salesRepId = itemParams.salesRepId; } if( itemParams.partnerType ) { analyzerParams.partnerType = itemParams.partnerType; } if( itemParams.mfr_id ) { analyzerParams.mfr_id = itemParams.mfr_id; } if( itemParams.salesRepOwnerType ) { analyzerParams.salesRepOwnerType = itemParams.salesRepOwnerType } if( itemParams.excludeDeletedSalesReps ) { analyzerParams.excludeDeletedSalesReps = true } if( itemParams.clientSegPath ) { analyzerParams.clientSegPath = itemParams.clientSegPath } if( itemParams.interactionMethod ) { analyzerParams.interactionMethod = itemParams.interactionMethod } dashboard.tabSearch( "InteractionAnalyzer", analyzerParams ) } SalesCallSummaryWidget.prototype.edit = function( applyFn ) { let instance = this; if( ! instance.preferencesPopup ) { const items = []; items.push( { label : { location : "left", text : "Territory" }, dataField : "territoryPath", editorType : "dxDropDownBox", cssClass : instance.activeFilterPreferences.territoryPath ? 'fx-active-preference' : null, // scrum 46030 editorOptions : Fse.UI.multiSelectDropDownBoxEditorOptions( { dataSource : Fse.Data.newDataSource( { dataURL : instance.dataURL, object : "TER.salesTerritories", keyField : "territoryPath" } ), searchExpr : "territoryPath", searchMode : "contains", displayExpr : "territoryPath", multipleSelectedDisplay : "Multiple Territories Selected", keyExpr : "territoryPath", title : "Select Territories" }) }); items.push( { label : { text : "Sales Rep", location : "left" }, dataField : "salesRepId", editorType : "dxDropDownBox", cssClass : instance.activeFilterPreferences.salesRepId ? 'fx-active-preference' : null, // scrum 46030 editorOptions : Fse.UI.multiSelectDropDownBoxEditorOptions( { dataSource : Fse.Data.newDataSource( { dataURL : instance.dataURL, object : "CRM.salesRepList", keyField : "valueId", objectParams : { version : 2 } } ), searchExpr : "valueName", searchMode : "contains", displayExpr : "valueName", multipleSelectedDisplay : "Multiple Sales Reps Selected", keyExpr : "valueId", title : "Select Sales Reps" }) }); if( Fse.Portal.appConfiguration.STP.ownerType === "MFR" ) { let staffTypeList = [ { "text": "Manufacturer Staff Only" , "staffType": "MFR"}, { "text": "Broker Staff Only" ,"staffType": "BRO"} ]; items.push( { label : { location : "left", text : "Staff Type" }, dataField : "salesRepOwnerType", editorType : "dxSelectBox", cssClass : instance.activeFilterPreferences.salesRepOwnerType ? 'fx-active-preference' : null, editorOptions : { dataSource: { store : { type : "array", data : staffTypeList, key : "staffType" }}, displayExpr: "text", valueExpr : "staffType", placeholder : "Select Staff Type", showClearButton: true } }) } items.push( { dataField : "excludeDeletedSalesReps", label : { text : "Exclude Inactive & Deleted Sales Reps" }, editorType : "dxSwitch" }) items.push( { label : { location : "left", text : "Call Date" }, dataField : "interactionDateRange", editorType : "dxDropDownBox", editorOptions : Fse.UI.dateRangePickerOptions( { applyCustomDateRange : function( startDate, endDate ) { instance.preferencesForm.updateData( { interactionDateStart : startDate, interactionDateEnd : endDate } ) } } ) } ) if( Fse.Portal.appConfiguration.STP.ownerType === "BRO" ) { items.push( { label : { location : "left", text : "Manufacturer" }, dataField : "mfr_id", editorType : "dxDropDownBox", cssClass : instance.activeFilterPreferences.mfr_id ? 'fx-active-preference' : null, // scrum 46030 editorOptions : Fse.UI.multiSelectDropDownBoxEditorOptions( { dataSource : Fse.Data.newDataSource( { dataURL : instance.dataURL, object : "PRD.manufacturers", keyField : "mfr_id" } ), searchExpr : "mfr_name", searchMode : "contains", displayExpr : "mfr_name", multipleSelectedDisplay : "Multiple Manufacturers Selected", keyExpr : "mfr_id", title : "Select Manufacturer" }) }) } items.push( { label : { text : "Customer Type" }, dataField : "partnerType", editorType : "dxSelectBox", editorOptions : { showClearButton : true, dataSource : { store : { type : "array", data : [ { text : "Operators", partnerType : "OPR"}, { text : "Distributors", partnerType : "CDR" } ], key : "partnerType" } }, displayExpr : "text", valueExpr : "partnerType", placeholder : "Select Customer Type" } }) items.push( { dataField : "showObjectiveCallsPct", editorType : "dxSwitch", label : { text : "Show Objective Calls %" } }); items.push({ dataField : "clientSegPath", label : { location : "left", text : "Segment" }, editorType : "dxDropDownBox", cssClass : instance.activeFilterPreferences.clientSegPath ? 'fx-active-preference' : null, editorOptions : Fse.UI.multiSelectDropDownBoxEditorOptions( { dataSource : Fse.Data.newDataSource( { dataURL : instance.dataURL, object : "SPL.segments", keyField : "clientSegPath" } ), searchExpr : "clientSegPath", searchMode : "contains", displayExpr : "clientSegPath", multipleSelectedDisplay : "Multiple Segments Selected", keyExpr : "clientSegPath", title : "Select Segments" }) }); items.push( { label : { location : "left", text : "Call Method" }, dataField : "interactionMethod", editorType : "dxDropDownBox", editorOptions : Fse.UI.multiSelectDropDownBoxEditorOptions( { dataSource : Fse.Data.newDataSource( { object : "CRM.interactionMethods", key : "methodCode" } ), searchExpr : "methodName", displayExpr : "methodName", searchMode : "contains", multipleSelectedDisplay : "Multiple Methods Selected", keyExpr : "methodCode", title : "Select Method" }) }) if( Object.keys( instance.activeFilterPreferences ).length ) { items.push( { template : function() { let annotation = $("
").append( $("").addClass( "dx-field-item-label-text" ).append( "*filter preference active")).addClass( "fx-active-preference" ).css( { "cursor" : "pointer" } ); annotation.on( "click", function( e ) { // if the global filter preferences are applied, then close this popup let onSaveFilterPreferences = function() { instance.preferencesPopup.hide(); } instance.widgetOptions.dashboard.editFilterPreferences( onSaveFilterPreferences ); }) return annotation; } }) } let formData = { territoryPath : instance.widgetOptions.config.territoryPath, salesRepId : instance.widgetOptions.config.salesRepId, mfr_id : instance.widgetOptions.config.mfr_id, interactionDateStart : instance.widgetOptions.config.interactionDateStart, insteractionDateEnd : instance.widgetOptions.config.interactionDateEnd, interactionDateRange : instance.widgetOptions.config.interactionDateRange, partnerType : instance.widgetOptions.config.partnerType, salesRepOwnerType : instance.widgetOptions.config.salesRepOwnerType, excludeDeletedSalesReps : instance.widgetOptions.config.excludeDeletedSalesReps, clientSegPath : instance.widgetOptions.config.clientSegPath, showCallsPerWeek : instance.widgetOptions.config.showCallsPerWeek, showCallsPerWeekPerRep : instance.widgetOptions.config.showCallsPerWeekPerRep, showAvgItemsPerCall : instance.widgetOptions.config.showAvgItemsPerCall, showAvgCallsToClose : instance.widgetOptions.config.showAvgCallsToClose, showCloseRate : instance.widgetOptions.config.showCloseRate, showEfficiencyRate : instance.widgetOptions.config.showEfficiencyRate, showUniqueQcctsPct : instance.widgetOptions.config.showUniqueQcctsPct, showObjectiveCallsPct : instance.widgetOptions.config.showObjectiveCallsPct, interactionMethod : instance.widgetOptions.config.interactionMethod }; instance.preferencesPopup = $("
").dxPopup( { title : "Preferences", width : 450, height : "auto", position : { my : "right top", at : "right top", of : instance.widgetOptions.portlet }, hideOnOutsideClick : true, contentTemplate : function() { instance.preferencesForm = $("
").dxForm( { colCount : 1, items : items, } ).dxForm("instance"); instance.preferencesForm.updateData( formData ); return instance.preferencesForm.element(); }, toolbarItems : [ { // scrum 49810.14 toolbar : "bottom", location : "after", widget : "dxButton", options : { text : "Use Defaults", type : "normal", onClick : function() { instance.preferencesPopup.hide(); applyFn( instance.getDefaultConfig() ); } } }, { toolbar : "bottom", location : "after", widget : "dxButton", options : { text : "Apply", type : "default", onClick : function() { instance.preferencesPopup.hide(); applyFn( instance.preferencesForm.option( "formData" ) ); } } } ] } ).dxPopup( "instance" ); instance.preferencesPopup.element().appendTo( "body" ); } instance.preferencesPopup.show(); } Fse.Portal.addWidgetFactory( "SalesCallSummaryWidget", function( widgetDef ) { return new SalesCallSummaryWidget( widgetDef ); })