diff --git a/public/static/error/index.html b/public/static/error/index.html index a4c141f85..53bb9fc08 100644 --- a/public/static/error/index.html +++ b/public/static/error/index.html @@ -114,7 +114,7 @@ body { debug: true, }, { - name: 'Test', + name: 'SysTest', hostName: 'www-test2.safelite.com', apiHostname: 'digitalapi.test.safelite.io', debug: true, @@ -142,83 +142,178 @@ body { 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 () => { // =============== Check for session info: const existingVuexDataJSON = window.localStorage.getItem('vuex'); const existingVuexData = existingVuexDataJSON ? JSON.parse(existingVuexDataJSON) : null; const existingBailoutInfoJSON = window.sessionStorage.getItem('bailoutInfo'); 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(existingVuexData); console.log(`================ PRIOR BAILOUT DATA`); console.log(existingBailoutInfo); + console.log(`================ PRIOR HEADER DATA`); + console.log(existingHeaderInfo); let bailoutInfo = null; + let headerInfo = null; if(existingVuexData) { - try { - // =============== Collect diagnostic data: - bailoutInfo = []; + const results = extractDataFromVuex(existingVuexData); + bailoutInfo = results.bailoutInfo; + headerInfo = results.headerInfo; - // App & Site Name - bailoutInfo.push({ name: 'App Name', value: 'FixMyGlass' }); - bailoutInfo.push({ name: 'Site Name', value: 'SafeliteDotcom' }); + clearApplicationData(); - // 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 }); - - // - 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'); + if(bailoutInfo) { + window.sessionStorage.setItem('bailoutInfo', JSON.stringify(bailoutInfo)); } - } else if(existingBailoutInfo) { - // Proceed with prior data. - bailoutInfo = existingBailoutInfo; + + if(headerInfo) { + window.sessionStorage.setItem('headerInfo', JSON.stringify(headerInfo)); + } + } else { + bailoutInfo = existingBailoutInfo ?? []; + headerInfo = existingHeaderInfo ?? []; } const environmentInfo = getCurrentEnvironmentData(); @@ -275,11 +370,11 @@ body { ); const entryString = `User encountered bailout page.\n${new Date()}\n${infoString ?? 'No information recoverable'}`; + const headers = generateRequestHeader(headerInfo); + const request = new Request(endpointUrl, { method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, + headers: headers, body: JSON.stringify({ entry: entryString, }), @@ -292,6 +387,38 @@ body { console.error(`=== ERROR SENDING LOGGING`); 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); + } } }; diff --git a/public/static/healthcheck/index.html b/public/static/healthcheck/index.html index 9e20bde78..52e21bb5b 100644 --- a/public/static/healthcheck/index.html +++ b/public/static/healthcheck/index.html @@ -509,7 +509,7 @@ 'AccountService': { baseUrl: apiUrl + '/account/api/v1', endpoints: [ - //{ path: '/healthcheck', method: 'GET' }, + { path: '/healthcheck', method: 'GET' }, { path: '/account/insurance-companies', 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' } @@ -518,7 +518,7 @@ 'LocationService': { baseUrl: apiUrl + '/location/api/v1', endpoints: [ - //{ path: '/healthcheck', method: 'GET' }, + { path: '/healthcheck', method: 'GET' }, { path: '/location/zip/43015/Replace', method: 'GET' }, { path: '/location/alert-reasons/03357', method: 'GET' }, { path: '/location/providers/43015/Replace/100/167132/true/CR00046193/false', method: 'GET' }, @@ -528,7 +528,7 @@ 'VehicleService': { baseUrl: apiUrl + '/vehicle/api/v1', endpoints: [ - //{ path: '/healthcheck', method: 'GET' }, + { path: '/healthcheck', method: 'GET' }, { path: '/vehicle/years', method: 'GET' }, { path: '/vehicle/makes/1997', method: 'GET' }, { path: '/vehicle/Models/1997/Toyota', method: 'GET' }, @@ -539,7 +539,7 @@ 'PartService': { baseUrl: apiUrl + '/parts/api/v1', endpoints: [ - //{ path: '/healthcheck', method: 'GET' }, + { path: '/healthcheck', method: 'GET' }, { path: '/parts/damage-options/CR00046193', 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"} }, @@ -553,7 +553,7 @@ 'PriceService': { baseUrl: apiUrl + '/price/api/v1', 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/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"} }, @@ -563,7 +563,7 @@ 'ScheduleService': { baseUrl: apiUrl + '/schedule/api/v1', 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/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': { baseUrl: apiUrl + '/analytics/api/v1', endpoints: [ - //{ path: '/healthcheck', method: 'GET' }, + { path: '/healthcheck', method: 'GET' }, ] }, 'ExperimentService': { baseUrl: apiUrl + '/experiments/api/v1', endpoints: [ - //{ path: '/healthcheck', method: 'GET' }, + { path: '/healthcheck', method: 'GET' }, ] }, 'OrderService': { baseUrl: apiUrl + '/order/api/v1', endpoints: [ - //{ path: '/healthcheck', method: 'GET' }, + { path: '/healthcheck', method: 'GET' }, ] }, 'ContentService': { baseUrl: apiUrl + '/content/api/v1', endpoints: [ - //{ path: '/healthcheck', method: 'GET' }, + { path: '/healthcheck', method: 'GET' }, { path: '/content/fmg/vehicle', method: 'GET' }, ] } @@ -674,7 +674,7 @@ } 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; let text = 'Healthy'; let className = 'stat-pill stat-pill-healthy'; @@ -763,14 +763,15 @@ // Determine overall service status const endpoints = healthData[serviceName].endpoints; const healthyCount = endpoints.filter(ep => !isDegraded(ep) && !isUnhealthy(ep)).length; + const unhealthyCount = endpoints.filter(ep => isUnhealthy(ep)).length; const totalCount = endpoints.length; if (healthyCount === totalCount) { healthData[serviceName].status = 'healthy'; - } else if (healthyCount > 0) { - healthData[serviceName].status = 'degraded'; - } else { + } else if (unhealthyCount > 0) { healthData[serviceName].status = 'unhealthy'; + } else { + healthData[serviceName].status = 'degraded'; } } diff --git a/src/digital-components/modal/modal.vue b/src/digital-components/modal/modal.vue index 867e73c75..3c3efd05b 100644 --- a/src/digital-components/modal/modal.vue +++ b/src/digital-components/modal/modal.vue @@ -33,6 +33,7 @@