Merge branch 'release/2026.08.13' into feature/CASH-1524

This commit is contained in:
Chris 2026-08-03 13:28:51 -04:00 committed by GitHub
commit 4ee0c0a2c1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 171 additions and 1 deletions

View file

@ -3,6 +3,7 @@ import scheduling from "./scheduling";
import { getMountOptions } from "@/helpers/unit-test-helper.js"; import { getMountOptions } from "@/helpers/unit-test-helper.js";
import { settleAllPromises } from "@/helpers/layout-helper"; import { settleAllPromises } from "@/helpers/layout-helper";
import { AppointmentTypeStrings } from "@/constants/schedule-constants"; import { AppointmentTypeStrings } from "@/constants/schedule-constants";
import { GaActions, GaCategories, GaLabels } from "@/constants/analytics";
import store from "@/store"; import store from "@/store";
jest.mock("@/store", () => ({ jest.mock("@/store", () => ({
@ -28,6 +29,7 @@ jest.mock("@/store", () => ({
damage: { isRepair: false }, damage: { isRepair: false },
lineItems: { glassParts: [] }, lineItems: { glassParts: [] },
policy: { isItac: false, isNoComp: false }, policy: { isItac: false, isNoComp: false },
isRecalAcknowledgedForScheduling: "",
}, },
lineItems: { supportingItems: [] }, lineItems: { supportingItems: [] },
applicationUser: { applicationUser: {
@ -84,6 +86,7 @@ function setupMocks({ isAppleBrowser = false } = {}) {
CLICKED_FORWARD: "clickedForward", CLICKED_FORWARD: "clickedForward",
CLICKED_FORWARD_WITH_MOBILE_SERVICE: "clickedForwardWithMobileService", CLICKED_FORWARD_WITH_MOBILE_SERVICE: "clickedForwardWithMobileService",
CLICKED_BACK: "clickedBack", CLICKED_BACK: "clickedBack",
ZIP_CODE_CHANGED_RECAL_ACK: "ZIP_CODE_CHANGED_RECAL_ACK",
}; };
}, },
pageName() { pageName() {
@ -283,6 +286,60 @@ describe("scheduling.vue", () => {
expect(wrapper.vm.$refs.schedulingZipSearch.focusZipInput).toHaveBeenCalled(); expect(wrapper.vm.$refs.schedulingZipSearch.focusZipInput).toHaveBeenCalled();
wrapper.unmount(); wrapper.unmount();
}); });
test.each([
["yes", "12345", GaLabels.YES],
["no", null, GaLabels.NO],
])(
"pushes mobile_available %s when mobileProviderNumber is %s",
async (_label, mobileProviderNumber, expectedGaLabel) => {
settleAllPromises.mockResolvedValueOnce({});
const { wrapper } = setupMocks();
wrapper.vm.pushEventToGA.mockClear();
await wrapper.vm.loadSchedulingData("43235", {
providersResult: { shopProviders: [], mobileProviderNumber },
});
expect(wrapper.vm.pushEventToGA).toHaveBeenCalledWith(
GaCategories.APPOINTMENT,
GaActions.MOBILE_AVAILABLE,
expectedGaLabel,
true
);
wrapper.unmount();
}
);
test("navigates to service-zip when zip changes and recal acknowledgement applies", async () => {
store.getters.order.lineItems = {
glassParts: [
{
partType: "WINDSHIELD",
requiresRecalibration: true,
canSafeliteRecalibrate: false,
},
],
};
try {
const { wrapper } = setupMocks();
await wrapper.setData({
isRecalAcknowledgedForScheduling: "",
isLoadingDates: false,
});
await wrapper.vm.onZipSearched({ zipCode: "44101", billToAccountNumber: "87291" });
expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalledWith(
"ZIP_CODE_CHANGED_RECAL_ACK",
"scheduling"
);
expect(wrapper.vm.isLoadingDates).toBe(false);
wrapper.unmount();
} finally {
store.getters.order.lineItems = { glassParts: [] };
}
});
}); });
describe("forwardButtonAction", () => { describe("forwardButtonAction", () => {
@ -413,6 +470,56 @@ describe("scheduling.vue", () => {
}); });
}); });
describe("forwardButtonAction (recal acknowledgement)", () => {
test("opens recal modal and does not navigate when required", async () => {
store.getters.order.lineItems = {
glassParts: [
{
partType: "WINDSHIELD",
requiresRecalibration: true,
canSafeliteRecalibrate: false,
},
],
};
const { wrapper } = setupMocks();
wrapper.vm.$refs.navbar.removeLoader = jest.fn();
wrapper.vm.$refs.recalAckModal.openModal = jest.fn();
await wrapper.setData({ isRecalAcknowledgedForScheduling: "" });
await wrapper.vm.forwardButtonAction();
expect(wrapper.vm.$refs.recalAckModal.openModal).toHaveBeenCalled();
expect(wrapper.vm.$router.navigateWithSaving).not.toHaveBeenCalled();
wrapper.unmount();
store.getters.order.lineItems = { glassParts: [] };
});
test("navigates forward after recal acknowledgement is saved", async () => {
const { wrapper } = setupMocks();
await wrapper.setData({ isRecalAcknowledgedForScheduling: "Yes" });
await wrapper.vm.forwardButtonAction();
expect(store.dispatch).toHaveBeenCalledWith(
"saveIsRecalAcknowledgedForScheduling",
"Yes"
);
expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalled();
wrapper.unmount();
});
test("onRecalAcknowledged sets acknowledgement and continues forward", async () => {
const { wrapper } = setupMocks();
wrapper.vm.forwardButtonAction = jest.fn();
wrapper.vm.onRecalAcknowledged("Yes");
expect(wrapper.vm.isRecalAcknowledgedForScheduling).toBe("Yes");
expect(wrapper.vm.forwardButtonAction).toHaveBeenCalled();
wrapper.unmount();
});
});
describe("in-shop Maps link", () => { describe("in-shop Maps link", () => {
const address = { const address = {
city: "Lewis Center", city: "Lewis Center",

View file

@ -84,6 +84,12 @@
</div> </div>
</div> </div>
</div> </div>
<recalAckModal
modalWidgetName="RecalAckModalWidget"
agreementWidgetName="RecalAgreementQuestionWidget"
groupName="recalAckGroup"
@recalAcknowledged="onRecalAcknowledged"
ref="recalAckModal" />
</Form> </Form>
</template> </template>
@ -98,15 +104,21 @@ import inshopSchedulingCard from "@/layouts/scheduling/inshop-scheduling-card/in
import interceptOverlay from "@/ux-components/intercept-overlay/intercept-overlay"; import interceptOverlay from "@/ux-components/intercept-overlay/intercept-overlay";
import waitlistQuestion from "@/layouts/scheduling/waitlist-question/waitlist-question"; import waitlistQuestion from "@/layouts/scheduling/waitlist-question/waitlist-question";
import schedulingZipSearch from "@/layouts/scheduling/scheduling-zip-search/scheduling-zip-search"; import schedulingZipSearch from "@/layouts/scheduling/scheduling-zip-search/scheduling-zip-search";
import recalAckModal from "@/layouts/schedule/recal-ack-modal/recal-ack-modal.vue";
import textLink from "@/ux-components/text-link/text-link"; import textLink from "@/ux-components/text-link/text-link";
import store from "@/store"; import store from "@/store";
import { storeActions } from "@/constants/store-actions"; import { storeActions } from "@/constants/store-actions";
import { AppointmentTypeStrings, PREMIUM_FEE_PART_TYPE } from "@/constants/schedule-constants"; import {
AppointmentTypeStrings,
PREMIUM_FEE_PART_TYPE,
RECAL_ACK_YES,
} from "@/constants/schedule-constants";
import { isDropOffRouteCode } from "@/layouts/schedule/helpers/schedule-helper"; import { isDropOffRouteCode } from "@/layouts/schedule/helpers/schedule-helper";
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper"; import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
import { settleAllPromises } from "@/helpers/layout-helper"; import { settleAllPromises } from "@/helpers/layout-helper";
import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper"; import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
import { getDisplayTextForDurationLength } from "@/helpers/duration-length-helper"; import { getDisplayTextForDurationLength } from "@/helpers/duration-length-helper";
import { partTypeStrings } from "@/constants/part-type-strings";
import { import {
flushPagePrereqsLogs, flushPagePrereqsLogs,
hasServiceZipInfo, hasServiceZipInfo,
@ -394,6 +406,8 @@ export default {
zipSearchCode: store.getters.order.serviceLocation.zipCode ?? "", zipSearchCode: store.getters.order.serviceLocation.zipCode ?? "",
datePickerKey: 0, datePickerKey: 0,
billToAccountNumber: store.getters.order.payment?.billToAccountNumber ?? null, billToAccountNumber: store.getters.order.payment?.billToAccountNumber ?? null,
isRecalAcknowledgedForScheduling:
store.getters.order.isRecalAcknowledgedForScheduling ?? "",
}; };
}, },
methods: { methods: {
@ -425,6 +439,17 @@ export default {
? { providerNumber: mobileProviderNumber, timeSlots: null } ? { providerNumber: mobileProviderNumber, timeSlots: null }
: null; : null;
var gaLabel = this.GaLabels.NO;
if (this.mobileProviderAndTimeSlot) {
gaLabel = this.GaLabels.YES;
}
this.pushEventToGA(
this.GaCategories.APPOINTMENT,
this.GaActions.MOBILE_AVAILABLE,
gaLabel,
true
);
const startDate = toDateString(0); const startDate = toDateString(0);
const endDate = toDateString(SCHEDULE_FETCH_DAYS - 1); const endDate = toDateString(SCHEDULE_FETCH_DAYS - 1);
const providerNumbers = providers.map((provider) => provider.providerNumber); const providerNumbers = providers.map((provider) => provider.providerNumber);
@ -503,6 +528,15 @@ export default {
this.$refs.schedulingZipSearch?.focusZipInput(); this.$refs.schedulingZipSearch?.focusZipInput();
}, },
async onZipSearched({ zipCode, billToAccountNumber }) { async onZipSearched({ zipCode, billToAccountNumber }) {
// When recal ack applies, changing zip must re-run parts selection via service-zip.
if (this.shouldShowRecalAckModal()) {
this.$router.navigateWithSaving(
this.navigationScenarios.ZIP_CODE_CHANGED_RECAL_ACK,
this.pageName
);
return;
}
this.billToAccountNumber = billToAccountNumber; this.billToAccountNumber = billToAccountNumber;
this.isLoadingDates = true; this.isLoadingDates = true;
try { try {
@ -731,9 +765,20 @@ export default {
); );
}, },
async forwardButtonAction() { async forwardButtonAction() {
if (this.shouldShowRecalAckModal()) {
this.$refs.navbar.removeLoader();
this.$refs.recalAckModal.openModal();
return;
}
const selectedScheduling = this.selectedScheduling; const selectedScheduling = this.selectedScheduling;
const selectedDate = this.selectedDate; const selectedDate = this.selectedDate;
await store.dispatch(
storeActions.SAVE_IS_RECAL_ACKNOWLEDGED_FOR_SCHEDULING,
this.isRecalAcknowledgedForScheduling
);
const appointmentType = this.getInShopOrDropOffApptType(selectedScheduling); const appointmentType = this.getInShopOrDropOffApptType(selectedScheduling);
const selectedProvider = this.getSelectedProvider(appointmentType, selectedScheduling); const selectedProvider = this.getSelectedProvider(appointmentType, selectedScheduling);
const serviceLocation = store.getters.order.serviceLocation; const serviceLocation = store.getters.order.serviceLocation;
@ -826,6 +871,23 @@ export default {
); );
} }
}, },
shouldShowRecalAckModal() {
if (this.isRecalAcknowledgedForScheduling === RECAL_ACK_YES) {
return false;
}
const glassParts = store.getters.order.lineItems?.glassParts ?? [];
return glassParts.some(
(part) =>
part.partType === partTypeStrings.WINDSHIELD &&
part.requiresRecalibration === true &&
part.canSafeliteRecalibrate === false
);
},
onRecalAcknowledged(isAcknowledged) {
this.isRecalAcknowledgedForScheduling = isAcknowledged;
this.forwardButtonAction();
},
}, },
components: { components: {
funnelHeader, funnelHeader,
@ -838,6 +900,7 @@ export default {
interceptOverlay, interceptOverlay,
waitlistQuestion, waitlistQuestion,
schedulingZipSearch, schedulingZipSearch,
recalAckModal,
textLink, textLink,
}, },
}; };