").dxButton( {
icon : $("link#PortalDocRootURL").attr( "href" ) + "/resources/stp-clean/graphics/ico-challenge.png",
onClick : function( e ) {
let okayToSubmitChallenge = true;
if( Fse.Portal.appConfiguration.STP.oneFSEnabled != "Y" ) {
// if the details tab need to be saved they user will have to do so
if( instance.isSaveNeeded() ) {
okayToSubmitChallenge = false;
alert( "Please save changes first" );
}
}
if( okayToSubmitChallenge ) {
let mcp = new MembershipChallengePopup();
let challenging = options.component.option( "formData")[dataFields[0]];
mcp.show( { distributorId : instance.data.distributorId, relationshipField : dataFields[0], challengeType : mg.challengeType, challenging : challenging })
}
}
}).appendTo( container );
}
}
)
} else {
fields.push( {
itemType : "empty"
})
}
})
items.push( {
itemType : "group",
colCount : 8,
alignItemLabels : false,
items : [
{
label : { text : mg.label },
template : function( options, container ) {
container.append( $("
" ) )
}
},
{
itemType : "group",
colSpan : 7,
colCount : 4,
alignItemLabels : false,
items : fields
}
]
});
})
return items;
}
DistributorDetails.prototype.addressAndPhoneFields = function() {
let instance = this;
let items = [];
// let countyDS = Fse.Data.newDataSource( { object : "UT.counties", paginate : false, keyField : "county" } );
let countyDS = Fse.Data.getLocalDataSource( "$.counties" );
countyDS.filter( [
[ "state", "=", '?' ],
"and",
[ "countryId", "=", 0 ]
] );
// let stateDS = Fse.Data.newDataSource( { object : "UT.states", paginate : false, keyField : "state" } );
let stateDS = Fse.Data.getLocalDataSource( "$.states" );
/* -- if we do this, the inbound will not pick the state in the drop down if we use $.states, if we use UT.states it does?
-- UT.states is a network hit every time
stateDS.filter( [
"countryId", "=", 0
])
*/
/* convert (555)555-5555 to 555-555-5555 */
let cleanPhone = instance.data.phone;
cleanPhone ??= '';
if ( cleanPhone.length > 0 ){
cleanPhone = cleanPhone.replace(/[{(}]/g, '');
instance.data.phone = cleanPhone.replace(/[{)}]/g, '-');
}
items.push(
{
dataField : "address", label : { text : "Street" }, editorOptions : { maxLength : 75, xwidth : 400, readOnly : instance.addressFieldsDisabled }
},
{
dataField : "address2", label : { text : "Street 2" }, editorOptions : { maxLength : 75, xwidth : 400, readOnly : instance.addressFieldsDisabled }
},
{ itemType : "empty", colSpan : 2 }
)
// only when the client has more than two countries
let countryIds = Fse.Portal.getConfiguration( "CRM.countryIdList" ).split(",");
let countryCount = 0;
let uniqueCountryIds = {}
countryIds.forEach( function( cid ) {
if( uniqueCountryIds[cid] ) return;
countryCount++;
uniqueCountryIds[cid] = cid;
})
if( countryCount > 1 ) {
items.push(
{ dataField : "countryId", label : { text : "Country" }, editorType : "dxSelectBox", editorOptions : {
// dataSource : Fse.Data.newDataSource( { object : "UT.countries", keyField : "countryId" } ),
dataSource : Fse.Data.getLocalDataSource( "$.countries" ),
displayExpr : "countryAbbrev",
valueExpr : "countryId",
readOnly : instance.addressFieldsDisabled,
width : 80,
},
}, { itemType : "empty", colSpan : 2 }
);
}
items.push(
{ dataField : "city", label : { text : "City" }, editorOptions : { maxLength : 75, readOnly : instance.addressFieldsDisabled} },
{ dataField : "state", label : { text : "State" },
editorType : "dxSelectBox", editorOptions : {
width : 150,
dataSource : stateDS,
placeholder : "state",
displayExpr : "state",
valueExpr : "state",
searchEnabled : true,
searchExpr : "state",
searchMethod : "startswith",
showClearButton : true,
readOnly : instance.addressFieldsDisabled
}},
{ dataField : "zipCode", label : { text : "Zip" },
editorOptions : { maxLength : 10, readOnly : instance.addressFieldsDisabled, width : 100 }
},
{ dataField : "phone",
label : { text : "Main Phone" },
editorOptions : {
maxLength : 30,
width : 150,
/*
mask: '000-000-0000',
maskChar : ' ',
maskInvalidMessage: 'The phone must have a correct phone format',
useMaskedValue : true,
*/
inputAttr: {
name: "cdr_dstPhone"
},
/*
onValueChanged : function( e ) {
if( e.value ) {
if( e.value.replace( /\s|-/g, '' ) == '' ) {
e.component.option( "value", null );
}
}
}
*/
onValueChanged : function( e ) {
if( e.component.option( "fseReformatted") ) {
e.component.option( "fseReformatted", false );
} else {
let formatted = Fse.UI.reformatPhone( e.value );
if( formatted != e.value ) {
e.component.option( {
"fseReformatted" : true,
value : formatted
} )
}
}
}
},
/*disabled : instance.addressFieldsDisabled,*/
validationRules : [
{
type: 'pattern',
pattern: /^\d{3}-\d{3}-\d{4}$/,
message: 'The phone must have a correct phone format',
ignoreEmptyValue : true,
}
]
},
{ itemType : "empty", colSpan : 2 }
);
return items;
}
DistributorDetails.prototype.internetFields = function() {
let instance = this;
let items = [];
let validURLPattern = Fse.CRM.socialMediaPlatforms.other.urlRegEx;
let internetFields = [
{
dataField : "url",
label : "URL", placeholder : "Company URL", maxLength : 150, width : 300,
type : "url"
},
{
dataField : "email",
label : "Email", placeholder : "email", maxLength : 75, width : 200,
validationRules : [ { type : "email", message : "Invalid Email" } ]
},
{
dataField : "facebookURL",
label : "Facebook", placeholder : "Facebook URL or ID", width : 300,
type : "url",
socialMediaPlatform : "facebook"
},
{
dataField : "instagramURL",
label : "Instagram", placeholder : "Instagram URL or ID", width : 300,
type : "url",
socialMediaPlatform : "instagram"
},
{
dataField : "twitterURL",
label : "\"X\"", placeholder : "\"X\" URL or ID (1-15 char)", width : 300,
type : "url",
socialMediaPlatform : "twitter"
},
{
dataField : "tiktokURL",
label : "TikTok",placeholder : "TikTok URL or ID", width : 300,
type : "url",
socialMediaPlatform : "tiktok",
},
{
dataField : "youtubeURL",
label : "YouTube", placeholder : "YouTube URL or ID (10 char)", width : 300,
type : "url",
socialMediaPlatform : "youtube"
},
{
dataField : "linkedInURL",
label : "LinkedIn", placeholder : "LinkedIn URL", maxLength : 150, width : 300,
type : "url",
socialMediaPlatform : "linkedin"
}
];
internetFields.forEach( function( il ) {
let item = {
dataField : il.dataField,
label : { text : il.label },
template : function( options, itemElement ) {
let customFields = options.component.option( "customFields" );
if( ! customFields ) {
customFields = {};
}
let textBoxConfig = {
maxLength : il.maxLength ? il.maxLength : null,
placeholder : il.placeholder ? il.placeholder : null,
width : il.width ? il.width : null,
onValueChanged : function( e ) {
options.component.updateData( options.dataField, e.value );
},
readOnly : instance.readOnly
};
if( il.type == "url" ) {
textBoxConfig.onOptionChanged = function( e ) {
if( e.name == "validationErrors" ) {
let validationErrors = e.value;
if( validationErrors ) {
let errorHandled = false;
validationErrors.forEach( function( ve ) {
if( errorHandled ) return;
if( ve.type == "pattern" ) {
if( ve.socialMediaPlatform ) {
let value = ve.value;
if( ! value.match( validURLPattern ) ) {
value = ve.urlTransform( value );
if( value.match( validURLPattern ) && value.match( ve.pattern ) ) {
errorHandled = true;
setTimeout( function() {
e.component.option( "value", value )
}, 10 );
}
}
} else {
let value = ve.value;
if( ! value.match( /^https+:\/\//i) ) {
value = value.replace( /^.*:/i, '' ).replace( /^\/\//, '' );
value = `https://${value}`
errorHandled = true;
setTimeout( function() {
e.component.option( "value", value )
}, 10 );
}
}
}
})
}
}
}
}
let textBox = $("
").dxTextBox( textBoxConfig ).dxTextBox( "instance" );
let validationRules = il.validationRules ? il.validationRules : null;
if( ! validationRules && il.type == "url" ) {
validationRules = [];
let message = "Invalid URL";
if( il.socialMediaPlatform && Fse.CRM.socialMediaPlatforms[il.socialMediaPlatform] ) {
let pattern = Fse.CRM.socialMediaPlatforms[il.socialMediaPlatform].urlRegEx;
let urlTransform = Fse.CRM.socialMediaPlatforms[il.socialMediaPlatform].urlTransform;
urlTransform ? urlTransform : function( v ) { return v };
message = `Invalid ${il.placeholder}`;
validationRules.push(
{ type : "pattern", socialMediaPlatform : il.socialMediaPlatform, message : message, pattern : pattern, urlTransform : urlTransform }
)
}
validationRules.push(
{ type : "pattern", message : message, pattern : validURLPattern }
);
}
textBox.element().dxValidator( {
validationGroup : options.component.option( "validationGroup" ),
validationRules : validationRules
})
let editorContainer = $("
");
editorContainer.append( textBox.element() );
itemElement.append( editorContainer );
customFields[il.dataField] = { editor : textBox };
options.component.option( "customFields", customFields );
}
}
items.push( item );
});
return items;
}
DistributorDetails.prototype.seasonalityFields = function() {
let items = [];
let instance = this;
let managedUnit = instance.data.isUnit;
let seasonalityReadOnly = managedUnit ? instance.data.seasonalityFollowsHQ == "Y" : false;
let configureSeasonalityDays = function( form, dataField ) {
let msb = form.option( `${dataField}_msb` );
let dsb = form.option( `${dataField}_dsb` );
let days = [];
let month = msb.option( "selectedItem" );
let daysDisabled = true;
if( month ) {
let dayValue = dsb.option( "value" );
daysDisabled = false;
days = [];
for( let d = 1; d <= month.days; d++ ) {
days.push( d );
}
if( dayValue && dayValue > days.length ) {
dayValue = days.length;
}
dsb.option( { dataSource : days, value : dayValue, disabled : false });
} else {
dsb.option( { dataSource : days, value : null, disabled : true });
}
}
// a two field template Month and Day to allow for selecting the season open/close date without the year
let seasonDateTemplate = function ( options, container ) {
let msb = null;
let dsb = null;
// the days in the month are based on 2000, a leap year
msb = $("
").dxSelectBox( {
showClearButton : true,
readOnly : seasonalityReadOnly,
dataSource : [
{ id : 0, month : "January", days : 31 },
{ id : 1, month : "Feburary", days : 29 },
{ id : 2, month : "March", days : 31 },
{ id : 3, month : "April", days : 30 },
{ id : 4, month : "May", days : 31 },
{ id : 5, month : "June", days : 30 },
{ id : 6, month : "July", days : 31 },
{ id : 7, month : "August", days : 31 },
{ id : 8, month : "September", days : 30 },
{ id : 9, month : "October", days : 31 },
{ id : 10, month : "November", days : 30 },
{ id : 11, month : "December", days : 31 },
],
displayExpr : "month",
valueExpr : "id",
placeholder : "month",
onValueChanged : function(e ) {
configureSeasonalityDays( options.component, options.dataField );
},
width : 120
}).css( "display", "inline-block" ).dxSelectBox( "instance" );
options.component.option( `${options.dataField}_msb`, msb );
dsb = $("
").dxSelectBox( {
dataSource : [],
readOnly : seasonalityReadOnly,
placeholder : "day",
validationGroup : options.component.option( "validationGroup" ),
width : 70,
disabled : true,
onValueChanged : function( e ) {
let dateValue = null;
if( e.value ) {
let d = e.value;
let m = msb.option( "value")
let date = null;
date = new Date();
date.setHours(0);
date.setMinutes(0);
date.setSeconds(0);
date.setMilliseconds(0);
date.setFullYear(2000);
date.setMonth( m );
date.setDate( d );
dateValue = DevExpress.localization.formatDate( date, "yyyy-MM-ddTHH:mm:ss" );
}
options.component.updateData( options.dataField, dateValue );
}
} ).css( { "display" : "inline-block", "margin-left" : "5px" }).dxSelectBox( "instance" );
options.component.option( `${options.dataField}_dsb`, dsb );
container.append( msb.element() ).append( dsb.element() )
let customFields = $.extend( true, {}, options.component.option( "customFields" ));
customFields[options.dataField] = {
onFieldDataChanged : function( e ) {
instance.reconfigureFields( e.component, e.dataField );
}
}
options.component.option( "customFields", customFields );
}
// seasonality
items.push ( {
itemType : "group",
colCount : 8,
alignItemLabels : false,
items : [
{
label : { text : "Seasonality Dates" },
template : function( options, container ) {
container.append( $("
" ));
}
},
{
colSpan : 7,
colCount : 8,
itemType : "group",
items : [
{ colSpan : 3,
dataField : "seasonOpenDate", dataType : "date", label : { text : "Opens On" },
template : function ( options, container ) {
seasonDateTemplate( options, container );
}
},
{ colSpan : 3,
dataField : "seasonCloseDate", dataType : "date", label : { text : "Closes On" },
template : function ( options, container ) {
seasonDateTemplate( options, container );
}
},
{ itemType : "empty", colSpan : 2 }
]
}
],
})
return items;
}
DistributorDetails.prototype.flexFieldItems = function( formData, fieldCategory ) {
let items = [];
let instance = this;
// basic a fields are inserted just before primary contact
instance.distributorProfileInstance.fieldCategoryStore.byKey( fieldCategory ).done( function( data ) {
if( data && data.items.length ) {
// instance.distributorProfileInstance.buildFlexFieldItems( data.items, items, 3 );
FlexFields.buildFormItems( data.items, items, 3 );
formData.flexCategories.push( fieldCategory );
if( instance.distributorProfileInstance.flexValues[0][fieldCategory] ) {
let values = {}; values[fieldCategory] = instance.distributorProfileInstance.flexValues[0][fieldCategory];
$.extend( true, formData, values );
}
}
})
return items;
}
DistributorDetails.prototype.updateSalesRepDisplay = function( form ) {
let instance = this;
let formData = form.option( "formData" );
let mfr_bsr_id = formData.mfr_bsr_id;
if ( mfr_bsr_id ) {
instance.salesRepDisplayElement.html( "loading..." );
let ds = form.getEditor( "mfr_bsr_id" ).getDataSource();
ds.store().byKey( mfr_bsr_id ).done( function( data ) {
let salesRep = Array.isArray( data ) & data.length ? data[0] : data;
let salesRepDisplay = $("
");
if( salesRep.title ) {
salesRepDisplay.append( $("
").text( salesRep.title ).css( { "padding-top" : "2px" } ) );
}
if( salesRep.phone ) {
let phoneElement = $("
").text( salesRep.phone ).css( { "padding-top" : "2px" });
if( salesRep.phoneExt ) {
phoneElement.append( `
Ext. ${salesRep.phoneExt}` );
}
salesRepDisplay.append( phoneElement );
}
salesRepDisplay.append( $("
").text( salesRep.email ).css( { "padding-top" : "2px" } ) )
instance.salesRepDisplayElement.empty().append( salesRepDisplay );
}).fail( function( e ) {
instance.salesRepDisplayElement.html( "" );
})
} else {
instance.salesRepDisplayElement.html( `
` );
}
}
DistributorDetails.prototype.updateProfileErrorDisplay = function( form ) {
let instance = this;
let formData = form.option( "formData" );
let profileErrors = formData.profileErrors;
if( profileErrors ) {
let priority = formData.priority;
if( priority == "*" ) {
priority = "A+";
}
let errorsElement = $("
")
.css( { "color" : "red", "border" : "1px solid red", "padding" : "5px", "maxWidth" : "400px" } )
errorsElement.append( $("
").text( "Profile Incomplete" ).css( "text-decoration", "underline" ) );
errorsElement.append( $("
").text( `The following elements of this profile are missing values which are required for "${priority}" distributors.` ) )
profileErrors.split( "," ).forEach( function( profileError ) {
errorsElement.append(
$("
")
.append( " · " )
.append( instance.resolveProfileError( profileError ) )
);
})
instance.profileErrorsElement.empty().append( errorsElement );
} else {
instance.profileErrorsElement.empty();
}
}
DistributorDetails.prototype.resolveProfileError = function( profileError ) {
let resolvedProfileError
switch ( profileError ) {
case "pc" : resolvedProfileError = "No Primary Contact"; break;
case "pc_phone" : resolvedProfileError = "Contact Phone"; break;
case "pc_email" : resolvedProfileError = "Contact Email"; break;
case "pc_title" : resolvedProfileError = "Contact Title"; break;
case "pc_last" : resolvedProfileError = "Contact Last Name"; break;
case "pc_first" : resolvedProfileError = "Contact First Name"; break;
case "pc_jobfunc" : resolvedProfileError = "Contact Job Func."; break;
case "addr" : resolvedProfileError = "Street"; break;
case "state" : resolvedProfileError = "State"; break;
//case "county" : resolvedProfileError = "County"; break;
case "city" : resolvedProfileError = "City"; break;
case "zip" : resolvedProfileError = "Zip/Postal Code"; break;
case "seg" : resolvedProfileError = "Primary Segment"; break;
case "cdr" : resolvedProfileError = "Primary Distributor"; break; // on units if HQ
case "dd" : resolvedProfileError = "Primary Distributor Details"; break;
case "cdr2" : resolvedProfileError = "Secondary Distributor"; break; // on units if HQ
case "dd2" : resolvedProfileError = "Seconary Distributor Details"; break;
case "cls" : resolvedProfileError = "Classification"; break;
case "phone" : resolvedProfileError = "Main Phone"; break;
case "fax" : resolvedProfileError = "Fax Number"; break;
case "cuisine" : resolvedProfileError = "Cuisine"; break;
case "pprof" : resolvedProfileError = "Incomplete Purchasing Profile"; break;
case "ansls" : resolvedProfileError = "Annual Sales (TY & LY)"; break;
case "cm" : resolvedProfileError = "Assigned Customer Manager"; break;
case "bp" : resolvedProfileError = "Business Plan (Opportunities)"; break;
}
if( ! resolvedProfileError ) {
if( profileError.match( /^f_\d+$/ ) != -1 ) {
// flex field id
let fieldId = profileError.split( "_" )[1];
instance.distributorProfileInstance.flexFields.forEach( function( field ) {
if( field.fieldId == fieldId ) {
resolvedProfileError = `${field.label} (${field.category})`;
}
})
}
if( ! resolvedProfileError ) {
resolvedProfileError = profileError;
}
}
return resolvedProfileError;
}