diff --git a/src/constants/endpoints.js b/src/constants/endpoints.js index aa0643821..085651f20 100644 --- a/src/constants/endpoints.js +++ b/src/constants/endpoints.js @@ -176,6 +176,10 @@ const endpoints = { url: "/analytics/api/v1/analytics/log-part-questions", method: "POST", }, + LogDigitalConsumer: { + url: "/analytics/api/v1/analytics/digitalconsumer-log", + method: "POST", + }, GetExperimentsByUser: { url: "/analytics/api/v1/analytics/get-experiments", method: "GET", diff --git a/src/constants/experiments.js b/src/constants/experiments.js index c5c24536d..871c6fd24 100644 --- a/src/constants/experiments.js +++ b/src/constants/experiments.js @@ -26,6 +26,7 @@ const experimentSettings = { FOSTER_LOVE: "DisplayFosterLove", SKIP_TO_INSURANCE: "SkipToInsurance", DISPLAY_AFTERPAY_BREAKOUT_DISPLAY: "Display_AfterpayBreakoutDisplay", + DYNAMO_LOGGING: "DynamoLogging", }; const experimentTriggers = { diff --git a/src/constants/progress-bar-mapper.js b/src/constants/progress-bar-mapper.js index 98ae07992..dc1bc4a05 100644 --- a/src/constants/progress-bar-mapper.js +++ b/src/constants/progress-bar-mapper.js @@ -15,6 +15,7 @@ const pagePercentageMapper = { "insurance-company": 52, "service-location": 60, schedule: 72, + "mobile-details": 76, "customer-details": 84, "payment-method": 92, payment: 96, diff --git a/src/constants/state-options.js b/src/constants/state-options.js new file mode 100644 index 000000000..19eb348d5 --- /dev/null +++ b/src/constants/state-options.js @@ -0,0 +1,54 @@ +const stateOptions = { + AL: "Alabama", + AK: "Alaska", + AZ: "Arizona", + AR: "Arkansas", + CA: "California", + CO: "Colorado", + CT: "Connecticut", + DE: "Delaware", + DC: "District Of Columbia", + FL: "Florida", + GA: "Georgia", + HI: "Hawaii", + ID: "Idaho", + IL: "Illinois", + IN: "Indiana", + IA: "Iowa", + KS: "Kansas", + KY: "Kentucky", + LA: "Louisiana", + ME: "Maine", + MD: "Maryland", + MA: "Massachusetts", + MI: "Michigan", + MN: "Minnesota", + MS: "Mississippi", + MO: "Missouri", + MT: "Montana", + NE: "Nebraska", + NV: "Nevada", + NH: "New Hampshire", + NJ: "New Jersey", + NM: "New Mexico", + NY: "New York", + NC: "North Carolina", + ND: "North Dakota", + OH: "Ohio", + OK: "Oklahoma", + OR: "Oregon", + PA: "Pennsylvania", + RI: "Rhode Island", + SC: "South Carolina", + SD: "South Dakota", + TN: "Tennessee", + TX: "Texas", + UT: "Utah", + VT: "Vermont", + VA: "Virginia", + WA: "Washington", + WV: "West Virginia", + WI: "Wisconsin", + WY: "Wyoming", +}; +export { stateOptions }; diff --git a/src/constants/store-actions.js b/src/constants/store-actions.js index 9f0965480..0fc1cc1b0 100644 --- a/src/constants/store-actions.js +++ b/src/constants/store-actions.js @@ -63,6 +63,7 @@ const storeActions = { LOG_CUSTOM_EVENT: "logCustomEvent", INITIALIZE_SESSION: "initializeSession", LOG_PART_QUESTIONS: "logPartQuestions", + LOG_DIGITALCONSUMER: "logDigitalConsumer", // DEPENDENCY MUTATIONS RESET_VEHICLE_STATE_AND_DEPENDENCIES: "resetVehicleStateAndDependencies", @@ -78,6 +79,7 @@ const storeActions = { SAVE_VIN_LOOKUP: "saveVinLookup", SAVE_SERVICE_ZIP_CODE_INFO: "saveServiceZipCodeInfo", SAVE_SERVICE_LOCATION: "saveServiceLocation", + SAVE_MOBILE_DETAILS: "saveMobileDetails", SAVE_SCHEDULE: "saveSchedule", SAVE_EMAIL: "saveEmail", SAVE_PHONE_NUMBER: "savePhoneNumber", diff --git a/src/constants/store-mutations.js b/src/constants/store-mutations.js index f7e8d9f4b..2112c789c 100644 --- a/src/constants/store-mutations.js +++ b/src/constants/store-mutations.js @@ -38,6 +38,7 @@ const storeMutations = { UPDATE_SERVICE_ZIP: "updateServiceZip", UPDATE_SERVICE_LOCATION: "updateServiceLocation", + UPDATE_MOBILE_DETAILS: "updateMobileDetails", UPDATE_SERVICE_LOCATION_TECH_NOTES: "updateServiceLocationTechNotes", UPDATE_SCHEDULE: "updateSchedule", diff --git a/src/experiment-components/donation-block.vue b/src/experiment-components/donation-block.vue index fde5509f8..34cd4178a 100644 --- a/src/experiment-components/donation-block.vue +++ b/src/experiment-components/donation-block.vue @@ -7,8 +7,10 @@
-
- +
+ + EIN: 26-3043727 +

@@ -79,7 +81,8 @@
-

+

+

@@ -121,6 +124,9 @@ export default { DonationFooterText() { return this.getCmsContent(this.cmsWidgetName, "FooterText"); }, + DonationFooterText2() { + return this.getCmsContent(this.cmsWidgetName, "FooterText2"); + }, LogoImage() { return this.getCmsContent("DonationWidget", "Image"); }, @@ -195,8 +201,26 @@ export default { } } - img { - width: 5rem; + .logo-block { + display: flex; + flex-direction: column; + background: white; + align-items: flex-start; + justify-content: center; + padding: 0 0.5rem; + margin-right: 1rem; + height: max-content; + + img { + width: 88px; + } + + span { + font-size: 0.625rem; + margin: 0 auto; + padding: 0.5rem 0; + font-family: UrbanistSemibold; + } } .donation-slider { diff --git a/src/fmg-components/cart/cart.vue b/src/fmg-components/cart/cart.vue index 55e062e24..fb717d283 100644 --- a/src/fmg-components/cart/cart.vue +++ b/src/fmg-components/cart/cart.vue @@ -119,7 +119,7 @@
{{ donationCartItemName }} - {{ getLineItemAmount(donationCartItem.subTotal) }} + {{ getLineItemAmount(donationCartItem.sellingPrice) }}
{{ amountDueText }} @@ -182,6 +182,7 @@ import { getAmountDue, getSubTotal, getSalesTax, + getAmountDueWithDonation, } from "@/helpers/pricing-helper.js"; // Constants @@ -211,6 +212,7 @@ export default { isNoComp: Boolean, isExpandedOnLoad: Boolean, isMSRFeeApplicable: Boolean, + donationCartItem: Object, }, data() { return { @@ -1083,33 +1085,6 @@ export default { donationCartItemName() { return this.getCmsContent("DonationCartTextWidget", "Text"); }, - donationCartItem() { - if (!this.lineItems || !this.lineItems.supportingItems) { - return null; - } - - const donationLineItem = this.lineItems.supportingItems.find( - (item) => item.partType === partTypeStrings.DONATION - ); - - if (donationLineItem) { - return { - name: this.donationCartItemName, - category: cartItemCategories.SUPPORTING_ITEMS, - cartItemType: cartItemTypes.DONATION, - isDisplayed: true, - isRemovable: false, - subTotal: donationLineItem.sellingPrice, - salesTax: 0, - lineItems: [], - isCoveredByInsurance: cartItemTypesCoveredByInsurance.includes( - cartItemTypes.DONATION - ), - }; - } - - return null; - }, removeLinkText() { return this.getCmsContent("RemoveCartItemTextWidget", "Text"); }, @@ -1156,9 +1131,15 @@ export default { amountDue() { if (!this.lineItems || this.lineItems.length < 1) return; if (this.showAsPaid) return 0; - return this.isInsurance || !this.shouldHideRecalibration - ? getAmountDue(this.lineItems) // calculate with recal (if on order) - : getAmountDue(this.lineItemsWithoutRecal); // calculated without recal + if (this.isInsurance || !this.shouldHideRecalibration) { + return this.donationCartItem + ? getAmountDueWithDonation(this.lineItems, this.donationCartItem) + : getAmountDue(this.lineItems); + } else { + return this.donationCartItem + ? getAmountDueWithDonation(this.lineItemsWithoutRecal, this.donationCartItem) + : getAmountDue(this.lineItemsWithoutRecal); + } }, amountPaid() { if (!this.showAsPaid) { @@ -1311,8 +1292,7 @@ export default { .amount-due, .amount-paid, .donation-amount { - font-family: - UrbanistSemibold, AvertaSemibold; /* Okay to remove AvertaSemibold after 2025.06.19 merge/release */ + font-family: UrbanistSemibold; color: $black; } .sub-total { diff --git a/src/global-methods.js b/src/global-methods.js index e90843bdc..75fabb356 100644 --- a/src/global-methods.js +++ b/src/global-methods.js @@ -100,34 +100,38 @@ export default { return resolve(response); }, (error) => { - if (logApiCall) { - analyticsMixIn.methods.pushEventToGA( - GaCategories.API_RESPONSE, - GaActions.RESULT, - `${GaLabels.ERROR}_${endpoint}`, - true - ); + if (endpoint.toLowerCase().includes("/digitalconsumer-log")) { + return resolve({ data: null, error: "Ignore errors when logging" }); + } else { + if (logApiCall) { + analyticsMixIn.methods.pushEventToGA( + GaCategories.API_RESPONSE, + GaActions.RESULT, + `${GaLabels.ERROR}_${endpoint}`, + true + ); + } + + if (error.response.status && error.response.status != "404") { + // Do not route to error logic when no wipers found or no promo found (404s) + const errorPayload = { + cause: `Response error ${error.response.status}`, + currentPage: pageNameToLog, + endpoint: endpoint, + }; + + router.bailout(errorPayload); + + // do not log 404 errors from services because we return NotFound + // when a service doesn't return an object + global.$logger.logError( + `${method}: ${endpoint}: ${error.message}`, + error.response + ); + } + + return reject(error.response); } - - if (error.response.status && error.response.status != "404") { - // Do not route to error logic when no wipers found or no promo found (404s) - const errorPayload = { - cause: `Response error ${error.response.status}`, - currentPage: pageNameToLog, - endpoint: endpoint, - }; - - router.bailout(errorPayload); - - // do not log 404 errors from services because we return NotFound - // when a service doesn't return an object - global.$logger.logError( - `${method}: ${endpoint}: ${error.message}`, - error.response - ); - } - - return reject(error.response); } ); }); diff --git a/src/helpers/pricing-helper.js b/src/helpers/pricing-helper.js index fda5b3b9c..33311fd21 100644 --- a/src/helpers/pricing-helper.js +++ b/src/helpers/pricing-helper.js @@ -1,6 +1,7 @@ import store from "@/store"; import { storeActions } from "@/constants/store-actions"; import baseMixin from "@/mixins/base-mixin.js"; +import { deepClone } from "@/helpers/object-helper"; export function getDisplayAmountDue(lineItemsObject, includeTax = true) { return getAmountDue(lineItemsObject, includeTax).toLocaleString("en-US", { @@ -62,6 +63,13 @@ export function getSalesTax(lineItemsObject) { ); } +export function getAmountDueWithDonation(lineItemsObject, donationLineItem) { + // this is amount due with Donation added + const lineItemsCloneWithDonation = deepClone(lineItemsObject); + lineItemsCloneWithDonation.supportingItems.push(donationLineItem); + return getAmountDue(lineItemsCloneWithDonation, false); +} + export async function getPricingByDayPartWithPrice(pageNameToLog) { // Get the Pricing By Day Part const basePriceByDayPart = await baseMixin.methods.dispatchStoreActionWithLogging( diff --git a/src/layouts/confirmation/confirmation.vue b/src/layouts/confirmation/confirmation.vue index e65eacc3c..e8c01fd05 100644 --- a/src/layouts/confirmation/confirmation.vue +++ b/src/layouts/confirmation/confirmation.vue @@ -55,7 +55,8 @@ :damage="damageInfo" :availableVaps="vaps" :allowItemRemoval="false" - v-model="lineItems" + v-model="lineItemsWithoutDonation" + :donationCartItem="donationLineItem" :showAsPaid="isPia" servicePackageOptionsCmsName="ServicePackageTitle" :isInsurance="isInsurance" @@ -123,6 +124,7 @@ import experimentMixin from "@/mixins/experiment-mixin.js"; import { containsRecalParts } from "@/helpers/recal-helper.js"; import donationBlock from "@/experiment-components/donation-block.vue"; import { partNumberStrings } from "@/constants/part-number-strings"; +import { partTypeStrings } from "@/constants/part-type-strings"; import analyticsMixin from "@/mixins/analytics-mixin"; export default { @@ -229,7 +231,7 @@ export default { }; const donationItems = lineItemsFromSubmittedOrder.supportingItems?.filter( - (item) => item.partNumber === partNumberStrings.DONATION + (item) => item.partType === partTypeStrings.DONATION ); const donationAmount = donationItems?.length > 0 ? donationItems[0].sellingPrice : 0; @@ -243,7 +245,7 @@ export default { vm.shouldDisplayFosterLove = hasFosterLoveExperiment; vm.donationAmount = donationAmount; vm.showDonationSuccess = lineItemsFromSubmittedOrder.supportingItems?.some( - (item) => item.partType === partNumberStrings.DONATION + (item) => item.partType === partTypeStrings.DONATION ); vm.pushAnalytics(); @@ -490,6 +492,22 @@ export default { donationValues() { return [1, 3, 5]; }, + lineItemsWithoutDonation() { + const lineItemsClone = deepClone(this.lineItems); + lineItemsClone.supportingItems = lineItemsClone.supportingItems?.filter( + (item) => item.partType !== partTypeStrings.DONATION + ); + return lineItemsClone; + }, + donationLineItem() { + if (!this.lineItems) return null; + const donationLineItems = this.lineItems.supportingItems?.filter( + (item) => item.partType === partTypeStrings.DONATION + ); + let output = + donationLineItems && donationLineItems.length > 0 ? donationLineItems[0] : null; + return output; + }, }, methods: { arePagePrerequisitesValid() { diff --git a/src/layouts/customer-details/customer-details.vue b/src/layouts/customer-details/customer-details.vue index e3549b813..f473aa50b 100644 --- a/src/layouts/customer-details/customer-details.vue +++ b/src/layouts/customer-details/customer-details.vue @@ -85,6 +85,7 @@ import { Form, defineRule } from "vee-validate"; import store from "@/store"; import { paymentMethods } from "@/constants/payment-method-constants"; import { flushSaveSessionQueue } from "@/helpers/heritage-integration/order-helper"; +import { AppointmentTypeStrings } from "@/constants/schedule-constants"; // DEFINE VALIDATION RULES defineRule("first-name-required", required(errorMessages.FIRST_NAME_REQUIRED)); @@ -157,11 +158,21 @@ export default { getTechNotesFromStore() { return store.getters.order.serviceLocation.techNotes; }, + getSelectedAppointmentType() { + return store.getters.order.serviceLocation.appointmentType; + }, backButtonAction() { - this.$router.navigateWithoutSaving( - this.navigationScenarios.CLICKED_BACK, - this.pageName - ); + if (this.getSelectedAppointmentType() == AppointmentTypeStrings.MOBILE) { + this.$router.navigateWithoutSaving( + this.navigationScenarios.CLICKED_BACK_WITH_MOBILE_SERVICE, + this.pageName + ); + } else { + this.$router.navigateWithoutSaving( + this.navigationScenarios.CLICKED_BACK, + this.pageName + ); + } }, async forwardButtonAction() { this.dispatchStoreAction( diff --git a/src/layouts/mobile-details/mobile-address-questions/mobile-address-questions.spec.js b/src/layouts/mobile-details/mobile-address-questions/mobile-address-questions.spec.js new file mode 100644 index 000000000..06a3ca901 --- /dev/null +++ b/src/layouts/mobile-details/mobile-address-questions/mobile-address-questions.spec.js @@ -0,0 +1,198 @@ +// Components +import mobileAddressQuestions from "@/layouts/mobile-details/mobile-address-questions/mobile-address-questions.vue"; + +// Supporting Files +import { mount, shallowMount } from "@vue/test-utils"; +import { getMountOptions } from "@/helpers/unit-test-helper.js"; +import { storeMutations } from "@/constants/store-mutations"; +import store from "@/store"; + +describe("initial state", () => { + test("Should show all the address field", () => { + // Arrange + const { wrapper } = setupMocks({}); + + // Assert + const streetAddressField = wrapper.findComponent({ ref: "streetAddress" }); + const cityField = wrapper.findComponent({ ref: "city" }); + const stateField = wrapper.findComponent({ ref: "state" }); + const zipCodeField = wrapper.findComponent({ ref: "zipCode" }); + + expect(streetAddressField.exists()).toBe(true); + expect(streetAddressField.isVisible()).toBe(true); + expect(cityField.exists()).toBe(true); + expect(cityField.isVisible()).toBe(true); + expect(stateField.exists()).toBe(true); + expect(stateField.isVisible()).toBe(true); + expect(zipCodeField.exists()).toBe(true); + expect(zipCodeField.isVisible()).toBe(true); + }); + + test("Should render mobileAddressQuestions sub-components (textbox-questions and dropdown-questions)", async () => { + // Arrange + const { wrapper } = setupMocks({}); + + // Act + const streetAddress = wrapper.findComponent({ ref: "streetAddress" }); + const city = wrapper.findComponent({ ref: "city" }); + const state = wrapper.findComponent({ ref: "state" }); + const zipCode = wrapper.findComponent({ ref: "zipCode" }); + + // Assert + expect(streetAddress.exists()).toBe(true); + expect(city.exists()).toBe(true); + expect(state.exists()).toBe(true); + expect(zipCode.exists()).toBe(true); + }); + + test("Should render apartmentNumberOrBusinessName textbox-question when captureApartmentNumberOrBusinessName = true", async () => { + // Arrange + const { wrapper } = setupMocks({ + props: { + modelValue: { + streetAddress: "", + apartmentNumberOrBusinessName: "", + city: "", + state: "", + zipCode: "", + }, + captureApartmentNumberOrBusinessName: true, + }, + }); + + // Act + const streetAddress = wrapper.findComponent({ ref: "streetAddress" }); + const apartmentNumberOrBusinessName = wrapper.findComponent({ + ref: "apartmentNumberOrBusinessName", + }); + const city = wrapper.findComponent({ ref: "city" }); + const state = wrapper.findComponent({ ref: "state" }); + const zipCode = wrapper.findComponent({ ref: "zipCode" }); + + // Assert + expect(streetAddress.exists()).toBe(true); + expect(apartmentNumberOrBusinessName.exists()).toBe(true); + expect(apartmentNumberOrBusinessName.isVisible()).toBe(true); + expect(city.exists()).toBe(true); + expect(state.exists()).toBe(true); + expect(zipCode.exists()).toBe(true); + }); + + test("Should *not* render apartmentNumberOrBusinessName textbox-question when captureApartmentNumberOrBusinessName = false", async () => { + // Arrange + const { wrapper } = setupMocks({ + props: { + modelValue: { + streetAddress: "", + apartmentNumberOrBusinessName: "", + city: "", + state: "", + zipCode: "", + }, + captureApartmentNumberOrBusinessName: false, + }, + }); + + // Act + const streetAddress = wrapper.findComponent({ ref: "streetAddress" }); + const apartmentNumberOrBusinessName = wrapper.findComponent({ + ref: "apartmentNumberOrBusinessName", + }); + const city = wrapper.findComponent({ ref: "city" }); + const state = wrapper.findComponent({ ref: "state" }); + const zipCode = wrapper.findComponent({ ref: "zipCode" }); + + // Assert + expect(streetAddress.exists()).toBe(true); + expect(apartmentNumberOrBusinessName.exists()).toBe(true); + expect(apartmentNumberOrBusinessName.isVisible()).toBe(false); + expect(city.exists()).toBe(true); + expect(state.exists()).toBe(true); + expect(zipCode.exists()).toBe(true); + }); +}); + +describe("happy paths", () => { + test("full street address is passed in => address fields are displayed", async () => { + // Arrange/Act + const { wrapper } = setupMocks({ + props: { + modelValue: { + streetAddress: "12345 Test Road", + city: "Tests", + state: "OH", + zipCode: "12312", + }, + }, + }); + + await wrapper.vm.$nextTick(); + + // Assert + const cityField = wrapper.findComponent({ ref: "city" }); + const stateField = wrapper.findComponent({ ref: "state" }); + const zipField = wrapper.findComponent({ ref: "zipCode" }); + expect(cityField.exists()).toBeTruthy(); + expect(cityField.isVisible()).toBeTruthy(); + expect(stateField.exists()).toBeTruthy(); + expect(cityField.isVisible()).toBeTruthy(); + expect(zipField.exists()).toBeTruthy(); + expect(cityField.isVisible()).toBeTruthy(); + }); +}); + +function setupMocks({ + mountOptions, + props, + isShallowMount = true, + querySelectorFunction, + geocoderResult = ["1234 Test Street"], + matchFound = false, +}) { + store.commit(storeMutations.RESET_STATE); + + const resultingMountOptions = getMountOptions({ + ...mountOptions, + router: { + navigate: jest.fn(), + navigate: jest.fn(), + }, + loadScript: jest.fn().mockResolvedValue(), + }); + + if (props) resultingMountOptions.propsData = props; + + if (isShallowMount) { + resultingMountOptions.global.stubs = { + ...(resultingMountOptions.global.stubs ?? {}), + textboxQuestion: { + template: ``, + methods: { + handleChange: jest.fn(), + }, + }, + dropdownQuestion: { + template: ``, + methods: { + handleChange: jest.fn(), + }, + }, + }; + } + + const wrapper = isShallowMount + ? shallowMount(mobileAddressQuestions, resultingMountOptions) + : mount(mobileAddressQuestions, resultingMountOptions); + + document.querySelector = jest.fn().mockImplementation((query) => { + let result = null; + if (query == ".pac-container") result = document.createElement("div"); + else if (querySelectorFunction) { + result = querySelectorFunction(query); + } + + return result ?? null; + }); + + return { wrapper }; +} diff --git a/src/layouts/mobile-details/mobile-address-questions/mobile-address-questions.vue b/src/layouts/mobile-details/mobile-address-questions/mobile-address-questions.vue new file mode 100644 index 000000000..2e5dd8324 --- /dev/null +++ b/src/layouts/mobile-details/mobile-address-questions/mobile-address-questions.vue @@ -0,0 +1,170 @@ + + + + + diff --git a/src/layouts/mobile-details/mobile-details.spec.js b/src/layouts/mobile-details/mobile-details.spec.js new file mode 100644 index 000000000..8ed9e27b7 --- /dev/null +++ b/src/layouts/mobile-details/mobile-details.spec.js @@ -0,0 +1,139 @@ +import mobileDetails from "@/layouts/mobile-details/mobile-details.vue"; +import { mount, shallowMount } from "@vue/test-utils"; +import { storeActions } from "@/constants/store-actions"; +import { getMountOptions } from "@/helpers/unit-test-helper.js"; +import store from "@/store"; + +const linkWidgetName = "linkWidgetName"; +const modalWidgetName = "modalWidgetName"; + +const mockLinkCmsContent = { + BodyText: "Sample link body text here.", +}; + +const mockModalCmsContent = { + FooterText: "Sample modal footer text here.", +}; + +const mockMixin = { + methods: { + getCmsContent: jest.fn((widgetName, cmsFieldName) => { + if (widgetName === linkWidgetName) { + return mockLinkCmsContent[cmsFieldName]; + } + + if (widgetName === modalWidgetName) { + return mockModalCmsContent[cmsFieldName]; + } + + return null; + }), + dispatchStoreAction: jest.fn((actionName) => { + if (actionName === storeActions.SAVE_MOBILE_DETAILS) { + return true; + } + }), + }, +}; + +beforeEach(() => { + store.getters = { + order: { + serviceLocation: { + zipCode: "43235", + zipCodeCtu: "00000", + state: "OH", + appointmentType: "Mobile", + city: "Columbus", + address: "123 Main St", + address2: "Suite 100", + isVehicleProtected: true, + }, + schedule: { + date: "2023-10-10", + endTime: "14:00", + startTime: "13:00", + routeCode: "R1", + jobMaxMinutes: "60", + jobMinMinutes: "30", + }, + }, + payment: { + isInsurance: false, + }, + }; +}); + +describe("mobile-details.vue", () => { + test("if the continue button is clicked, navigate forward", async () => { + // Arrange + const { wrapper } = setupMocks(mobileDetails, { + mixins: [mockMixin], + attachTo: document.body, + }); + + wrapper.setData({ + addressQuestions: { + streetAddress: "Test Street", + apartmentNumberOrBusinessName: "Test Apartment", + city: "Test City", + state: "TS", + zipCode: "12345", + }, + isVehicleProtected: true, + }); + + // Act + await wrapper.vm.forwardButtonAction(); + + // Assert + expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalled(); + }); + test("arePagePrerequisitesValid should be true ", async () => { + //Arrange + const { wrapper } = setupMocks(mobileDetails, { + mixins: [mockMixin], + attachTo: document.body, + }); + + //Act + let arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid(); + + //Assert + expect(arePagePrerequisitesValid).toBe(true); + }); + + test("if the back button is clicked, navigate back", async () => { + // Arrange + const { wrapper } = setupMocks(mobileDetails, { + mixins: [mockMixin], + attachTo: document.body, + }); + + // Act + await wrapper.vm.backButtonAction(); + + // Assert + expect(wrapper.vm.$router.navigateWithoutSaving).toHaveBeenCalled(); + }); +}); + +function setupMocks({ mountOptions, mixins, props, isShallowMount = true }) { + const resultingMountOptions = getMountOptions({ + ...mountOptions, + mixins, + router: { + navigate: jest.fn(), + navigateWithSaving: jest.fn(), + navigateWithoutSaving: jest.fn(), + }, + }); + + if (props) resultingMountOptions.propsData = props; + resultingMountOptions["attachTo"] = document.body; + const wrapper = isShallowMount + ? shallowMount(mobileDetails, resultingMountOptions) + : mount(mobileDetails, resultingMountOptions); + + return { wrapper }; +} diff --git a/src/layouts/mobile-details/mobile-details.vue b/src/layouts/mobile-details/mobile-details.vue new file mode 100644 index 000000000..25268805c --- /dev/null +++ b/src/layouts/mobile-details/mobile-details.vue @@ -0,0 +1,168 @@ + + diff --git a/src/layouts/service-location/service-location.spec.js b/src/layouts/service-location/service-location.spec.js index 58187496e..551e15ae0 100644 --- a/src/layouts/service-location/service-location.spec.js +++ b/src/layouts/service-location/service-location.spec.js @@ -314,57 +314,6 @@ describe("service-location.vue", () => { expect(wrapper.vm.serviceZipCodeQuestion).toStrictEqual(newServiceZipCodeQuestion); }); - test("resets mobile location when service zip code is updated", () => { - // Arrange - const { wrapper } = setupMocks({}); - - const mobileLocationQuestionsComponent = wrapper.findComponent({ - ref: "mobileLocationQuestions", - }); - mobileLocationQuestionsComponent.resetComponent = jest.fn(); - - const serviceZipCodeComponent = wrapper.findComponent({ - ref: "serviceZipCodeQuestion", - }); - serviceZipCodeComponent.resetMobileFeePart = jest.fn(); - - const newServiceZipCodeQuestion = { - zipCode: "61606", - state: "IL", - zipCodeCtu: "01526", - }; - - const mobileLocationQuestions = { - addressQuestions: { - streetAddress: "5555 Sulgrave Dr", - apartmentNumberOrBusinessName: "Apt 1", - city: "New Albany", - state: "OH", - zipCode: "43054", - }, - isVehicleProtected: true, - }; - wrapper.vm.mobileLocationQuestions = mobileLocationQuestions; - - const newMobileLocationQuestions = { - addressQuestions: { - streetAddress: "", - apartmentNumberOrBusinessName: "", - city: "", - state: "IL", - zipCode: "61606", - }, - isVehicleProtected: null, - isMobileSelected: false, - }; - - // Act - serviceZipCodeComponent.vm.$emit("update:modelValue", newServiceZipCodeQuestion); - - // Assert - expect(wrapper.vm.mobileLocationQuestions).toStrictEqual(newMobileLocationQuestions); - }); - test("resets appointment type selection when service zip code is updated by service zip modal", () => { // Arrange const { wrapper } = setupMocks({}); @@ -424,190 +373,6 @@ describe("service-location.vue", () => { }); }); - describe("updating mobile location", () => { - test("updates the page model after providing the mobile location", async () => { - // Arrange - const { wrapper } = setupMocks({}); - - await wrapper.setData({ - shopProviderData: { - mobileProviderNumber: "001820", - }, - selectedAppointmentType: "Mobile", - providerData: { mobileProviderNumber: "01820" }, - isGlassServiceableMobile: true, - }); - - const mobileLocationQuestionsComponent = wrapper.findComponent({ - ref: "mobileLocationQuestions", - }); - mobileLocationQuestionsComponent.resetComponent = jest.fn(); - - const serviceZipCodeComponent = wrapper.findComponent({ - ref: "serviceZipCodeQuestion", - }); - serviceZipCodeComponent.resetMobileFeePart = jest.fn(); - wrapper.vm.forwardButtonAction = jest.fn(); - - const mobileLocationQuestions = { - addressQuestions: { - streetAddress: "", - apartmentNumberOrBusinessName: "", - city: "", - state: "", - zipCode: "", - }, - isVehicleProtected: null, - }; - wrapper.vm.mobileLocationQuestions = mobileLocationQuestions; - const newMobileLocationQuestions = { - addressQuestions: { - streetAddress: "5555 Sulgrave Dr", - apartmentNumberOrBusinessName: "Apt 1", - city: "New Albany", - state: "OH", - zipCode: "43054", - }, - isVehicleProtected: true, - isMobileSelected: true, - }; - - // Act - - // Trigger the event - await mobileLocationQuestionsComponent.vm.$emit( - "setMobileLocation", - newMobileLocationQuestions - ); - - await wrapper.vm.$nextTick(); // Wait for DOM updates - - // Assert - expect(wrapper.vm.mobileLocationQuestions).toStrictEqual(newMobileLocationQuestions); - }); - - test("resets service zip code when mobile location is updated", async () => { - // Arrange - const { wrapper } = setupMocks({}); - - await wrapper.setData({ - shopProviderData: { - mobileProviderNumber: "001820", - }, - selectedAppointmentType: "Mobile", - providerData: { mobileProviderNumber: "01820" }, - isGlassServiceableMobile: true, - }); - - const mobileLocationQuestionsComponent = wrapper.findComponent({ - ref: "mobileLocationQuestions", - }); - mobileLocationQuestionsComponent.resetComponent = jest.fn(); - - const serviceZipCodeComponent = wrapper.findComponent({ - ref: "serviceZipCodeQuestion", - }); - serviceZipCodeComponent.resetMobileFeePart = jest.fn(); - wrapper.vm.forwardButtonAction = jest.fn(); - const mobileLocationQuestions = { - addressQuestions: { - streetAddress: "", - apartmentNumberOrBusinessName: "", - city: "", - state: "", - zipCode: "", - }, - isVehicleProtected: null, - isMobileSelectedAndModalClosed: false, - mobileFeePart: null, - }; - wrapper.vm.mobileLocationQuestions = mobileLocationQuestions; - - const newMobileLocationQuestions = { - addressQuestions: { - streetAddress: "5555 Rustic Dr", - apartmentNumberOrBusinessName: "Apt 1", - city: "Westerville", - state: "OH", - zipCode: "43081", - }, - isVehicleProtected: true, - isMobileSelectedAndModalClosed: true, - mobileFeePart: null, - }; - - wrapper.vm.serviceZipCodeQuestion = { - zipCode: "61606", - state: "IL", - zipCodeCtu: "01526", - }; - - const newServiceZipCodeInfo = { - state: "OH", - zipCode: "43081", - zipCodeCtu: "01526", - }; - - wrapper.vm.closeModalAction = jest.fn(); - // Act - - // Trigger the event - await mobileLocationQuestionsComponent.vm.$emit( - "setMobileLocation", - newMobileLocationQuestions - ); - - await wrapper.vm.$nextTick(); // Wait for DOM updates - - // Assert - expect(wrapper.vm.serviceZipCodeQuestion).toStrictEqual(newServiceZipCodeInfo); - }); - - test("does not reset appointment type selection when service zip code is updated by mobile location modal when Mobile is selected", async () => { - // Arrange - const { wrapper } = setupMocks({}); - - await wrapper.setData({ - shopProviderData: { - mobileProviderNumber: "001820", - }, - selectedAppointmentType: "Mobile", - providerData: { mobileProviderNumber: "01820" }, - }); - - const mobileLocationQuestionsComponent = wrapper.findComponent({ - ref: "mobileLocationQuestions", - }); - mobileLocationQuestionsComponent.resetComponent = jest.fn(); - wrapper.vm.forwardButtonAction = jest.fn(); - wrapper.vm.isServiceableMobile = true; - - const serviceZipCodeComponent = wrapper.findComponent({ - ref: "serviceZipCodeQuestion", - }); - serviceZipCodeComponent.resetMobileFeePart = jest.fn(); - - const mobileLocationQuestions = { - addressQuestions: { - streetAddress: "555 Some Street", - apartmentNumberOrBusinessName: "", - city: "Westerville", - state: "OH", - zipCode: "43081", - }, - isVehicleProtected: "YesAnswer", - isMobileSelectedAndModalClosed: true, - }; - - wrapper.vm.closeModalAction = jest.fn(); - // Act - mobileLocationQuestionsComponent.vm.$emit("setMobileLocation", mobileLocationQuestions); - - // Assert - expect(wrapper.vm.selectedAppointmentType).toStrictEqual("Mobile"); - }); - }); - describe("serviceability logic", () => { describe("should be logical AND when recalibration is defined.", () => { test("T & T => T", async () => { @@ -1434,8 +1199,5 @@ function setupMocks({ mountOptionsMockData = {} }) { const mountOptions = getMountOptions(mountOptionsMockData); const wrapper = shallowMount(serviceLocation, mountOptions); wrapper.vm.setCmsContent = baseMixin.methods.setCmsContent; - wrapper.vm.$refs.mobileLocationQuestions.isMobileAddressComplete = jest.fn(); - wrapper.vm.$refs.mobileLocationQuestions.resetAlerts = jest.fn(); - wrapper.vm.$refs.mobileLocationQuestions.openModal = jest.fn(); return { wrapper, apiPromise }; } diff --git a/src/layouts/service-location/service-location.vue b/src/layouts/service-location/service-location.vue index 6893ad9b4..ccb2c7866 100644 --- a/src/layouts/service-location/service-location.vue +++ b/src/layouts/service-location/service-location.vue @@ -89,24 +89,14 @@
- - +
+ +
{ - if (this.isServiceableMobile) { - this.shopProviderData = result.data; - this.selectedProvider = new Provider( - this.shopProviderData.mobileProviderNumber - ); - } - }); - } - if (this.isServiceableMobile) { - this.setMobileLocation(newValue); - this.setMobileLocationInValid(false); - } else { - await getZipCodeData(newZipCode).then((zipCodeData) => { - this.serviceZipCodeQuestion = { - state: zipCodeData.state, - zipCode: newZipCode, - zipCodeCtu: zipCodeData.zipCodeCtu, - }; - }); - } - }, getCarIdfromStore() { return store.getters.vehicle.carId; }, @@ -590,15 +556,6 @@ export default { this.isRecalibrationServiceableMobile = serviceabilityDetails.isRecalibrationServiceableMobile; }, - setMobileLocation(mobileLocation) { - this.streetAddress = mobileLocation.addressQuestions.streetAddress; - this.apartmentNumberOrBusinessName = - mobileLocation.addressQuestions.apartmentNumberOrBusinessName; - this.city = mobileLocation.addressQuestions.city; - this.state = mobileLocation.addressQuestions.state; - this.zipCode = mobileLocation.addressQuestions.zipCode; - this.isVehicleProtected = mobileLocation.isVehicleProtected; - }, async reloadShopData(zipCode) { await this.$refs.shopQuestion.reloadShopData(zipCode); }, @@ -755,13 +712,49 @@ export default { this.updateAndSaveSupportingItems(); this.updateAndSaveIsMSRFeeApplicable(); - this.$router.navigateWithSaving( - this.navigationScenarios.CLICKED_FORWARD, - this.pageName + + //mocked schedule and should be clear when implement schedule page. + this.dispatchStoreAction( + this.storeActions.SAVE_SCHEDULE, + this.getTimeSlotInfo(), + false ); + + if (this.selectedAppointmentType == AppointmentTypeStrings.MOBILE) { + //navigate to mobile details page + this.$router.navigateWithSaving( + this.navigationScenarios.CLICKED_FORWARD_WITH_MOBILE_SERVICE, + this.pageName + ); + } else { + this.$router.navigateWithSaving( + this.navigationScenarios.CLICKED_FORWARD, + this.pageName + ); + } }, - setMobileLocationInValid(isMobileLocationInValid) { - this.isMobileAddressValid = !isMobileLocationInValid; + getTimeSlotInfo() { + // Get the current date + const currentDate = new Date(); + + // Add 10 days to the current date + currentDate.setDate(currentDate.getDate() + 10); + + // Format the date as yyyy-mm-dd + const year = currentDate.getFullYear(); + const month = String(currentDate.getMonth() + 1).padStart(2, "0"); + const day = String(currentDate.getDate()).padStart(2, "0"); + + const formattedDate = `${year}-${month}-${day}`; + + return { + date: formattedDate, + endTime: "12:00", + jobMaxMinutes: "180", + jobMinMinutes: "120", + routeCode: "3357I-03357-M-I*20988*AM", + startTime: "08:00", + }; }, }, watch: { @@ -787,16 +780,6 @@ export default { this.selectedProvider = new Provider( this.shopProviderData.mobileProviderNumber ); - if (this.zipCode == this.getServiceZipCodeFromStore()) { - //restore mobile address from store in case user make changes to address but not commit it. - this.streetAddress = this.getServiceAddressFromStore(); - this.apartmentNumberOrBusinessName = this.getServiceAddress2FromStore(); - this.city = this.getServiceCityFromStore(); - this.isVehicleProtected = this.getIsVehicleProtectedFromStore(); - } else { - this.resetMobileLocation(); - } - this.$refs.mobileLocationQuestions.resetAlerts(); } else { this.selectedProvider = new Provider(); } @@ -807,7 +790,6 @@ export default { alert, serviceZipModalQuestion, appointmentTypeQuestion, - mobileLocationModalQuestions, funnelHeader, navbar, funnelSubHeader, @@ -815,6 +797,7 @@ export default { loadingModal, contentGroupModal, shopQuestion, + textBlock, }, }; diff --git a/src/mixins/analytics-mixin.js b/src/mixins/analytics-mixin.js index 298a3af8f..90513e764 100644 --- a/src/mixins/analytics-mixin.js +++ b/src/mixins/analytics-mixin.js @@ -12,7 +12,8 @@ import { setSessionKeyIfUnset, } from "@/helpers/heritage-integration/cookie-helper"; import { queryStrings } from "@/constants/query-strings"; -import { experimentSettings } from "@/constants/experiments"; +import { experimentSettings, experimentUniverses } from "@/constants/experiments"; +import experimentMixin from "@/mixins/experiment-mixin"; import { analyticsPageEvents, GaCategories, @@ -128,6 +129,60 @@ export default { } }, + async logDigitalConsumer() { + const currentPageName = getPageNameFromRouter(); + const universes = store.getters.applicationUser.experiments; + console.log(JSON.stringify(universes)); + + let hasDynamoLogging = experimentMixin.methods.hasSettingEqualTo( + experimentSettings.DYNAMO_LOGGING, + "true", + universes + ); + if (!hasDynamoLogging) { + return; + } + + const variationNames = universes + .filter((item) => item.universeName === experimentUniverses.CONCEPT_FUNNEL) + .map((item) => item.variationName) + .filter(Boolean); // removes undefined/null + + const conceptVariation = variationNames.length > 0 ? variationNames[0] : ""; + + const submittedOrder = baseMixin.methods.getSubmittedOrder(); + const hasSubmittedOrder = baseMixin.methods.hasSubmittedOrder(); + const hasSubmittedOrderAtConfirmationPage = + hasSubmittedOrder && currentPageName?.toLowerCase() == routeData.CONFIRMATION.name; + + const refSequenceNum = hasSubmittedOrderAtConfirmationPage + ? submittedOrder.referralSequenceNumber + : store.getters.order.referralSequenceNumber; + + var payload = { + actionName: `Browser page:${currentPageName}`, + referralSequenceNumber: hasSubmittedOrderAtConfirmationPage + ? submittedOrder.referralSequenceNumber + : store.getters.order.referralSequenceNumber, + referralNumber: hasSubmittedOrderAtConfirmationPage + ? submittedOrder.referralNumber + : store.getters.order.referralNumber, + workOrderId: hasSubmittedOrderAtConfirmationPage + ? submittedOrder.workOrderId + : store.getters.order.workOrderId, + workOrderNumber: hasSubmittedOrderAtConfirmationPage + ? submittedOrder.workOrderNumber + : store.getters.order.workOrderNumber, + conceptVariation: conceptVariation, + }; + + await baseMixin.methods.dispatchStoreAction( + storeActions.LOG_DIGITALCONSUMER, + payload, + false + ); + }, + async pushEventForChatsToGA(category, action, label, pushToLogApp = false) { const currentPageName = getPageNameFromRouter(); const value = `2.0_${currentPageName}`; diff --git a/src/mixins/base-mixin.js b/src/mixins/base-mixin.js index ecce2632d..4046fa477 100644 --- a/src/mixins/base-mixin.js +++ b/src/mixins/base-mixin.js @@ -197,6 +197,18 @@ export default { return [parseInt(v[1], 10), parseInt(v[2], 10), parseInt(v[3] || 0, 10)]; } }, + isMobileDevice() { + const userAgent = navigator.userAgent; + return ( + userAgent.includes("Android") || + userAgent.includes("Mobile") || + userAgent.includes("iPod") || + userAgent.includes("iPhone") || + userAgent.includes("IEMobile") || + userAgent.includes("BlackBerry") || + userAgent.includes("webOS") + ); + }, getSubmittedOrder() { return JSON.parse( window.sessionStorage.getItem(sessionStorageKeyConstants.SUBMITTED_STATE) diff --git a/src/router/constants/navigation-scenarios.js b/src/router/constants/navigation-scenarios.js index a6e117948..b660afca3 100644 --- a/src/router/constants/navigation-scenarios.js +++ b/src/router/constants/navigation-scenarios.js @@ -49,6 +49,12 @@ const navigationScenarios = { // Insurance-Company CLICKED_PAY_ON_MY_OWN: "CLICKED_PAY_ON_MY_OWN", + //Service-Location + CLICKED_FORWARD_WITH_MOBILE_SERVICE: "CLICKED_FORWARD_WITH_MOBILE_SERVICE", + + //Customer-details + CLICKED_BACK_WITH_MOBILE_SERVICE: "CLICKED_BACK_WITH_MOBILE_SERVICE", + // Schedule CLICKED_CHANGE_LOCATION: "CLICKED_CHANGE_LOCATION", diff --git a/src/router/constants/routes.js b/src/router/constants/routes.js index d03954867..7e9a3a793 100644 --- a/src/router/constants/routes.js +++ b/src/router/constants/routes.js @@ -68,6 +68,10 @@ export const routeData = { name: "schedule", path: "/schedule", }, + MOBILE_DETAILS: { + name: "mobile-details", + path: "/mobile-details", + }, CUSTOMER_DETAILS: { name: "customer-details", path: "/customer-details", diff --git a/src/router/constants/routing-table.js b/src/router/constants/routing-table.js index 4b7286741..4a07bcfaf 100644 --- a/src/router/constants/routing-table.js +++ b/src/router/constants/routing-table.js @@ -460,6 +460,10 @@ const routingTable = function () { scenario: navigationScenarios.CLICKED_FORWARD, destinationPageData: routeData.SCHEDULE, }, + { + scenario: navigationScenarios.CLICKED_FORWARD_WITH_MOBILE_SERVICE, + destinationPageData: routeData.MOBILE_DETAILS, + }, ], }, { @@ -479,6 +483,19 @@ const routingTable = function () { }, ], }, + { + pageName: routeData.MOBILE_DETAILS.name, + maps: [ + { + scenario: navigationScenarios.CLICKED_BACK, + destinationPageData: routeData.SERVICE_LOCATION, + }, + { + scenario: navigationScenarios.CLICKED_FORWARD, + destinationPageData: routeData.CUSTOMER_DETAILS, + }, + ], + }, { pageName: routeData.CUSTOMER_DETAILS.name, maps: [ @@ -486,6 +503,10 @@ const routingTable = function () { scenario: navigationScenarios.CLICKED_BACK, destinationPageData: routeData.SCHEDULE, }, + { + scenario: navigationScenarios.CLICKED_BACK_WITH_MOBILE_SERVICE, + destinationPageData: routeData.MOBILE_DETAILS, + }, { scenario: navigationScenarios.CLICKED_FORWARD, destinationPageData: routeData.PAYMENT_METHOD, diff --git a/src/router/methods/after-each.js b/src/router/methods/after-each.js index ef3dc0d93..27cecffde 100644 --- a/src/router/methods/after-each.js +++ b/src/router/methods/after-each.js @@ -1,6 +1,9 @@ import analyticsMixin from "@/mixins/analytics-mixin"; export async function afterEach(to, from) { + // digital consumer logging + analyticsMixin.methods.logDigitalConsumer(); + // Push page view to GA analyticsMixin.methods.pushPageViewToGA(); diff --git a/src/router/methods/routes.js b/src/router/methods/routes.js index c029ce2cb..01f4f8a58 100644 --- a/src/router/methods/routes.js +++ b/src/router/methods/routes.js @@ -37,6 +37,7 @@ export const routes = [ createRoute(routeData.PAYMENT_PIA_RETURN), createRoute(routeData.CONFIRMATION), createRoute(routeData.RETURN_USER), + createRoute(routeData.MOBILE_DETAILS), // Virtual pages (resolve to a non-virtual page.) createVirtualRoute(routeData.LANDING, landingBeforeEnter), diff --git a/src/store/index.js b/src/store/index.js index 073a50e23..83d8d99af 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -11,7 +11,11 @@ import { experimentTriggers } from "@/constants/experiments"; import { damageLocationsSelected } from "@/constants/damage-locations-selected"; import { singleWindshieldCarIds } from "@/constants/single-windshield-carids"; import { routeData } from "@/router/constants/routes"; -import { deleteFunnelCookie } from "@/helpers/heritage-integration/cookie-helper.js"; +import { + deleteFunnelCookie, + getSessionIdValue, + getDeviceIdValue, +} from "@/helpers/heritage-integration/cookie-helper.js"; import { deepEqual } from "@/helpers/object-helper"; import baseMixin from "@/mixins/base-mixin.js"; import { @@ -421,6 +425,12 @@ export const mutations = { }, }; }, + updateMobileDetails(state, mobileDetails) { + state.order.serviceLocation.address = mobileDetails.address; + state.order.serviceLocation.address2 = mobileDetails.address2; + state.order.serviceLocation.city = mobileDetails.city; + state.order.serviceLocation.isVehicleProtected = mobileDetails.isVehicleProtected; + }, updateServiceLocationTechNotes(state, techNotes) { state.order.serviceLocation.techNotes = techNotes; }, @@ -1551,6 +1561,36 @@ export const actions = { }); }, + logDigitalConsumer( + context, + { + actionName, + referralSequenceNumber, + referralNumber, + workOrderId, + workOrderNumber, + conceptVariation, + } + ) { + var payload = { + sessionId: getSessionIdValue(), + deviceId: getDeviceIdValue(), + actionName: actionName ?? "", + referralSequenceNumber: referralSequenceNumber ?? "", + referralNumber: referralNumber ?? "", + applicationName: baseMixin.methods.isMobileDevice() ? "2.0 Mobile" : "2.0", + workOrderId: workOrderId ?? "", + workOrderNumber: workOrderNumber ?? "", + conceptVariation: conceptVariation, + }; + + return globalMethods.callHttpClient({ + method: endpoints.LogDigitalConsumer.method, + endpoint: endpoints.LogDigitalConsumer.url, + payload: payload, + }); + }, + // Misc Actions setReferralInformation(context, { referralNumber, referralDate, referralCorrelationId, eon }) { context.commit(storeMutations.UPDATE_REFERRAL_NUMBER, referralNumber); @@ -3204,6 +3244,9 @@ export const actions = { context.commit(storeMutations.UPDATE_SERVICE_LOCATION, serviceLocationInfo); }, + saveMobileDetails(context, mobileDetails) { + context.commit(storeMutations.UPDATE_MOBILE_DETAILS, mobileDetails); + }, saveServiceLocationTechNotes(context, techNotesInfo) { context.commit(storeMutations.UPDATE_SERVICE_LOCATION_TECH_NOTES, techNotesInfo); diff --git a/src/styles/ux-variables.scss b/src/styles/ux-variables.scss index 01b122b31..69ba0d7ac 100644 --- a/src/styles/ux-variables.scss +++ b/src/styles/ux-variables.scss @@ -121,8 +121,8 @@ $body-color: $gray-600; //Fonts $font-family-sans-serif: UrbanistRegular, Arial, Helvetica, sans-serif; -$font-family-monospace: - UrbanistRegular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; +$font-family-monospace: UrbanistRegular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", + monospace; // stylelint-enable value-keyword-case $font-family-base: $font-family-sans-serif; $font-family-code: $font-family-monospace;