diff --git a/src/App.vue b/src/App.vue
index 422ae2893..f4c5754aa 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -36,6 +36,9 @@ export default {
loadingModal,
funnelFooter,
},
+ mounted() {
+ showFmgLoadingModal(true);
+ },
};
// Add a global Javascript exception handler for logging exceptions, this handler will log when there is an uncaught exception
diff --git a/src/constants/analytics.js b/src/constants/analytics.js
index f6534cda4..fe43be06d 100644
--- a/src/constants/analytics.js
+++ b/src/constants/analytics.js
@@ -23,7 +23,6 @@ const GaActions = {
SUBMITTED: "Submitted",
DISPLAYED: "Displayed",
CASH_QUOTE_DISPLAYED: "Cash quote displayed",
- SERVICE_PACKAGE_PRICE: "Service package price",
MOBILE_AVAILABLE: "mobile_available",
SHOPS_FIRST_DISPLAYED: "shops_first_displayed",
MORE_LOCATIONS_CLICKED: "more_locations_clicked",
diff --git a/src/constants/application-config.js b/src/constants/application-config.js
index 14f195cfc..788de3fd7 100644
--- a/src/constants/application-config.js
+++ b/src/constants/application-config.js
@@ -6,6 +6,7 @@ const applicationConfig = {
COOKIE_PATH: "/",
CURRENT_ENVIRONMENT: process.env.VUE_APP_CURRENT_ENVIRONMENT, // "Localhost", "Dev", "QA", and "Prod"
HERITAGE_FUNNEL: process.env.VUE_APP_HERITAGE_FUNNEL,
+ SAFELITE_HOP: process.env.VUE_APP_SAFELITE_HOP,
APPLICATION_NAME: "FixMyGlass",
ANALYTICS_APPLICATION_NAME: "FixMyGlassNextGen",
APPLICATION_ABBREVIATION: "fmg",
@@ -15,21 +16,12 @@ const applicationConfig = {
CASH_DEFAULT_BILL_TO_ACCOUNT_NUMBER: "87291",
MY_ACCOUNT: process.env.VUE_APP_MY_ACCOUNT,
PIA_RESPONSE_URL:
- location.protocol +
- "//" +
- location.host +
- "/fmg/?fmgPage=payment-pia-return&src=concept-funnel",
+ location.protocol + "//" + location.host + "/fmg/payment-pia-return?src=concept-funnel",
PIA_CANCEL_URL:
- location.protocol +
- "//" +
- location.host +
- "/fmg/?fmgPage=payment-method&src=concept-funnel",
+ location.protocol + "//" + location.host + "/fmg/payment-method?src=concept-funnel",
PIA_ERROR_URL:
- location.protocol +
- "//" +
- location.host +
- "/fmg/?fmgPage=payment-pia-return&src=concept-funnel",
- CONFIRMATION_URL: location.protocol + "//" + location.host + "/fmg/?fmgPage=confirmation",
+ location.protocol + "//" + location.host + "/fmg/payment-pia-return?src=concept-funnel",
+ CONFIRMATION_URL: location.protocol + "//" + location.host + "/fmg/confirmation",
GOOGLE_CALENDAR: "https://www.google.com/calendar/render?action=TEMPLATE",
YAHOO_CALENDAR: "https://calendar.yahoo.com/?v=60",
OUTLOOK_CALENDAR:
@@ -38,7 +30,7 @@ const applicationConfig = {
AFFILIATE_COOKIE_CONTAINING_NAME: "_Track",
COOKIE_NAME_LENGTH_MAX_LIMIT: 44,
SESSION_TIMEOUT_MINUTES: process.env.VUE_APP_SESSION_TIMEOUT_MINUTES,
- RETURN_USER_PAGE: "/fmg/?fmgPage=return-user",
+ RETURN_USER_PAGE: "/fmg/return-user",
};
export { applicationConfig };
diff --git a/src/constants/store-actions.js b/src/constants/store-actions.js
index 7b76d4064..9f0965480 100644
--- a/src/constants/store-actions.js
+++ b/src/constants/store-actions.js
@@ -117,6 +117,7 @@ const storeActions = {
UPDATE_EXTERNAL_PARAMETER_MMS: "updateExternalParameterMMS",
SAVE_LOGGING_OPTION: "saveLoggingOption",
+ UPDATE_HAS_TRIGGERED_ERROR: "updateHasTriggeredError",
};
export { storeActions };
diff --git a/src/constants/store-mutations.js b/src/constants/store-mutations.js
index 2f53d8133..f7e8d9f4b 100644
--- a/src/constants/store-mutations.js
+++ b/src/constants/store-mutations.js
@@ -69,6 +69,7 @@ const storeMutations = {
UPDATE_SETTLED_TENDER_AMOUNT: "updateSettledTenderAmount",
UPDATE_IS_RECAL_ACK_OPT_IN: "updateIsRecalAckOptIn",
UPDATE_IS_MSR_FEE_APPLICABLE: "updateIsMSRFeeApplicable",
+ UPDATE_CASH_PRICE_SUBTOTAL: "updateCashPriceSubTotal",
// EVENT BUS MUTATIONS
ADD_EVENT_TO_BUS: "addEventToBus",
@@ -93,6 +94,7 @@ const storeMutations = {
UPDATE_SAVE_SESSION_PROMISE: "updateSaveSessionPromise",
UPDATE_LAST_PAGE_VISITED: "updateLastPageVisited",
UPDATE_LOGGING_OPTION: "updateLoggingOption",
+ UPDATE_HAS_TRIGGERED_ERROR: "updateHasTriggeredError",
// EXPERIMENT MUTATIONS
UPDATE_EXPERIMENTS: "updateExperiments",
diff --git a/src/digital-components/base-input-button/button-functionality-props.js b/src/digital-components/base-input-button/button-functionality-props.js
index fe2104457..73da5f25c 100644
--- a/src/digital-components/base-input-button/button-functionality-props.js
+++ b/src/digital-components/base-input-button/button-functionality-props.js
@@ -35,4 +35,9 @@ export const inputButtonProps = {
default: false,
},
suppressError: Boolean,
+ labelBold: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
};
diff --git a/src/digital-components/button-question/button-question.spec.js b/src/digital-components/button-question/button-question.spec.js
index 3b7a4e80e..73c048936 100644
--- a/src/digital-components/button-question/button-question.spec.js
+++ b/src/digital-components/button-question/button-question.spec.js
@@ -1143,7 +1143,7 @@ describe("buttonQuestion.vue", () => {
});
function setupMocks(mountOptionsMockData = {}) {
- const defaultMountOptions = { route: { query: { fmgPage: "page-name" } } };
+ const defaultMountOptions = { route: { name: "page-name" } };
const baseMountOptions = getMountOptions(
Object.assign(defaultMountOptions, mountOptionsMockData)
);
diff --git a/src/digital-components/button-question/button-question.vue b/src/digital-components/button-question/button-question.vue
index 36f036379..2f1273511 100644
--- a/src/digital-components/button-question/button-question.vue
+++ b/src/digital-components/button-question/button-question.vue
@@ -9,7 +9,9 @@ https://safelite.atlassian.net/wiki/spaces/DC/pages/76644418/Button+Question+Com
v-if="questionText && answers && answers.length > 0"
class="question-text d-flex"
:class="{ 'small-question-text': isSmallQuestionText }">
- {{ questionText }}
+
+ {{ questionText }}
+
@@ -57,6 +59,7 @@ https://safelite.atlassian.net/wiki/spaces/DC/pages/76644418/Button+Question+Com
:setLastValuePushedToGa="setLastValuePushedToGa"
:isInsuranceSelected="isInsuranceSelected"
:suppressError="suppressError"
+ :labelBold="labelBold"
@buttonEvent="handleButtonEvent"
v-model="selectedValues" />
@@ -141,6 +144,11 @@ export default {
default: false,
},
isInsuranceSelected: Boolean,
+ labelBold: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
setup(props) {
const propsClone = Object.assign({}, props);
@@ -360,6 +368,9 @@ export default {
& > span {
text-align: center;
}
+ .span-bold {
+ font-weight: 600;
+ }
}
.small-question-text {
&.question-text {
diff --git a/src/digital-components/date-picker/date-picker.vue b/src/digital-components/date-picker/date-picker.vue
index 1a7df91e0..d81474ba2 100644
--- a/src/digital-components/date-picker/date-picker.vue
+++ b/src/digital-components/date-picker/date-picker.vue
@@ -688,17 +688,18 @@ export default {
if (!this.months) return;
this.isLoading = true;
let monthToShow;
- let monthStartDateNum = 0;
+ let monthStartDate;
if (this.hideSomeDaysForInitialView) {
monthToShow = this.months.find(
({ isMonthThatHidesSomeDaysForInitialView }) =>
isMonthThatHidesSomeDaysForInitialView
);
+
// find the first day-hidden to become the next api call start date
- monthStartDateNum =
- monthToShow.dates.find(({ dayClasses }) => dayClasses.includes("day-hidden"))
- .dateNum - 1;
+ monthStartDate = monthToShow.dates.find(({ dayClasses }) =>
+ dayClasses.includes("day-hidden")
+ );
} else {
if (this.calendarViewDirection === "future") {
monthToShow = this.months.find((month) =>
@@ -713,9 +714,13 @@ export default {
}
}
if (monthToShow) {
+ const monthStartDateString = monthStartDate
+ ? monthStartDate.inputValue
+ : monthToShow.dates[0].inputValue;
+
// make new API call with this month's start and end dates
const moreSelectableDatesData = await this.updateSelectableDates(
- monthToShow.dates[monthStartDateNum].inputValue,
+ monthStartDateString,
monthToShow.dates[monthToShow.dates.length - 1].inputValue
);
this.isLoading = false;
@@ -1042,6 +1047,9 @@ export default {
min-width: 3rem;
width: 3rem;
}
+ &:hover {
+ border: 2px solid $blue;
+ }
}
}
&.unavailable-day {
@@ -1083,7 +1091,6 @@ export default {
}
input[type="radio"]:checked + label:after {
background-color: $black;
- top: 2rem;
}
}
}
diff --git a/src/digital-components/dropdown-question/dropdown-question.vue b/src/digital-components/dropdown-question/dropdown-question.vue
index 8b3cf023f..7b52745fa 100644
--- a/src/digital-components/dropdown-question/dropdown-question.vue
+++ b/src/digital-components/dropdown-question/dropdown-question.vue
@@ -6,6 +6,7 @@
:for="dropdownId"
:aria-label="questionText"
class="form-label"
+ :class="[labelBold ? 'label-bold' : '']"
v-html="questionText">
@@ -27,7 +28,8 @@
customInputId="apartmentNumberOrBusinessName"
cmsWidgetName="ApartmentNumberOrBusinessNameQuestionWidget"
v-model="addressModel.apartmentNumberOrBusinessName"
- ref="apartmentNumberOrBusinessName" />
+ ref="apartmentNumberOrBusinessName"
+ :labelBold="labelBold" />
@@ -40,7 +42,8 @@
v-model="addressModel.city"
ref="city"
validationRules="city-required"
- autocomplete="address-level2" />
+ autocomplete="address-level2"
+ :labelBold="labelBold" />
@@ -54,7 +57,8 @@
ref="state"
:options="stateOptions"
validationRules="state-required"
- autocomplete="address-level1" />
+ autocomplete="address-level1"
+ :labelBold="labelBold" />
+ autocomplete="postal-code"
+ :labelBold="labelBold" />
@@ -124,6 +129,11 @@ export default {
type: Boolean,
default: false,
},
+ labelBold: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
data() {
return {
diff --git a/src/fmg-components/funnel-header/funnel-header.vue b/src/fmg-components/funnel-header/funnel-header.vue
index ceb4d627d..33a398428 100644
--- a/src/fmg-components/funnel-header/funnel-header.vue
+++ b/src/fmg-components/funnel-header/funnel-header.vue
@@ -1,13 +1,21 @@
-
-
-
![Safelite logo]()
-
-
-
-
+
@@ -45,7 +53,7 @@ export default {
cmsWidgetName: String,
hideSalesforceWebchatLaunchButton: {
type: Boolean,
- default: false,
+ default: true,
},
},
computed: {
@@ -100,16 +108,23 @@ export default {
diff --git a/src/fmg-components/funnel-header/menu-modal/menu-modal.vue b/src/fmg-components/funnel-header/menu-modal/menu-modal.vue
index 4dc07f3a5..8f68ab6e9 100644
--- a/src/fmg-components/funnel-header/menu-modal/menu-modal.vue
+++ b/src/fmg-components/funnel-header/menu-modal/menu-modal.vue
@@ -11,21 +11,6 @@
-
-
@@ -34,6 +35,12 @@ export default {
isServiceableInshop: Boolean,
isServiceableDropoff: Boolean,
mobileFeeApplies: Boolean,
+ labelBold: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ zipCode: String,
},
computed: {
questionText() {
@@ -47,6 +54,7 @@ export default {
const shouldShowInshop = this.isServiceableInshop;
const shouldShowDropoff =
this.isServiceableDropoff && !this.$store.getters.damage.isRepair;
+ const zipCode = this.zipCode;
var answers = this.answersFromCms
? this.answersFromCms.filter((answer) => {
@@ -87,12 +95,15 @@ export default {
watch: {
answersToDisplay: {
handler(newValue) {
- // If there is only one option to display and that option is 'Mobile' then select it
- if (
- newValue.length == 1 &&
- newValue.findIndex((answer) => answer.Name == "Mobile") != -1
- ) {
- this.selectedValue = "Mobile";
+ // If there is only one option to display and that option is 'Mobile' or 'Inshop' then select it
+ if (newValue.length == 1) {
+ const name = newValue[0].Name;
+ if (
+ name === AppointmentTypeStrings.MOBILE ||
+ name === AppointmentTypeStrings.IN_SHOP
+ ) {
+ this.selectedValue = name;
+ }
}
},
immediate: true,
@@ -100,14 +111,14 @@ export default {
isMobileOnly: {
handler(newValue) {
if (newValue) {
- this.selectedValue = "Mobile";
+ this.selectedValue = AppointmentTypeStrings.MOBILE;
}
},
},
isInshopOnly: {
handler(newValue) {
if (newValue) {
- this.selectedValue = "Inshop";
+ this.selectedValue = AppointmentTypeStrings.IN_SHOP;
}
},
},
diff --git a/src/layouts/service-location/helpers/service-location-helper/service-location-helper.js b/src/layouts/service-location/helpers/service-location-helper/service-location-helper.js
index 106ed892c..a85b180f9 100644
--- a/src/layouts/service-location/helpers/service-location-helper/service-location-helper.js
+++ b/src/layouts/service-location/helpers/service-location-helper/service-location-helper.js
@@ -8,6 +8,7 @@ export async function getPricedMobileFeePart(serviceZipCode, pageNameToLog) {
}
const zipCodeData = await getZipCodeData(serviceZipCode);
+ const providerData = await getShopProviderData(serviceZipCode);
// Get the Mobile Fee Part
const mobileFeePart = await baseMixin.methods.dispatchStoreActionWithLogging(
@@ -15,6 +16,7 @@ export async function getPricedMobileFeePart(serviceZipCode, pageNameToLog) {
{
serviceZipCode: serviceZipCode,
serviceZipCodeCtu: zipCodeData.zipCodeCtu,
+ mobileProviderNumber: providerData.data.mobileProviderNumber,
},
pageNameToLog,
false
diff --git a/src/layouts/service-location/helpers/service-location-helper/service-location-helper.spec.js b/src/layouts/service-location/helpers/service-location-helper/service-location-helper.spec.js
index 5bb41c657..06f34f125 100644
--- a/src/layouts/service-location/helpers/service-location-helper/service-location-helper.spec.js
+++ b/src/layouts/service-location/helpers/service-location-helper/service-location-helper.spec.js
@@ -91,6 +91,7 @@ const mockStoreActionPriceOrderItemsAndSaveServerData =
storeActions.PRICE_ORDER_ITEMS_AND_SAVE_SERVER_DATA;
const mockStoreActionGetServiceabilityDetails = storeActions.GET_SERVICEABILITY_DETAILS;
const mockStoreActionGetShopTimeSlots = storeActions.GET_SHOP_TIME_SLOTS;
+const mockStoreActionGetProviders = storeActions.GET_PROVIDERS;
const mockGetShopTimeSlotsGoodAvailability = {
data: {
@@ -236,6 +237,15 @@ jest.mock("@/mixins/base-mixin.js", () => ({
return Promise.resolve(mockGetShopTimeSlotsLowAvailability);
}
+
+ if (actionName === mockStoreActionGetProviders) {
+ return Promise.resolve({
+ data: {
+ mobileProviderNumber: "001820",
+ shopProviders: [{}],
+ },
+ });
+ }
}),
},
}));
diff --git a/src/layouts/service-location/mobile-location-modal-questions/mobile-location-modal-questions.spec.js b/src/layouts/service-location/mobile-location-modal-questions/mobile-location-modal-questions.spec.js
index 3e2873fa1..49d233402 100644
--- a/src/layouts/service-location/mobile-location-modal-questions/mobile-location-modal-questions.spec.js
+++ b/src/layouts/service-location/mobile-location-modal-questions/mobile-location-modal-questions.spec.js
@@ -189,41 +189,7 @@ describe("mobile-location-modal-questions.vue", () => {
expect(wrapper.vm.internalModel.serviceZipCode).toEqual("55555");
});
- it("Should open the modal when the 'Enter your service address' link is clicked", async () => {
- // Arrange
- const mobileLocationQuestions = {
- addressQuestions: {
- streetAddress: "555 Some St",
- apartmentNumberOrBusinessName: "Apt 1",
- city: "Funkytown",
- state: "OH",
- zipCode: "55555",
- },
- isVehicleProtected: true,
- serviceZipCode: "55555",
- };
-
- const { wrapper } = setupMocks({
- mixins: [mockMixin],
- props: {
- modelValue: mobileLocationQuestions,
- },
- mountOptions: {
- attachTo: document.body,
- },
- });
-
- wrapper.vm.$refs.MobileLocationModalWidget.openModal = jest.fn();
- const mobileLocationLink = wrapper.findComponent({ ref: "mobileLocationLink" });
-
- // // Act
- await mobileLocationLink.trigger("click-event");
-
- // // Assert
- expect(wrapper.vm.$refs.MobileLocationModalWidget.openModal).toHaveBeenCalled();
- });
-
- it("Should emit setMobileLocationAndProceed on setMobileLocation for a valid address and vehicle protection answer", async () => {
+ it("Should emit setMobileLocation on setMobileLocation for a valid address and vehicle protection answer", async () => {
// Arrange
const mobileLocationQuestions = {
addressQuestions: {
@@ -258,7 +224,6 @@ describe("mobile-location-modal-questions.vue", () => {
},
attachTo: document.body,
});
- wrapper.vm.$refs.MobileLocationModalWidget.closeModal = jest.fn();
// Act
wrapper.vm.internalModel = newMobileLocationQuestions;
@@ -266,13 +231,13 @@ describe("mobile-location-modal-questions.vue", () => {
await wrapper.vm.setMobileLocation();
// Assert
- expect(wrapper.emitted()["setMobileLocationAndProceed"]).toBeTruthy();
- expect(wrapper.emitted()["setMobileLocationAndProceed"][0][0]).toStrictEqual(
+ expect(wrapper.emitted()["setMobileLocation"]).toBeTruthy();
+ expect(wrapper.emitted()["setMobileLocation"][0][0]).toStrictEqual(
newMobileLocationQuestions
);
});
- it("Should not emit setMobileLocationAndProceed on setMobileLocation for an invalid address zip code", async () => {
+ it("Should not emit setMobileLocation on setMobileLocation for an invalid address zip code", async () => {
// Arrange
const mobileLocationQuestions = {
addressQuestions: {
@@ -307,15 +272,13 @@ describe("mobile-location-modal-questions.vue", () => {
},
attachTo: document.body,
});
- wrapper.vm.$refs.MobileLocationModalWidget.closeModal = jest.fn();
- wrapper.vm.resetModalButtonStyle = jest.fn();
// Act
wrapper.vm.internalModel = newMobileLocationQuestions;
await wrapper.vm.setMobileLocation();
// Assert
- expect(wrapper.emitted("setMobileLocationAndProceed")).not.toBeTruthy();
+ expect(wrapper.emitted("setMobileLocation")).not.toBeTruthy();
});
it("Should display invalid zip alert for invalid address zip inputs", async () => {
diff --git a/src/layouts/service-location/mobile-location-modal-questions/mobile-location-modal-questions.vue b/src/layouts/service-location/mobile-location-modal-questions/mobile-location-modal-questions.vue
index ac597c72b..fc83f041a 100644
--- a/src/layouts/service-location/mobile-location-modal-questions/mobile-location-modal-questions.vue
+++ b/src/layouts/service-location/mobile-location-modal-questions/mobile-location-modal-questions.vue
@@ -1,52 +1,48 @@
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+ {{ errorMessage }}
+
+
+
-
-
- {{ errorMessage }}
-
-
-
-
-
-
+
+
+ preserveCityAndStateOnReset="true"
+ labelBold="true" />
+ cmsWidgetName="VehicleProtectedQuestionWidget"
+ labelBold="true" />
-
-
-
+
+
+
-