Merge branch 'develop' into jnou/add-oem-test
This commit is contained in:
commit
557fe7cf9e
10 changed files with 433 additions and 143 deletions
|
|
@ -114,7 +114,7 @@ body {
|
||||||
debug: true,
|
debug: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Test',
|
name: 'SysTest',
|
||||||
hostName: 'www-test2.safelite.com',
|
hostName: 'www-test2.safelite.com',
|
||||||
apiHostname: 'digitalapi.test.safelite.io',
|
apiHostname: 'digitalapi.test.safelite.io',
|
||||||
debug: true,
|
debug: true,
|
||||||
|
|
@ -142,83 +142,178 @@ body {
|
||||||
return match;
|
return match;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getExperimentSettingsFromVuex(vuexData) {
|
||||||
|
const experiments = vuexData?.applicationUser?.experiments;
|
||||||
|
|
||||||
|
if(experiments) {
|
||||||
|
return experiments
|
||||||
|
.filter((e) => !!e.isActive)
|
||||||
|
.map((e) => e.settings)
|
||||||
|
.reduce((prev, next) => Object.assign(prev, next), {}) ?? {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
function extractDataFromVuex(existingVuexData) {
|
||||||
|
let bailoutInfo = [];
|
||||||
|
let headerInfo = [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
// =============== Collect diagnostic data:
|
||||||
|
bailoutInfo = [];
|
||||||
|
|
||||||
|
// App & Site Name
|
||||||
|
bailoutInfo.push({ name: 'App Name', value: 'FixMyGlass' });
|
||||||
|
bailoutInfo.push({ name: 'Site Name', value: 'SafeliteDotcom' });
|
||||||
|
|
||||||
|
// Not included (yet) from heritage:
|
||||||
|
// - Site ID
|
||||||
|
// - Code
|
||||||
|
// - Module
|
||||||
|
// - Page
|
||||||
|
|
||||||
|
bailoutInfo.push({ name: 'Timestamp', value: `${new Date()}` });
|
||||||
|
|
||||||
|
// - URL
|
||||||
|
// - Server
|
||||||
|
|
||||||
|
const sessionId = getCookieValueByName('sid');
|
||||||
|
bailoutInfo.push({ name: 'Session Id', value: sessionId });
|
||||||
|
|
||||||
|
const userAgent = window.navigator.userAgent;
|
||||||
|
bailoutInfo.push({ name: 'User Agent', value: userAgent });
|
||||||
|
|
||||||
|
// - IP
|
||||||
|
// - Session Log Sequence Number
|
||||||
|
|
||||||
|
bailoutInfo.push({ name: 'Referral Seq Number', value: existingVuexData.order?.referralSequenceNumber });
|
||||||
|
bailoutInfo.push({ name: 'Referral Number', value: existingVuexData.order?.referralNumber });
|
||||||
|
bailoutInfo.push({ name: 'Referral Date', value: existingVuexData.order?.referralDate });
|
||||||
|
bailoutInfo.push({ name: 'Referral Provider Number', value: existingVuexData.order?.serviceLocation?.provider?.providerNumber });
|
||||||
|
bailoutInfo.push({ name: 'Referral CTU', value: existingVuexData.order?.serviceLocation?.provider?.address?.zipCodeCtu });
|
||||||
|
|
||||||
|
// - Referral Insured Zip
|
||||||
|
// - Referral Insured Service Zip
|
||||||
|
|
||||||
|
bailoutInfo.push({ name: 'Referral CarID', value: existingVuexData.order?.vehicle?.carId });
|
||||||
|
|
||||||
|
const vehicle = existingVuexData.order?.vehicle;
|
||||||
|
const vehicleString = vehicle?.year
|
||||||
|
? `${vehicle.year} ${vehicle.make} ${vehicle.model} - ${vehicle.style}`
|
||||||
|
: null;
|
||||||
|
bailoutInfo.push({ name: 'Referral Vehicle', value: vehicleString });
|
||||||
|
bailoutInfo.push({ name: 'Work Order Number', value: existingVuexData.order?.workOrderNumber });
|
||||||
|
bailoutInfo.push({ name: 'Work Order ID', value: existingVuexData.order?.workOrderId });
|
||||||
|
bailoutInfo.push({ name: 'Parent Account Number', value: existingVuexData.order?.payment?.parentAccountNumber });
|
||||||
|
|
||||||
|
const environmentData = getCurrentEnvironmentData();
|
||||||
|
const userIdCookieName = `FunnelUserId-${environmentData?.name}`;
|
||||||
|
bailoutInfo.push({ name: 'User Id', value: getCookieValueByName(userIdCookieName) });
|
||||||
|
|
||||||
|
// - Parent Account Name
|
||||||
|
// - Client GUID
|
||||||
|
|
||||||
|
// =============== Collect header data:
|
||||||
|
headerInfo = [];
|
||||||
|
|
||||||
|
headerInfo.push({ name: 'X-Experiment-Data', value: JSON.stringify(getExperimentSettingsFromVuex(existingVuexData)) });
|
||||||
|
headerInfo.push({ name: 'X-Application-Name', value: 'FixMyGlass' });
|
||||||
|
|
||||||
|
const sessionKeyCookieName = `FunnelSessionKey-${environmentData?.name}`;
|
||||||
|
|
||||||
|
headerInfo.push({ name: 'X-Session-Sequence-Number', value: getCookieValueByName(sessionKeyCookieName) });
|
||||||
|
headerInfo.push({ name: 'X-Referral-Sequence-Number', value: existingVuexData?.order?.referralSequenceNumber });
|
||||||
|
headerInfo.push({ name: 'X-Enterprise-Order-Number', value: existingVuexData?.order?.eon });
|
||||||
|
headerInfo.push({ name: 'log-enabled', value: existingVuexData?.applicationUser?.loggingOption ?? false });
|
||||||
|
} finally {
|
||||||
|
return {
|
||||||
|
headerInfo: headerInfo,
|
||||||
|
bailoutInfo: bailoutInfo
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateRequestHeader(headerInfo) {
|
||||||
|
let headers = {};
|
||||||
|
|
||||||
|
headerInfo.forEach(
|
||||||
|
keyPair => {
|
||||||
|
headers[keyPair.name] = keyPair.value;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
headers['X-Transaction-Id'] = crypto.randomUUID();
|
||||||
|
|
||||||
|
headers['Content-Type'] = 'application/json';
|
||||||
|
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFieldWithName(info, name) {
|
||||||
|
const match = info?.find(
|
||||||
|
(row) => row.name === name
|
||||||
|
);
|
||||||
|
|
||||||
|
return match?.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearApplicationData() {
|
||||||
|
window.localStorage.removeItem('vuex');
|
||||||
|
|
||||||
|
const environmentData = getCurrentEnvironmentData();
|
||||||
|
const cookieNames = [
|
||||||
|
`FunnelUserId-${environmentData?.name}`,
|
||||||
|
`FunnelSessionKey-${environmentData?.name}`,
|
||||||
|
`FunnelSessionInfo-${environmentData?.name}`,
|
||||||
|
`sid`,
|
||||||
|
`dxdev`,
|
||||||
|
];
|
||||||
|
|
||||||
|
cookieNames.forEach(
|
||||||
|
(cookieName) => {
|
||||||
|
let cookieToAdd = `${cookieName}=undefined; path=/; domain=${environmentData.hostName}; max-age=0`;
|
||||||
|
document.cookie = cookieToAdd;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
window.onload = async () => {
|
window.onload = async () => {
|
||||||
// =============== Check for session info:
|
// =============== Check for session info:
|
||||||
const existingVuexDataJSON = window.localStorage.getItem('vuex');
|
const existingVuexDataJSON = window.localStorage.getItem('vuex');
|
||||||
const existingVuexData = existingVuexDataJSON ? JSON.parse(existingVuexDataJSON) : null;
|
const existingVuexData = existingVuexDataJSON ? JSON.parse(existingVuexDataJSON) : null;
|
||||||
const existingBailoutInfoJSON = window.sessionStorage.getItem('bailoutInfo');
|
const existingBailoutInfoJSON = window.sessionStorage.getItem('bailoutInfo');
|
||||||
const existingBailoutInfo = existingBailoutInfoJSON ? JSON.parse(existingBailoutInfoJSON) : null;
|
const existingBailoutInfo = existingBailoutInfoJSON ? JSON.parse(existingBailoutInfoJSON) : null;
|
||||||
|
const existingHeaderInfoJSON = window.sessionStorage.getItem('headerInfo');
|
||||||
|
const existingHeaderInfo = existingHeaderInfoJSON ? JSON.parse(existingHeaderInfoJSON) : null;
|
||||||
|
|
||||||
console.log(`================ VUEX DATA`);
|
console.log(`================ VUEX DATA`);
|
||||||
console.log(existingVuexData);
|
console.log(existingVuexData);
|
||||||
console.log(`================ PRIOR BAILOUT DATA`);
|
console.log(`================ PRIOR BAILOUT DATA`);
|
||||||
console.log(existingBailoutInfo);
|
console.log(existingBailoutInfo);
|
||||||
|
console.log(`================ PRIOR HEADER DATA`);
|
||||||
|
console.log(existingHeaderInfo);
|
||||||
|
|
||||||
let bailoutInfo = null;
|
let bailoutInfo = null;
|
||||||
|
let headerInfo = null;
|
||||||
|
|
||||||
if(existingVuexData) {
|
if(existingVuexData) {
|
||||||
try {
|
const results = extractDataFromVuex(existingVuexData);
|
||||||
// =============== Collect diagnostic data:
|
bailoutInfo = results.bailoutInfo;
|
||||||
bailoutInfo = [];
|
headerInfo = results.headerInfo;
|
||||||
|
|
||||||
// App & Site Name
|
clearApplicationData();
|
||||||
bailoutInfo.push({ name: 'App Name', value: 'FixMyGlass' });
|
|
||||||
bailoutInfo.push({ name: 'Site Name', value: 'SafeliteDotcom' });
|
|
||||||
|
|
||||||
// Not included (yet) from heritage:
|
if(bailoutInfo) {
|
||||||
// - Site ID
|
window.sessionStorage.setItem('bailoutInfo', JSON.stringify(bailoutInfo));
|
||||||
// - Code
|
|
||||||
// - Module
|
|
||||||
// - Page
|
|
||||||
|
|
||||||
bailoutInfo.push({ name: 'Timestamp', value: `${new Date()}` });
|
|
||||||
|
|
||||||
// - URL
|
|
||||||
// - Server
|
|
||||||
|
|
||||||
const sessionId = getCookieValueByName('sid');
|
|
||||||
bailoutInfo.push({ name: 'Session Id', value: sessionId });
|
|
||||||
|
|
||||||
const userAgent = window.navigator.userAgent;
|
|
||||||
bailoutInfo.push({ name: 'User Agent', value: userAgent });
|
|
||||||
|
|
||||||
// - IP
|
|
||||||
// - Session Log Sequence Number
|
|
||||||
|
|
||||||
bailoutInfo.push({ name: 'Referral Seq Number', value: existingVuexData.order?.referralSequenceNumber });
|
|
||||||
bailoutInfo.push({ name: 'Referral Number', value: existingVuexData.order?.referralNumber });
|
|
||||||
bailoutInfo.push({ name: 'Referral Date', value: existingVuexData.order?.referralDate });
|
|
||||||
bailoutInfo.push({ name: 'Referral Provider Number', value: existingVuexData.order?.serviceLocation?.provider?.providerNumber });
|
|
||||||
bailoutInfo.push({ name: 'Referral CTU', value: existingVuexData.order?.serviceLocation?.provider?.address?.zipCodeCtu });
|
|
||||||
|
|
||||||
// - Referral Insured Zip
|
|
||||||
// - Referral Insured Service Zip
|
|
||||||
|
|
||||||
bailoutInfo.push({ name: 'Referral CarID', value: existingVuexData.order?.vehicle?.carId });
|
|
||||||
|
|
||||||
const vehicle = existingVuexData.order?.vehicle;
|
|
||||||
const vehicleString = vehicle?.year
|
|
||||||
? `${vehicle.year} ${vehicle.make} ${vehicle.model} - ${vehicle.style}`
|
|
||||||
: null;
|
|
||||||
bailoutInfo.push({ name: 'Referral Vehicle', value: vehicleString });
|
|
||||||
bailoutInfo.push({ name: 'Work Order Number', value: existingVuexData.order?.workOrderNumber });
|
|
||||||
bailoutInfo.push({ name: 'Work Order ID', value: existingVuexData.order?.workOrderId });
|
|
||||||
bailoutInfo.push({ name: 'Parent Account Number', value: existingVuexData.order?.payment?.parentAccountNumber });
|
|
||||||
|
|
||||||
// - Parent Account Name
|
|
||||||
// - Client GUID
|
|
||||||
} finally {
|
|
||||||
// If any information gathered, write to session storage.
|
|
||||||
if(bailoutInfo) {
|
|
||||||
window.sessionStorage.setItem('bailoutInfo', JSON.stringify(bailoutInfo));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then *always* clear vuex data.
|
|
||||||
window.localStorage.removeItem('vuex');
|
|
||||||
}
|
}
|
||||||
} else if(existingBailoutInfo) {
|
|
||||||
// Proceed with prior data.
|
if(headerInfo) {
|
||||||
bailoutInfo = existingBailoutInfo;
|
window.sessionStorage.setItem('headerInfo', JSON.stringify(headerInfo));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bailoutInfo = existingBailoutInfo ?? [];
|
||||||
|
headerInfo = existingHeaderInfo ?? [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const environmentInfo = getCurrentEnvironmentData();
|
const environmentInfo = getCurrentEnvironmentData();
|
||||||
|
|
@ -275,11 +370,11 @@ body {
|
||||||
);
|
);
|
||||||
const entryString = `User encountered bailout page.\n${new Date()}\n${infoString ?? 'No information recoverable'}`;
|
const entryString = `User encountered bailout page.\n${new Date()}\n${infoString ?? 'No information recoverable'}`;
|
||||||
|
|
||||||
|
const headers = generateRequestHeader(headerInfo);
|
||||||
|
|
||||||
const request = new Request(endpointUrl, {
|
const request = new Request(endpointUrl, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: headers,
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
entry: entryString,
|
entry: entryString,
|
||||||
}),
|
}),
|
||||||
|
|
@ -292,6 +387,38 @@ body {
|
||||||
console.error(`=== ERROR SENDING LOGGING`);
|
console.error(`=== ERROR SENDING LOGGING`);
|
||||||
console.error(e);
|
console.error(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const endpointUrl = `https://${apiHostname}/analytics/api/v1/analytics/log-page-view`;
|
||||||
|
|
||||||
|
const headers = generateRequestHeader(headerInfo);
|
||||||
|
|
||||||
|
const payload = {
|
||||||
|
AppName: "FixMyGlass",
|
||||||
|
action: "",
|
||||||
|
applicationName: "FixMyGlassNextGen",
|
||||||
|
event: "ENTRY",
|
||||||
|
experimentsForUser: [],
|
||||||
|
pageName: "static-error",
|
||||||
|
parentAccountNumber: getFieldWithName(bailoutInfo, 'Parent Account Number') ?? 0,
|
||||||
|
referralSequenceNumber: getFieldWithName(bailoutInfo, 'Referral Seq Number'),
|
||||||
|
sessionId: getFieldWithName(bailoutInfo, 'Session Id'),
|
||||||
|
sessionKey: getFieldWithName(headerInfo, 'X-Session-Sequence-Number'),
|
||||||
|
shouldUseSessionId: false,
|
||||||
|
userId: getFieldWithName(bailoutInfo, 'User Id'),
|
||||||
|
};
|
||||||
|
|
||||||
|
const request = new Request(endpointUrl, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: headers,
|
||||||
|
body: JSON.stringify(payload),
|
||||||
|
});
|
||||||
|
|
||||||
|
const response = await fetch(request);
|
||||||
|
} catch(e) {
|
||||||
|
console.error(`=== ERROR SENDING PAGEVIEW`);
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -509,7 +509,7 @@
|
||||||
'AccountService': {
|
'AccountService': {
|
||||||
baseUrl: apiUrl + '/account/api/v1',
|
baseUrl: apiUrl + '/account/api/v1',
|
||||||
endpoints: [
|
endpoints: [
|
||||||
//{ path: '/healthcheck', method: 'GET' },
|
{ path: '/healthcheck', method: 'GET' },
|
||||||
{ path: '/account/insurance-companies', method: 'GET' },
|
{ path: '/account/insurance-companies', method: 'GET' },
|
||||||
{ path: '/account/account-details/373341', method: 'GET' },
|
{ path: '/account/account-details/373341', method: 'GET' },
|
||||||
{ path: '/account/bill-to-account-number?ParentAccountNumber=373341&ProviderNumber=01820&IsItac=false&TypeOfClaim=Personal&LineOfBusiness=Commercial', method: 'GET' }
|
{ path: '/account/bill-to-account-number?ParentAccountNumber=373341&ProviderNumber=01820&IsItac=false&TypeOfClaim=Personal&LineOfBusiness=Commercial', method: 'GET' }
|
||||||
|
|
@ -518,7 +518,7 @@
|
||||||
'LocationService': {
|
'LocationService': {
|
||||||
baseUrl: apiUrl + '/location/api/v1',
|
baseUrl: apiUrl + '/location/api/v1',
|
||||||
endpoints: [
|
endpoints: [
|
||||||
//{ path: '/healthcheck', method: 'GET' },
|
{ path: '/healthcheck', method: 'GET' },
|
||||||
{ path: '/location/zip/43015/Replace', method: 'GET' },
|
{ path: '/location/zip/43015/Replace', method: 'GET' },
|
||||||
{ path: '/location/alert-reasons/03357', method: 'GET' },
|
{ path: '/location/alert-reasons/03357', method: 'GET' },
|
||||||
{ path: '/location/providers/43015/Replace/100/167132/true/CR00046193/false', method: 'GET' },
|
{ path: '/location/providers/43015/Replace/100/167132/true/CR00046193/false', method: 'GET' },
|
||||||
|
|
@ -528,7 +528,7 @@
|
||||||
'VehicleService': {
|
'VehicleService': {
|
||||||
baseUrl: apiUrl + '/vehicle/api/v1',
|
baseUrl: apiUrl + '/vehicle/api/v1',
|
||||||
endpoints: [
|
endpoints: [
|
||||||
//{ path: '/healthcheck', method: 'GET' },
|
{ path: '/healthcheck', method: 'GET' },
|
||||||
{ path: '/vehicle/years', method: 'GET' },
|
{ path: '/vehicle/years', method: 'GET' },
|
||||||
{ path: '/vehicle/makes/1997', method: 'GET' },
|
{ path: '/vehicle/makes/1997', method: 'GET' },
|
||||||
{ path: '/vehicle/Models/1997/Toyota', method: 'GET' },
|
{ path: '/vehicle/Models/1997/Toyota', method: 'GET' },
|
||||||
|
|
@ -539,7 +539,7 @@
|
||||||
'PartService': {
|
'PartService': {
|
||||||
baseUrl: apiUrl + '/parts/api/v1',
|
baseUrl: apiUrl + '/parts/api/v1',
|
||||||
endpoints: [
|
endpoints: [
|
||||||
//{ path: '/healthcheck', method: 'GET' },
|
{ path: '/healthcheck', method: 'GET' },
|
||||||
{ path: '/parts/damage-options/CR00046193', method: 'GET' },
|
{ path: '/parts/damage-options/CR00046193', method: 'GET' },
|
||||||
{ path: '/parts/rain-repel', method: 'GET' },
|
{ path: '/parts/rain-repel', method: 'GET' },
|
||||||
{ path: '/parts/parts-or-questions', method: 'POST', body: {"carId":"CR00046193","glassPieces":[{"location":"Windshield","name":"Single"}],"zip":"43015","vin":null,"serviceType":null,"referralSeqNumber":environmentContext.referralNumber,"AppName":"FixMyGlass"} },
|
{ path: '/parts/parts-or-questions', method: 'POST', body: {"carId":"CR00046193","glassPieces":[{"location":"Windshield","name":"Single"}],"zip":"43015","vin":null,"serviceType":null,"referralSeqNumber":environmentContext.referralNumber,"AppName":"FixMyGlass"} },
|
||||||
|
|
@ -553,7 +553,7 @@
|
||||||
'PriceService': {
|
'PriceService': {
|
||||||
baseUrl: apiUrl + '/price/api/v1',
|
baseUrl: apiUrl + '/price/api/v1',
|
||||||
endpoints: [
|
endpoints: [
|
||||||
//{ path: '/healthcheck', method: 'GET' },
|
{ path: '/healthcheck', method: 'GET' },
|
||||||
{ path: '/price/taxed-order-items?ParentAccountNumber=167132&BillToAccountNumber=87291&ProviderNumber=003357&AppointmentType=Mobile&ServiceLocation.City=asdf&ServiceLocation.State=OH&ServiceLocation.ZipCode=43015&lineItems[0].partNumber=FW02018GBNN&lineItems[0].laborAmount=60&lineItems[0].kitPrice=0&lineItems[0].sellingPrice=297.33&lineItems[1].partNumber=GGG%202018&lineItems[1].laborAmount=0&lineItems[1].kitPrice=0&lineItems[1].sellingPrice=13.4&lineItems[2].partNumber=RECYCLE%20FEE&lineItems[2].laborAmount=39.99&lineItems[2].kitPrice=0&lineItems[2].sellingPrice=0&ServerData=' + encodeURIComponent(environmentContext.serverData), method: 'GET' },
|
{ path: '/price/taxed-order-items?ParentAccountNumber=167132&BillToAccountNumber=87291&ProviderNumber=003357&AppointmentType=Mobile&ServiceLocation.City=asdf&ServiceLocation.State=OH&ServiceLocation.ZipCode=43015&lineItems[0].partNumber=FW02018GBNN&lineItems[0].laborAmount=60&lineItems[0].kitPrice=0&lineItems[0].sellingPrice=297.33&lineItems[1].partNumber=GGG%202018&lineItems[1].laborAmount=0&lineItems[1].kitPrice=0&lineItems[1].sellingPrice=13.4&lineItems[2].partNumber=RECYCLE%20FEE&lineItems[2].laborAmount=39.99&lineItems[2].kitPrice=0&lineItems[2].sellingPrice=0&ServerData=' + encodeURIComponent(environmentContext.serverData), method: 'GET' },
|
||||||
{ path: '/price/order-items', method: 'POST', body: { "parentAccountNumber": 167132, "billToAccountNumber": "87291", "providerNumber": "03357", "carId": "CR00046193", "year": 1997, "make": "Toyota", "model": "Camry", "eon": environmentContext.eon, "state": "OH", "referralNumber": environmentContext.referralNumber, "referralSequenceNumber": environmentContext.referralNumber, "zipCode": "43015", "serviceZipCode": "43015", "referralDate": "2025-10-29T10:55:27.18", "isReplacement": true, "deductible": 0, "coverageStatus": null, "lineItems": [ { "partNumber": "RECYCLE FEE" }, { "partNumber": "FW02018GBNN" }, { "partNumber": "GGG 2018" }, { "partNumber": "SBB19" }, { "partNumber": "SBB21" } ], "serverData": "", "AppName": "FixMyGlass" } },
|
{ path: '/price/order-items', method: 'POST', body: { "parentAccountNumber": 167132, "billToAccountNumber": "87291", "providerNumber": "03357", "carId": "CR00046193", "year": 1997, "make": "Toyota", "model": "Camry", "eon": environmentContext.eon, "state": "OH", "referralNumber": environmentContext.referralNumber, "referralSequenceNumber": environmentContext.referralNumber, "zipCode": "43015", "serviceZipCode": "43015", "referralDate": "2025-10-29T10:55:27.18", "isReplacement": true, "deductible": 0, "coverageStatus": null, "lineItems": [ { "partNumber": "RECYCLE FEE" }, { "partNumber": "FW02018GBNN" }, { "partNumber": "GGG 2018" }, { "partNumber": "SBB19" }, { "partNumber": "SBB21" } ], "serverData": "", "AppName": "FixMyGlass" } },
|
||||||
{ path: '/price/order-promo', method: 'POST', body: {"promoCode":"WEBSL45","addableVaps":[{"partNumber":"RAIN REPEL","description":null,"partType":"RAIN REPEL","isInsurable":null,"id":"ccb0865d-1c0a-4be7-8605-bc7b9cb3d221","isChildPart":false,"laborAmount":0,"sellingPrice":44.99,"kitPrice":0,"salesTax":null},{"partNumber":"SBB19","description":"SAFELITE BEAM BLADE 19","partType":"FRONT WIPER","isInsurable":null,"id":"4db19a8b-739f-4f7b-983e-f6308c5f5901","isChildPart":false,"laborAmount":0,"sellingPrice":34.99,"kitPrice":0,"salesTax":null},{"partNumber":"SBB21","description":"SAFELITE BEAM BLADE 21","partType":"FRONT WIPER","isInsurable":null,"id":"e27ae20a-3a60-4fdf-a738-4b56d2e761cb","isChildPart":false,"laborAmount":0,"sellingPrice":34.99,"kitPrice":0,"salesTax":null}],"order":{"appointmentType":"Mobile","carId":"CR00046193","correlationId":"b0261e48-1111-44fc-abc9-34b41380687c","eon":environmentContext.eon,"isInsurance":false,"isRepair":false,"glassToReplace":[{"location":"Windshield","name":"Single"}],"lineItemsOnOrder":[{"partNumber":"FW02018GBNN","basePartNumber":"FW02018","description":"solar","color":"Green Tint, Blue Shade","partType":"WINDSHIELD","canSafeliteRecalibrate":false,"requiresRecalibration":false,"requiresCapabilityQuestions":false,"recalibrationType":null,"childParts":[{"partNumber":"GGG 2018","safelitePartNumber":"GGG 2018","id":"329cf0ec-8106-4e62-ab66-de0da8f18a1d","isChildPart":true,"laborAmount":0,"sellingPrice":13.4,"kitPrice":0,"salesTax":0.94}],"kitPrice":0,"sellingPrice":368.94,"laborAmount":60,"id":"4326cee6-b39e-41ff-bb29-df9220e32298","isChildPart":false,"salesTax":30.02},{"partNumber":"GGG 2018","safelitePartNumber":"GGG 2018","id":"329cf0ec-8106-4e62-ab66-de0da8f18a1d","isChildPart":true,"laborAmount":0,"sellingPrice":13.4,"kitPrice":0,"salesTax":0.94},{"partNumber":"RECYCLE FEE","description":null,"partType":"REPLACE FEE","isInsurable":null,"isChildPart":false,"laborAmount":39.99,"sellingPrice":0,"kitPrice":0,"salesTax":2.8,"id":"3771aed5-0de1-4f0e-b1ad-394f73e08f38","cartItemType":"REPLACE FEE"},{"partNumber":"MOBILE FEE","description":null,"partType":"MOBILE FEE","isInsurable":true,"isChildPart":false,"laborAmount":0,"sellingPrice":0,"kitPrice":0,"salesTax":0,"id":"2bc936ec-ef5e-4fe2-95fb-e946792b1ae5"},{"partNumber":"SBB19","description":"SAFELITE BEAM BLADE 19","partType":"FRONT WIPER","isInsurable":null,"id":"4db19a8b-739f-4f7b-983e-f6308c5f5901","isChildPart":false,"laborAmount":0,"sellingPrice":34.99,"kitPrice":0,"salesTax":2.45,"cartItemType":"FRONT WIPERS"},{"partNumber":"SBB21","description":"SAFELITE BEAM BLADE 21","partType":"FRONT WIPER","isInsurable":null,"id":"e27ae20a-3a60-4fdf-a738-4b56d2e761cb","isChildPart":false,"laborAmount":0,"sellingPrice":34.99,"kitPrice":0,"salesTax":2.45,"cartItemType":"FRONT WIPERS"},{"partNumber":"RAIN REPEL","description":null,"partType":"RAIN REPEL","isInsurable":null,"id":"ccb0865d-1c0a-4be7-8605-bc7b9cb3d221","isChildPart":false,"laborAmount":0,"sellingPrice":44.99,"kitPrice":0,"salesTax":3.15,"cartItemType":"RAIN REPEL"}],"parentAccountNumber":"167132","referralSequenceNumber":environmentContext.referralNumber,"serviceState":"OH","serverData":environmentContext.serverData,"vehicleYear":"1997","zipCodeOrProviderCtu":"03357"},"AppName":"FixMyGlass"} },
|
{ path: '/price/order-promo', method: 'POST', body: {"promoCode":"WEBSL45","addableVaps":[{"partNumber":"RAIN REPEL","description":null,"partType":"RAIN REPEL","isInsurable":null,"id":"ccb0865d-1c0a-4be7-8605-bc7b9cb3d221","isChildPart":false,"laborAmount":0,"sellingPrice":44.99,"kitPrice":0,"salesTax":null},{"partNumber":"SBB19","description":"SAFELITE BEAM BLADE 19","partType":"FRONT WIPER","isInsurable":null,"id":"4db19a8b-739f-4f7b-983e-f6308c5f5901","isChildPart":false,"laborAmount":0,"sellingPrice":34.99,"kitPrice":0,"salesTax":null},{"partNumber":"SBB21","description":"SAFELITE BEAM BLADE 21","partType":"FRONT WIPER","isInsurable":null,"id":"e27ae20a-3a60-4fdf-a738-4b56d2e761cb","isChildPart":false,"laborAmount":0,"sellingPrice":34.99,"kitPrice":0,"salesTax":null}],"order":{"appointmentType":"Mobile","carId":"CR00046193","correlationId":"b0261e48-1111-44fc-abc9-34b41380687c","eon":environmentContext.eon,"isInsurance":false,"isRepair":false,"glassToReplace":[{"location":"Windshield","name":"Single"}],"lineItemsOnOrder":[{"partNumber":"FW02018GBNN","basePartNumber":"FW02018","description":"solar","color":"Green Tint, Blue Shade","partType":"WINDSHIELD","canSafeliteRecalibrate":false,"requiresRecalibration":false,"requiresCapabilityQuestions":false,"recalibrationType":null,"childParts":[{"partNumber":"GGG 2018","safelitePartNumber":"GGG 2018","id":"329cf0ec-8106-4e62-ab66-de0da8f18a1d","isChildPart":true,"laborAmount":0,"sellingPrice":13.4,"kitPrice":0,"salesTax":0.94}],"kitPrice":0,"sellingPrice":368.94,"laborAmount":60,"id":"4326cee6-b39e-41ff-bb29-df9220e32298","isChildPart":false,"salesTax":30.02},{"partNumber":"GGG 2018","safelitePartNumber":"GGG 2018","id":"329cf0ec-8106-4e62-ab66-de0da8f18a1d","isChildPart":true,"laborAmount":0,"sellingPrice":13.4,"kitPrice":0,"salesTax":0.94},{"partNumber":"RECYCLE FEE","description":null,"partType":"REPLACE FEE","isInsurable":null,"isChildPart":false,"laborAmount":39.99,"sellingPrice":0,"kitPrice":0,"salesTax":2.8,"id":"3771aed5-0de1-4f0e-b1ad-394f73e08f38","cartItemType":"REPLACE FEE"},{"partNumber":"MOBILE FEE","description":null,"partType":"MOBILE FEE","isInsurable":true,"isChildPart":false,"laborAmount":0,"sellingPrice":0,"kitPrice":0,"salesTax":0,"id":"2bc936ec-ef5e-4fe2-95fb-e946792b1ae5"},{"partNumber":"SBB19","description":"SAFELITE BEAM BLADE 19","partType":"FRONT WIPER","isInsurable":null,"id":"4db19a8b-739f-4f7b-983e-f6308c5f5901","isChildPart":false,"laborAmount":0,"sellingPrice":34.99,"kitPrice":0,"salesTax":2.45,"cartItemType":"FRONT WIPERS"},{"partNumber":"SBB21","description":"SAFELITE BEAM BLADE 21","partType":"FRONT WIPER","isInsurable":null,"id":"e27ae20a-3a60-4fdf-a738-4b56d2e761cb","isChildPart":false,"laborAmount":0,"sellingPrice":34.99,"kitPrice":0,"salesTax":2.45,"cartItemType":"FRONT WIPERS"},{"partNumber":"RAIN REPEL","description":null,"partType":"RAIN REPEL","isInsurable":null,"id":"ccb0865d-1c0a-4be7-8605-bc7b9cb3d221","isChildPart":false,"laborAmount":0,"sellingPrice":44.99,"kitPrice":0,"salesTax":3.15,"cartItemType":"RAIN REPEL"}],"parentAccountNumber":"167132","referralSequenceNumber":environmentContext.referralNumber,"serviceState":"OH","serverData":environmentContext.serverData,"vehicleYear":"1997","zipCodeOrProviderCtu":"03357"},"AppName":"FixMyGlass"} },
|
||||||
|
|
@ -563,7 +563,7 @@
|
||||||
'ScheduleService': {
|
'ScheduleService': {
|
||||||
baseUrl: apiUrl + '/schedule/api/v1',
|
baseUrl: apiUrl + '/schedule/api/v1',
|
||||||
endpoints: [
|
endpoints: [
|
||||||
//{ path: '/healthcheck', method: 'GET' },
|
{ path: '/healthcheck', method: 'GET' },
|
||||||
{ path: '/schedule/mobile-time-slots', method: 'POST', body: {"startDate":startDate,"endDate":endDate,"applicationName":"FixMyGlass","parentAccountNumber":"167132","carId":"CR00046193","lineItems":[{"partNumber":"RECYCLE FEE","partType":"REPLACE FEE","isGlassPart":false},{"partNumber":"SBB19","partType":"FRONT WIPER","isGlassPart":false},{"partNumber":"SBB21","partType":"FRONT WIPER","isGlassPart":false},{"partNumber":"RAIN REPEL","partType":"RAIN REPEL","isGlassPart":false},{"partNumber":"FW02018GBNN","partType":"WINDSHIELD","isGlassPart":true},{"partNumber":"GGG 2018","isGlassPart":false}],"glassPieces":[{"location":"Windshield","name":"Single"}],"eon":environmentContext.eon,"billToAccountNumber":"87291","coverage":{"status":null,"deductible":0,"additionalAuthFlag":null},"partSelection":{"hasAnsweredPartQuestions":false,"hasAnsweredMoldingQuestions":false,"hasAnsweredCapabilityQuestions":false,"hasManuallySelectedParts":false},"vehicle":{"year":1997,"make":"Toyota","model":"Camry","style":"4 door sedan","vin":""},"zipCode":"43015","AppName":"FixMyGlass"} },
|
{ path: '/schedule/mobile-time-slots', method: 'POST', body: {"startDate":startDate,"endDate":endDate,"applicationName":"FixMyGlass","parentAccountNumber":"167132","carId":"CR00046193","lineItems":[{"partNumber":"RECYCLE FEE","partType":"REPLACE FEE","isGlassPart":false},{"partNumber":"SBB19","partType":"FRONT WIPER","isGlassPart":false},{"partNumber":"SBB21","partType":"FRONT WIPER","isGlassPart":false},{"partNumber":"RAIN REPEL","partType":"RAIN REPEL","isGlassPart":false},{"partNumber":"FW02018GBNN","partType":"WINDSHIELD","isGlassPart":true},{"partNumber":"GGG 2018","isGlassPart":false}],"glassPieces":[{"location":"Windshield","name":"Single"}],"eon":environmentContext.eon,"billToAccountNumber":"87291","coverage":{"status":null,"deductible":0,"additionalAuthFlag":null},"partSelection":{"hasAnsweredPartQuestions":false,"hasAnsweredMoldingQuestions":false,"hasAnsweredCapabilityQuestions":false,"hasManuallySelectedParts":false},"vehicle":{"year":1997,"make":"Toyota","model":"Camry","style":"4 door sedan","vin":""},"zipCode":"43015","AppName":"FixMyGlass"} },
|
||||||
{ path: '/schedule/shop-time-slots', method: 'POST', body: {"providerNumber":"003417","startDate":startDate,"endDate":endDate,"shopAppointmentType":"InshopOrDropoff","applicationName":"FixMyGlass","parentAccountNumber":"167132","carId":"CR00046193","lineItems":[{"partNumber":"RECYCLE FEE","partType":"REPLACE FEE","isGlassPart":false},{"partNumber":"FW02018GBNN","partType":"WINDSHIELD","isGlassPart":true},{"partNumber":"GGG 2018","isGlassPart":false}],"glassPieces":[{"location":"Windshield","name":"Single"}],"eon":environmentContext.eon,"billToAccountNumber":"87291","coverage":{"status":null,"deductible":0,"additionalAuthFlag":null},"partSelection":{"hasAnsweredPartQuestions":false,"hasAnsweredMoldingQuestions":false,"hasAnsweredCapabilityQuestions":false,"hasManuallySelectedParts":false},"vehicle":{"year":1997,"make":"Toyota","model":"Camry","style":"4 door sedan","vin":""},"AppName":"FixMyGlass"} }
|
{ path: '/schedule/shop-time-slots', method: 'POST', body: {"providerNumber":"003417","startDate":startDate,"endDate":endDate,"shopAppointmentType":"InshopOrDropoff","applicationName":"FixMyGlass","parentAccountNumber":"167132","carId":"CR00046193","lineItems":[{"partNumber":"RECYCLE FEE","partType":"REPLACE FEE","isGlassPart":false},{"partNumber":"FW02018GBNN","partType":"WINDSHIELD","isGlassPart":true},{"partNumber":"GGG 2018","isGlassPart":false}],"glassPieces":[{"location":"Windshield","name":"Single"}],"eon":environmentContext.eon,"billToAccountNumber":"87291","coverage":{"status":null,"deductible":0,"additionalAuthFlag":null},"partSelection":{"hasAnsweredPartQuestions":false,"hasAnsweredMoldingQuestions":false,"hasAnsweredCapabilityQuestions":false,"hasManuallySelectedParts":false},"vehicle":{"year":1997,"make":"Toyota","model":"Camry","style":"4 door sedan","vin":""},"AppName":"FixMyGlass"} }
|
||||||
]
|
]
|
||||||
|
|
@ -571,25 +571,25 @@
|
||||||
'AnalyticsService': {
|
'AnalyticsService': {
|
||||||
baseUrl: apiUrl + '/analytics/api/v1',
|
baseUrl: apiUrl + '/analytics/api/v1',
|
||||||
endpoints: [
|
endpoints: [
|
||||||
//{ path: '/healthcheck', method: 'GET' },
|
{ path: '/healthcheck', method: 'GET' },
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
'ExperimentService': {
|
'ExperimentService': {
|
||||||
baseUrl: apiUrl + '/experiments/api/v1',
|
baseUrl: apiUrl + '/experiments/api/v1',
|
||||||
endpoints: [
|
endpoints: [
|
||||||
//{ path: '/healthcheck', method: 'GET' },
|
{ path: '/healthcheck', method: 'GET' },
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
'OrderService': {
|
'OrderService': {
|
||||||
baseUrl: apiUrl + '/order/api/v1',
|
baseUrl: apiUrl + '/order/api/v1',
|
||||||
endpoints: [
|
endpoints: [
|
||||||
//{ path: '/healthcheck', method: 'GET' },
|
{ path: '/healthcheck', method: 'GET' },
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
'ContentService': {
|
'ContentService': {
|
||||||
baseUrl: apiUrl + '/content/api/v1',
|
baseUrl: apiUrl + '/content/api/v1',
|
||||||
endpoints: [
|
endpoints: [
|
||||||
//{ path: '/healthcheck', method: 'GET' },
|
{ path: '/healthcheck', method: 'GET' },
|
||||||
{ path: '/content/fmg/vehicle', method: 'GET' },
|
{ path: '/content/fmg/vehicle', method: 'GET' },
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -674,7 +674,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateAtAGlanceText() {
|
function updateAtAGlanceText() {
|
||||||
const healthyCount = Object.values(healthData).filter(service => service.status === 'healthy').length;
|
const healthyCount = Object.values(healthData).filter(service => (service.status === 'healthy' || service.status === 'degraded')).length;
|
||||||
const totalCount = Object.values(healthData).length;
|
const totalCount = Object.values(healthData).length;
|
||||||
let text = 'Healthy';
|
let text = 'Healthy';
|
||||||
let className = 'stat-pill stat-pill-healthy';
|
let className = 'stat-pill stat-pill-healthy';
|
||||||
|
|
@ -763,14 +763,15 @@
|
||||||
// Determine overall service status
|
// Determine overall service status
|
||||||
const endpoints = healthData[serviceName].endpoints;
|
const endpoints = healthData[serviceName].endpoints;
|
||||||
const healthyCount = endpoints.filter(ep => !isDegraded(ep) && !isUnhealthy(ep)).length;
|
const healthyCount = endpoints.filter(ep => !isDegraded(ep) && !isUnhealthy(ep)).length;
|
||||||
|
const unhealthyCount = endpoints.filter(ep => isUnhealthy(ep)).length;
|
||||||
const totalCount = endpoints.length;
|
const totalCount = endpoints.length;
|
||||||
|
|
||||||
if (healthyCount === totalCount) {
|
if (healthyCount === totalCount) {
|
||||||
healthData[serviceName].status = 'healthy';
|
healthData[serviceName].status = 'healthy';
|
||||||
} else if (healthyCount > 0) {
|
} else if (unhealthyCount > 0) {
|
||||||
healthData[serviceName].status = 'degraded';
|
|
||||||
} else {
|
|
||||||
healthData[serviceName].status = 'unhealthy';
|
healthData[serviceName].status = 'unhealthy';
|
||||||
|
} else {
|
||||||
|
healthData[serviceName].status = 'degraded';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<slot name="modal-footer-slot"></slot>
|
<slot name="modal-footer-slot"></slot>
|
||||||
<modalButtonMain
|
<modalButtonMain
|
||||||
|
v-if="!isFooterButtonSuppressed"
|
||||||
:isPrimary="isFooterButtonPrimary"
|
:isPrimary="isFooterButtonPrimary"
|
||||||
class="w-100 modal-footer-button"
|
class="w-100 modal-footer-button"
|
||||||
:id="modalId + '-modalbtn'"
|
:id="modalId + '-modalbtn'"
|
||||||
|
|
@ -67,6 +68,7 @@ export default {
|
||||||
staticBackdrop: Boolean,
|
staticBackdrop: Boolean,
|
||||||
footerButtonDisabled: Boolean,
|
footerButtonDisabled: Boolean,
|
||||||
isFooterButtonPrimary: Boolean,
|
isFooterButtonPrimary: Boolean,
|
||||||
|
isFooterButtonSuppressed: Boolean,
|
||||||
onModalOpenedCallback: {
|
onModalOpenedCallback: {
|
||||||
type: Function,
|
type: Function,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div id="afterpay-banner" role="alert">
|
<div class="afterpay-modal-banner" role="alert">
|
||||||
<component
|
|
||||||
:is="'script'"
|
|
||||||
src="https://js.squarecdn.com/square-marketplace.js"
|
|
||||||
async></component>
|
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<span
|
<span
|
||||||
class="callout"
|
class="callout"
|
||||||
|
|
@ -22,22 +17,52 @@
|
||||||
<span v-else v-html="token"></span>
|
<span v-else v-html="token"></span>
|
||||||
<span class="nbsp"> </span>
|
<span class="nbsp"> </span>
|
||||||
</span>
|
</span>
|
||||||
<a
|
<textLink
|
||||||
id="afterpay-learnmore"
|
linkType="text"
|
||||||
href="#"
|
:text="bannerCta"
|
||||||
data-afterpay-modal="en_US"
|
href="javascript:void(0)"
|
||||||
data-bind="click:afterpayLearnMore">
|
@click-event="openModal" />
|
||||||
{{ modalCopy }}
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<modal
|
||||||
|
:ref="modalName"
|
||||||
|
class="afterpay-modal"
|
||||||
|
:headerText="modalHeaderText"
|
||||||
|
:isFooterButtonSuppressed="true">
|
||||||
|
<template v-slot:modal-header-slot>
|
||||||
|
<span>{{ modalSubHeaderText }}</span>
|
||||||
|
</template>
|
||||||
|
<template v-slot>
|
||||||
|
<span class="afterpay-sections">
|
||||||
|
<span
|
||||||
|
v-for="section in afterpaySectionsContent"
|
||||||
|
:key="section"
|
||||||
|
class="afterpay-section">
|
||||||
|
<span class="section-header">
|
||||||
|
<img v-if="section.AnswerImageUrl" :src="section.AnswerImageUrl" />
|
||||||
|
<h3 v-if="section.Text" v-html="section.Text"></h3>
|
||||||
|
</span>
|
||||||
|
<span v-html="section.content"></span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<template v-slot:modal-footer-slot>
|
||||||
|
<p class="modal-disclaimer" v-html="modalDisclaimerText"></p>
|
||||||
|
</template>
|
||||||
|
</modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { splitCopyOnCMSPlaceHolder, splitCMSCopyOnBR } from "@/helpers/cms-content-helper";
|
import {
|
||||||
import baseMixin from "@/mixins/base-mixin.js";
|
doesCopyContainTextLink,
|
||||||
import { getPromosThatMatchLineItemsOnOrder } from "@/helpers/promotions-helper";
|
splitCopyOnCMSPlaceHolder,
|
||||||
import { getArrayOfAllLineItemsAndChildParts } from "@/store";
|
getRouterLinkRouteFromCopy,
|
||||||
|
getRouterLinkDisplayTextFromCopy,
|
||||||
|
getExternalLink,
|
||||||
|
splitCMSCopyOnBR,
|
||||||
|
} from "@/helpers/cms-content-helper";
|
||||||
|
import textLink from "@/ux-components/text-link/text-link";
|
||||||
|
import modal from "@/digital-components/modal/modal";
|
||||||
|
|
||||||
const INLINE_IMAGE_TOKEN = "custom:inlineImage";
|
const INLINE_IMAGE_TOKEN = "custom:inlineImage";
|
||||||
const AFTERPAY_PRICE_TOKEN = "custom:afterpayPrice";
|
const AFTERPAY_PRICE_TOKEN = "custom:afterpayPrice";
|
||||||
|
|
@ -48,20 +73,16 @@ function isInlineImageToken(token) {
|
||||||
function isAfterpayPriceToken(token) {
|
function isAfterpayPriceToken(token) {
|
||||||
return token.includes(AFTERPAY_PRICE_TOKEN);
|
return token.includes(AFTERPAY_PRICE_TOKEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getInlineAltText(token) {
|
function getInlineAltText(token) {
|
||||||
const innerTokens = token.split(",");
|
const innerTokens = token.split(",");
|
||||||
|
|
||||||
return innerTokens[1] ?? "";
|
return innerTokens[1] ?? "";
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "afterpay-modal-banner",
|
name: "afterpay-banner",
|
||||||
props: {
|
props: {
|
||||||
cmsWidgetName: String,
|
cmsWidgetName: String,
|
||||||
lineItems: Array,
|
modalWidgetName: String,
|
||||||
isInsuranceSelected: Boolean,
|
|
||||||
afterpayExtendedPayOptionThreshold: Number,
|
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {};
|
return {};
|
||||||
|
|
@ -70,6 +91,17 @@ export default {
|
||||||
isInlineImageToken,
|
isInlineImageToken,
|
||||||
isAfterpayPriceToken,
|
isAfterpayPriceToken,
|
||||||
getInlineAltText,
|
getInlineAltText,
|
||||||
|
doesCopyContainTextLink,
|
||||||
|
splitCopyOnCMSPlaceHolder,
|
||||||
|
getRouterLinkRouteFromCopy,
|
||||||
|
getRouterLinkDisplayTextFromCopy,
|
||||||
|
getExternalLink,
|
||||||
|
openModal() {
|
||||||
|
this.modal?.openModal();
|
||||||
|
},
|
||||||
|
closeModal() {
|
||||||
|
this.modal?.closeModal();
|
||||||
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
imageUrl() {
|
imageUrl() {
|
||||||
|
|
@ -84,39 +116,40 @@ export default {
|
||||||
afterpayCopyTokens() {
|
afterpayCopyTokens() {
|
||||||
return splitCopyOnCMSPlaceHolder(this.getCmsContent(this.cmsWidgetName, "BodyText"));
|
return splitCopyOnCMSPlaceHolder(this.getCmsContent(this.cmsWidgetName, "BodyText"));
|
||||||
},
|
},
|
||||||
modalCopy() {
|
bannerCta() {
|
||||||
return this.getCmsContent(this.cmsWidgetName, "FooterText");
|
return this.getCmsContent(this.cmsWidgetName, "FooterText");
|
||||||
},
|
},
|
||||||
getTierOnePackagePrice() {
|
modalHeaderText() {
|
||||||
let allLineItems = getArrayOfAllLineItemsAndChildParts(this.lineItems);
|
return this.getCmsContent(this.modalWidgetName, "HeaderText");
|
||||||
if (this.lineItems.promos) {
|
},
|
||||||
allLineItems = allLineItems?.filter((item) => item.partType !== "PROMO_DISCOUNT");
|
modalDisclaimerText() {
|
||||||
}
|
return this.getCmsContent(this.modalWidgetName, "BodyText2");
|
||||||
|
},
|
||||||
let price = baseMixin.methods.getTierOnePackagePrice(
|
modalName() {
|
||||||
baseMixin.methods.filterOutFees(allLineItems)
|
return this.modalWidgetName;
|
||||||
);
|
},
|
||||||
|
modal() {
|
||||||
if (this.lineItems.promos) {
|
return this.$refs[this.modalName];
|
||||||
let allLineItems = getArrayOfAllLineItemsAndChildParts(this.lineItems);
|
},
|
||||||
const promos = getPromosThatMatchLineItemsOnOrder(
|
afterpaySectionsContent() {
|
||||||
this.lineItems.promos,
|
const sections = this.getCmsContent("AfterpayModalSectionsWidget", "Answers");
|
||||||
allLineItems
|
if (!sections) return [];
|
||||||
);
|
sections.forEach((section) => {
|
||||||
promos.forEach((promo) => {
|
const sectionContent = this.getCmsContent(section.Name, "BodyText");
|
||||||
price += baseMixin.methods.getTotalLineItemPrice(promo);
|
section["content"] = sectionContent;
|
||||||
});
|
});
|
||||||
}
|
return sections;
|
||||||
|
|
||||||
return price;
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
components: {},
|
components: {
|
||||||
|
textLink,
|
||||||
|
modal,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
#afterpay-banner {
|
.afterpay-modal-banner {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
background-color: $blue-150;
|
background-color: $blue-150;
|
||||||
|
|
@ -130,6 +163,7 @@ export default {
|
||||||
|
|
||||||
.callout {
|
.callout {
|
||||||
font-family: UrbanistSemibold, Arial, Helvetica, sans-serif;
|
font-family: UrbanistSemibold, Arial, Helvetica, sans-serif;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
> div:first-of-type {
|
> div:first-of-type {
|
||||||
|
|
@ -163,15 +197,119 @@ export default {
|
||||||
}
|
}
|
||||||
@include media-breakpoint-up(md) {
|
@include media-breakpoint-up(md) {
|
||||||
padding-left: 1rem;
|
padding-left: 1rem;
|
||||||
max-width: 32rem;
|
max-width: 36rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
& .alert-heading {
|
}
|
||||||
font-size: 0.875rem;
|
|
||||||
|
.afterpay-modal {
|
||||||
|
&.modal.modal-component {
|
||||||
|
:deep(.modal-dialog) {
|
||||||
|
margin: 0 1rem;
|
||||||
|
top: 0;
|
||||||
|
|
||||||
|
@include media-breakpoint-up(md) {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 840px;
|
||||||
|
top: 3.4rem;
|
||||||
|
right: 0;
|
||||||
|
left: 0;
|
||||||
|
transform: none;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 0 1rem;
|
||||||
|
}
|
||||||
|
.modal-content {
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
margin: 1.5rem 0;
|
||||||
|
|
||||||
|
.modal-header.mt-6 {
|
||||||
|
background-color: $blue-100;
|
||||||
|
margin-top: 0;
|
||||||
|
padding: 1.5rem 2rem 1.5rem 1.5rem;
|
||||||
|
font-family: UrbanistSemibold;
|
||||||
|
|
||||||
|
.modal-title.justify-content-center {
|
||||||
|
// OVERRIDE
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
.btn-close {
|
||||||
|
top: 1.25rem;
|
||||||
|
right: 1.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.afterpay-section {
|
||||||
|
display: block;
|
||||||
|
border: 1px solid $gray-200;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
padding: 1rem;
|
||||||
|
|
||||||
|
.section-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
h3 {
|
||||||
|
font-family: UrbanistSemibold;
|
||||||
|
font-size: 1rem;
|
||||||
|
margin: 0 0 0 0.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
}
|
||||||
|
strong {
|
||||||
|
font-family: UrbanistSemibold;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-body {
|
||||||
|
padding: 1.5rem;
|
||||||
|
|
||||||
|
.afterpay-sections {
|
||||||
|
@include media-breakpoint-up(md) {
|
||||||
|
display: flex;
|
||||||
|
column-gap: 1rem;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
.afterpay-section {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
|
||||||
|
@include media-breakpoint-up(md) {
|
||||||
|
flex: 0 1 33%;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-of-type {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.modal-dialog-centered {
|
||||||
|
height: auto;
|
||||||
|
min-height: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.modal-footer) {
|
||||||
|
position: relative;
|
||||||
|
background: $gray-100;
|
||||||
|
|
||||||
|
.modal-disclaimer {
|
||||||
|
font-style: italic;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
a {
|
||||||
|
padding: 0;
|
||||||
|
line-height: 1.625;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#afterpay-learnmore {
|
|
||||||
font-weight: 600;
|
|
||||||
font-size: 0.875rem;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -45,10 +45,8 @@
|
||||||
<!-- If Cash AND vehicle does not require recal OR if Cash AND State requires showing recal, then show Afterpay banner -->
|
<!-- If Cash AND vehicle does not require recal OR if Cash AND State requires showing recal, then show Afterpay banner -->
|
||||||
<afterpayModalBanner
|
<afterpayModalBanner
|
||||||
v-if="showAfterpayBanner"
|
v-if="showAfterpayBanner"
|
||||||
cmsWidgetName="AfterpayModalWidget"
|
cmsWidgetName="AfterpayBannerWidget"
|
||||||
:afterpayExtendedPayOptionThreshold="afterpayExtendedPayOptionThreshold"
|
modalWidgetName="AfterpayModalWidget" />
|
||||||
:isInsuranceSelected="isInsuranceSelected"
|
|
||||||
:lineItems="lineItems" />
|
|
||||||
|
|
||||||
<!-- If vehicle requires recal AND we are hiding recal pricing info, then show recal disclaimer banner. -->
|
<!-- If vehicle requires recal AND we are hiding recal pricing info, then show recal disclaimer banner. -->
|
||||||
<recalDisclaimer
|
<recalDisclaimer
|
||||||
|
|
|
||||||
|
|
@ -958,6 +958,8 @@ function getPageNameFromRouter() {
|
||||||
) {
|
) {
|
||||||
return router.currentRoute.value.name;
|
return router.currentRoute.value.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return window.location.href.replace(/\/$/, "").split("/").pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
function getValueToLog(value, valueToLogType) {
|
function getValueToLog(value, valueToLogType) {
|
||||||
|
|
|
||||||
|
|
@ -3,16 +3,33 @@ import { queryStrings } from "@/constants/query-strings";
|
||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
import { storeMutations } from "@/constants/store-mutations";
|
import { storeMutations } from "@/constants/store-mutations";
|
||||||
import { updateOrCreateFunnelCookie } from "@/helpers/heritage-integration/cookie-helper";
|
import { updateOrCreateFunnelCookie } from "@/helpers/heritage-integration/cookie-helper";
|
||||||
|
import { getFunnelCookie } from "@/helpers/heritage-integration/cookie-helper";
|
||||||
|
|
||||||
export async function consumeReferralQuerystrings() {
|
export async function consumeReferralQuerystrings() {
|
||||||
const referralNumber = getQuerystringParameter(queryStrings.REFERRAL_NUMBER);
|
let referralNumber = getQuerystringParameter(queryStrings.REFERRAL_NUMBER);
|
||||||
const parentAccount = getQuerystringParameter(queryStrings.PARENT_ACCOUNT);
|
let parentAccount = getQuerystringParameter(queryStrings.PARENT_ACCOUNT);
|
||||||
const correlationId = getQuerystringParameter(queryStrings.CORRELATION_ID);
|
let correlationId = getQuerystringParameter(queryStrings.CORRELATION_ID);
|
||||||
|
let referralDate = null;
|
||||||
|
|
||||||
|
// If no referral number in querystring, check if heritage funnel updated last.
|
||||||
|
// This would indicate a customer that went to heritage but did not return through the
|
||||||
|
// normal route. ie: left the site in search of a discount and returned without querystrings.
|
||||||
|
const didHeritageFunnelUpdateLast = getFunnelCookie()?.DidHeritageFunnelUpdateLast;
|
||||||
|
if (didHeritageFunnelUpdateLast && !referralNumber) {
|
||||||
|
referralNumber = getFunnelCookie().ReferralNumber;
|
||||||
|
parentAccount = getFunnelCookie().ReferralParentAccountNumber;
|
||||||
|
correlationId = getFunnelCookie().ReferralCorrelationId;
|
||||||
|
referralDate = getFunnelCookie().ReferralDate;
|
||||||
|
}
|
||||||
|
|
||||||
if (referralNumber) {
|
if (referralNumber) {
|
||||||
store.commit(storeMutations.UPDATE_REFERRAL_NUMBER, referralNumber);
|
store.commit(storeMutations.UPDATE_REFERRAL_NUMBER, referralNumber);
|
||||||
store.commit(storeMutations.UPDATE_PARENT_ACCT_NUMBER, parentAccount);
|
store.commit(storeMutations.UPDATE_PARENT_ACCT_NUMBER, parentAccount);
|
||||||
store.commit(storeMutations.UPDATE_REFERRAL_CORRELATION_ID, correlationId);
|
store.commit(storeMutations.UPDATE_REFERRAL_CORRELATION_ID, correlationId);
|
||||||
// log(" --update cookie");
|
|
||||||
|
if (referralDate) {
|
||||||
|
store.commit(storeMutations.UPDATE_REFERRAL_DATE, referralDate);
|
||||||
|
}
|
||||||
updateOrCreateFunnelCookie();
|
updateOrCreateFunnelCookie();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,13 @@ import { queryStrings } from "@/constants/query-strings";
|
||||||
import { consumeReferralQuerystrings } from "@/router/methods/helpers/consume-referral-info";
|
import { consumeReferralQuerystrings } from "@/router/methods/helpers/consume-referral-info";
|
||||||
import { loadSessionIfPresent } from "@/helpers/heritage-integration/order-helper";
|
import { loadSessionIfPresent } from "@/helpers/heritage-integration/order-helper";
|
||||||
import { routeData } from "@/router/constants/routes";
|
import { routeData } from "@/router/constants/routes";
|
||||||
|
import { getFunnelCookie } from "@/helpers/heritage-integration/cookie-helper";
|
||||||
|
|
||||||
export async function handleHeritageReturn(to, from) {
|
export async function handleHeritageReturn(to, from) {
|
||||||
const fromHeritageFlag = to.query[queryStrings.FROM_HERITAGE];
|
const fromHeritageFlag = to.query[queryStrings.FROM_HERITAGE];
|
||||||
|
const didHeritageFunnelUpdateLast = getFunnelCookie()?.DidHeritageFunnelUpdateLast;
|
||||||
|
|
||||||
if (fromHeritageFlag) {
|
if (fromHeritageFlag || didHeritageFunnelUpdateLast) {
|
||||||
// Get new referral info from querystring in case passed there.
|
// Get new referral info from querystring in case passed there.
|
||||||
await consumeReferralQuerystrings();
|
await consumeReferralQuerystrings();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import router from "@/router";
|
||||||
import { routeData, FUNNEL_START_PAGE } from "@/router/constants/routes";
|
import { routeData, FUNNEL_START_PAGE } from "@/router/constants/routes";
|
||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
import { handleHardError } from "@/router/methods/error";
|
import { handleHardError } from "@/router/methods/error";
|
||||||
|
import analyticsMixin from "@/mixins/analytics-mixin";
|
||||||
|
|
||||||
export async function errorBeforeEnter(to, from) {
|
export async function errorBeforeEnter(to, from) {
|
||||||
// Check if `hasAlreadyTriggeredErrror` is available.
|
// Check if `hasAlreadyTriggeredErrror` is available.
|
||||||
|
|
@ -18,6 +19,7 @@ export async function errorBeforeEnter(to, from) {
|
||||||
nextPage: to?.name,
|
nextPage: to?.name,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
analyticsMixin.methods.logDigitalConsumer();
|
||||||
await handleHardError(errorPayload);
|
await handleHardError(errorPayload);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -31,6 +33,7 @@ export async function errorBeforeEnter(to, from) {
|
||||||
nextPage: to?.name,
|
nextPage: to?.name,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
analyticsMixin.methods.logDigitalConsumer();
|
||||||
await handleHardError(errorPayload);
|
await handleHardError(errorPayload);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -123,8 +123,8 @@ $body-color: $gray-600;
|
||||||
|
|
||||||
//Fonts
|
//Fonts
|
||||||
$font-family-sans-serif: UrbanistRegular, Arial, Helvetica, sans-serif;
|
$font-family-sans-serif: UrbanistRegular, Arial, Helvetica, sans-serif;
|
||||||
$font-family-monospace:
|
$font-family-monospace: UrbanistRegular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New",
|
||||||
UrbanistRegular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
monospace;
|
||||||
// stylelint-enable value-keyword-case
|
// stylelint-enable value-keyword-case
|
||||||
$font-family-base: $font-family-sans-serif;
|
$font-family-base: $font-family-sans-serif;
|
||||||
$font-family-code: $font-family-monospace;
|
$font-family-code: $font-family-monospace;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue