Merge pull request #1001 from Safelite/feature/CSR-1088
Feature/csr 1088
This commit is contained in:
commit
d9ea53f389
11 changed files with 122 additions and 93 deletions
|
|
@ -22,6 +22,7 @@
|
|||
aria-live="polite">
|
||||
<div class="col">
|
||||
<textboxQuestion
|
||||
customInputId="apartmentNumberOrBusinessName"
|
||||
cmsWidgetName="ApartmentNumberOrBusinessNameQuestionWidget"
|
||||
v-model="addressModel.apartmentNumberOrBusinessName"
|
||||
ref="apartmentNumberOrBusinessName" />
|
||||
|
|
@ -32,6 +33,7 @@
|
|||
<div class="row mb-4" v-show="showAddressFields" aria-live="polite">
|
||||
<div class="col">
|
||||
<textboxQuestion
|
||||
customInputId="city"
|
||||
cmsWidgetName="CityQuestionWidget"
|
||||
v-model="addressModel.city"
|
||||
ref="city"
|
||||
|
|
@ -43,14 +45,17 @@
|
|||
<div class="row mb-4" v-show="showAddressFields" aria-live="polite">
|
||||
<div class="col">
|
||||
<dropdownQuestion
|
||||
customInputId="state"
|
||||
cmsWidgetName="StateQuestionWidget"
|
||||
v-model="addressModel.state"
|
||||
ref="state"
|
||||
:options="stateOptions"
|
||||
initialValue="FL"
|
||||
validationRules="state-required" />
|
||||
</div>
|
||||
<div class="col">
|
||||
<textboxQuestion
|
||||
customInputId="zipCode"
|
||||
cmsWidgetName="ZipQuestionWidget"
|
||||
v-model="addressModel.zipCode"
|
||||
ref="zipCode"
|
||||
|
|
|
|||
|
|
@ -40,13 +40,6 @@
|
|||
"
|
||||
:data-bs-target="'#' + getRouterLinkRouteFromCopy(copy)"
|
||||
aria-label="Modal window" />
|
||||
<!-- <textLink
|
||||
linkType="text"
|
||||
:text="getRouterLinkDisplayTextFromCopy(copy)"
|
||||
href="#!"
|
||||
data-bs-toggle="modal"
|
||||
:data-bs-target="'#' + getRouterLinkRouteFromCopy(copy)"
|
||||
aria-label="Modal window" /> -->
|
||||
</span>
|
||||
</template>
|
||||
</li>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,13 @@
|
|||
import { storeActions } from "@/constants/store-actions";
|
||||
import baseMixin from "@/mixins/base-mixin.js";
|
||||
|
||||
export async function getPricedMobileFeePart(serviceZipCode) {
|
||||
if (!serviceZipCode) {
|
||||
export async function getPricedMobileFeePart(
|
||||
serviceZipCode,
|
||||
serviceType,
|
||||
parentAccountNumber,
|
||||
billToAccountNumber
|
||||
) {
|
||||
if (!serviceZipCode || !serviceType || !parentAccountNumber) {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
|
|
@ -11,7 +16,11 @@ export async function getPricedMobileFeePart(serviceZipCode) {
|
|||
// Get the Mobile Fee Part
|
||||
const mobileFeePart = await baseMixin.methods.dispatchStoreAction(
|
||||
storeActions.GET_MOBILE_FEE_PART,
|
||||
null,
|
||||
{
|
||||
serviceType: serviceType,
|
||||
parentAccountNumber: parentAccountNumber,
|
||||
billToAccountNumber: billToAccountNumber,
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
|
|
@ -21,7 +30,7 @@ export async function getPricedMobileFeePart(serviceZipCode) {
|
|||
{
|
||||
availableLineItems: [mobileFeePart.data],
|
||||
serviceZipCode: serviceZipCode,
|
||||
ctu: zipCodeData.zipCodeCtu,
|
||||
serviceZipCodeCtu: zipCodeData.zipCodeCtu,
|
||||
},
|
||||
false
|
||||
);
|
||||
|
|
@ -48,10 +48,18 @@ describe("service-location-helper.js", () => {
|
|||
it("Should return null if no service zip code is passed in", async () => {
|
||||
// Arrange
|
||||
const serviceZipCode = null;
|
||||
const serviceType = "Replace";
|
||||
const parentAccountNumber = 167132;
|
||||
const billToAccountNumber = 1234;
|
||||
const expected = null;
|
||||
|
||||
// Act
|
||||
const result = await getPricedMobileFeePart(serviceZipCode);
|
||||
const result = await getPricedMobileFeePart(
|
||||
serviceZipCode,
|
||||
serviceType,
|
||||
parentAccountNumber,
|
||||
billToAccountNumber
|
||||
);
|
||||
|
||||
// Assert
|
||||
expect(result).toEqual(expected);
|
||||
|
|
@ -60,6 +68,10 @@ describe("service-location-helper.js", () => {
|
|||
it("Should return the priced mobile fee part", async () => {
|
||||
// Arrange
|
||||
const serviceZipCode = "43235";
|
||||
const serviceType = "Replace";
|
||||
const parentAccountNumber = 167132;
|
||||
const billToAccountNumber = 1234;
|
||||
|
||||
const expected = {
|
||||
partNumber: "MOBILE FEE",
|
||||
description: "MOBILE FEE",
|
||||
|
|
@ -70,7 +82,12 @@ describe("service-location-helper.js", () => {
|
|||
};
|
||||
|
||||
// Act
|
||||
const result = await getPricedMobileFeePart(serviceZipCode);
|
||||
const result = await getPricedMobileFeePart(
|
||||
serviceZipCode,
|
||||
serviceType,
|
||||
parentAccountNumber,
|
||||
billToAccountNumber
|
||||
);
|
||||
|
||||
// Assert
|
||||
expect(result).toEqual(expected);
|
||||
|
|
@ -247,6 +247,7 @@ 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;
|
||||
|
|
@ -293,6 +294,7 @@ describe("mobile-location-modal-questions.vue", () => {
|
|||
});
|
||||
|
||||
wrapper.vm.internalModel = newMobileLocationQuestions;
|
||||
wrapper.vm.resetModalButtonStyle = jest.fn();
|
||||
|
||||
// Act
|
||||
await wrapper.vm.setMobileLocation();
|
||||
|
|
@ -300,40 +302,6 @@ describe("mobile-location-modal-questions.vue", () => {
|
|||
// Assert
|
||||
expect(wrapper.vm.displayInvalidZipAlert).toBe(true);
|
||||
});
|
||||
|
||||
it("Should clear the internal model when resetModel is called", 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,
|
||||
},
|
||||
});
|
||||
|
||||
// Act
|
||||
wrapper.vm.resetModel();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.internalModel.addressQuestions.streetAddress).toEqual("");
|
||||
expect(wrapper.vm.internalModel.addressQuestions.apartmentNumberOrBusinessName).toEqual("");
|
||||
expect(wrapper.vm.internalModel.addressQuestions.city).toEqual("");
|
||||
expect(wrapper.vm.internalModel.isVehicleProtected).toEqual(null);
|
||||
});
|
||||
});
|
||||
|
||||
function setupMocks({ mountOptions, mixins, props, isShallowMount = true }) {
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ import addressQuestions from "@/layouts/address-lookup/customer-questions/addres
|
|||
import vehicleProtectedQuestion from "@/layouts/service-location/mobile-location-modal-questions/vehicle-protected-question/vehicle-protected-question";
|
||||
|
||||
// Helpers
|
||||
import { deepClone } from "@/helpers/object-cloning-helper";
|
||||
import { deepClone } from "@/layouts/service-location/helpers/object-cloning-helper/object-cloning-helper";
|
||||
|
||||
export default {
|
||||
name: "mobile-location-modal-questions",
|
||||
|
|
@ -81,10 +81,12 @@ export default {
|
|||
zipCode: "",
|
||||
},
|
||||
isVehicleProtected: null,
|
||||
serviceZipCode: "",
|
||||
mobileFeePart: null,
|
||||
}),
|
||||
},
|
||||
mobileFeePart: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
isZipServiceableMobile: Boolean,
|
||||
isZipServiceableInShop: Boolean,
|
||||
linkWidgetName: String,
|
||||
|
|
@ -122,13 +124,14 @@ export default {
|
|||
return cmsContentText.replaceAll("{custom:mobileFee}", this.mobileFee);
|
||||
},
|
||||
mobileFee() {
|
||||
if (!this.modelValue.mobileFeePart) {
|
||||
if (!this.mobileFeePart) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (
|
||||
this.modelValue.mobileFeePart.laborAmount +
|
||||
this.modelValue.mobileFeePart.sellingPrice +
|
||||
this.modelValue.mobileFeePart.kitPrice
|
||||
this.mobileFeePart.laborAmount +
|
||||
this.mobileFeePart.sellingPrice +
|
||||
this.mobileFeePart.kitPrice
|
||||
);
|
||||
},
|
||||
modalFooterText() {
|
||||
|
|
@ -148,23 +151,22 @@ export default {
|
|||
closeModal() {
|
||||
this.$refs[this.modalName].closeModal();
|
||||
},
|
||||
resetComponent() {
|
||||
this.resetModel();
|
||||
|
||||
// Reset the validation form
|
||||
this.$refs[this.modalName].form.resetForm();
|
||||
|
||||
// Reinitialize the Address Auto Complete
|
||||
this.$refs.addressQuestions.setupAddressLookup();
|
||||
resetComponent(updatedServiceZipCodeInfo) {
|
||||
// Reset the validation form, setting the initial values
|
||||
// for the state and zipCode to those that were entered
|
||||
// on the service-zip-modal-question component
|
||||
this.$refs[this.modalName].form.resetForm({
|
||||
values: {
|
||||
state: updatedServiceZipCodeInfo.state,
|
||||
zipCode: updatedServiceZipCodeInfo.zipCode,
|
||||
},
|
||||
});
|
||||
},
|
||||
resetModel() {
|
||||
// Address
|
||||
this.internalModel.addressQuestions.streetAddress = "";
|
||||
this.internalModel.addressQuestions.apartmentNumberOrBusinessName = "";
|
||||
this.internalModel.addressQuestions.city = "";
|
||||
|
||||
// Is Vehicle Protected
|
||||
this.internalModel.isVehicleProtected = null;
|
||||
resetModalButtonStyle() {
|
||||
this.$refs[this.modalName].resetButtonStyle();
|
||||
},
|
||||
onAddressUpdated(updatedServiceZipCodeInfo) {
|
||||
this.resetComponent(updatedServiceZipCodeInfo);
|
||||
},
|
||||
async setMobileLocation() {
|
||||
// Validate the Zip Code
|
||||
|
|
@ -174,6 +176,7 @@ export default {
|
|||
|
||||
if (!zipCodeData.isValid) {
|
||||
this.displayInvalidZipAlert = true;
|
||||
this.resetModalButtonStyle();
|
||||
} else {
|
||||
// Update the page level model
|
||||
this.$emit("update:modelValue", this.internalModel);
|
||||
|
|
@ -185,6 +188,11 @@ export default {
|
|||
modelValue: {
|
||||
handler(newValue) {
|
||||
this.internalModel = deepClone(newValue);
|
||||
|
||||
this.resetComponent({
|
||||
state: newValue.state,
|
||||
zipCode: newValue.zipCode,
|
||||
});
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -32,11 +32,14 @@ const mockGetPricedMobileFeePart = (mockServiceZipCode) => {
|
|||
return Promise.resolve(mobileFeePart);
|
||||
};
|
||||
|
||||
jest.mock("@/helpers/service-location-helper", () => ({
|
||||
getPricedMobileFeePart: jest.fn((mockServiceZipCode) => {
|
||||
return mockGetPricedMobileFeePart(mockServiceZipCode);
|
||||
}),
|
||||
}));
|
||||
jest.mock(
|
||||
"@/layouts/service-location/helpers/service-location-helper/service-location-helper",
|
||||
() => ({
|
||||
getPricedMobileFeePart: jest.fn((mockServiceZipCode) => {
|
||||
return mockGetPricedMobileFeePart(mockServiceZipCode);
|
||||
}),
|
||||
})
|
||||
);
|
||||
|
||||
jest.mock("@/store", () => ({
|
||||
commit: jest.fn(),
|
||||
|
|
@ -107,6 +110,9 @@ const mockMixin = {
|
|||
getTotalLineItemPrice: jest.fn((lineItem) => {
|
||||
return 49.99;
|
||||
}),
|
||||
|
||||
onSubmit: jest.fn(),
|
||||
onInvalidSubmit: jest.fn(),
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -130,6 +136,9 @@ beforeEach(() => {
|
|||
state: "OH",
|
||||
},
|
||||
},
|
||||
damage: {
|
||||
isRepair: false,
|
||||
},
|
||||
payment: {
|
||||
isInsurance: false,
|
||||
},
|
||||
|
|
@ -168,7 +177,7 @@ describe("service-location.vue", () => {
|
|||
);
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.mobileLocationQuestions.mobileFeePart).toStrictEqual(mobileFeePart);
|
||||
expect(wrapper.vm.mobileFeePart).toStrictEqual(mobileFeePart);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -217,7 +226,9 @@ describe("service-location.vue", () => {
|
|||
describe("updating service zip", () => {
|
||||
test("updates the page model after providing the service zip code", () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
const { wrapper } = setupMocks({
|
||||
mixins: [mockMixin],
|
||||
});
|
||||
wrapper.vm.$refs.mobileLocationModalQuestions.resetComponent = jest.fn();
|
||||
|
||||
const newServiceZipCodeQuestion = {
|
||||
|
|
@ -256,7 +267,6 @@ describe("service-location.vue", () => {
|
|||
zipCode: "43054",
|
||||
},
|
||||
isVehicleProtected: true,
|
||||
mobileFeePart: null,
|
||||
};
|
||||
wrapper.vm.mobileLocationQuestions = mobileLocationQuestions;
|
||||
|
||||
|
|
@ -269,7 +279,6 @@ describe("service-location.vue", () => {
|
|||
zipCode: "61606",
|
||||
},
|
||||
isVehicleProtected: null,
|
||||
mobileFeePart: null,
|
||||
};
|
||||
|
||||
const serviceZipCodeComponent = wrapper.findComponent({
|
||||
|
|
@ -299,7 +308,6 @@ describe("service-location.vue", () => {
|
|||
zipCode: "",
|
||||
},
|
||||
isVehicleProtected: null,
|
||||
mobileFeePart: null,
|
||||
};
|
||||
wrapper.vm.mobileLocationQuestions = mobileLocationQuestions;
|
||||
|
||||
|
|
@ -312,7 +320,6 @@ describe("service-location.vue", () => {
|
|||
zipCode: "43054",
|
||||
},
|
||||
isVehicleProtected: true,
|
||||
mobileFeePart: null,
|
||||
};
|
||||
|
||||
const mobileLocationComponent = wrapper.findComponent({
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
modalWidgetName="ServiceZipModalWidget" />
|
||||
<mobileLocationModalQuestions
|
||||
v-model="mobileLocationQuestions"
|
||||
:mobileFeePart="mobileFeePart"
|
||||
ref="mobileLocationModalQuestions"
|
||||
linkWidgetName="MobileLocationLinkWidget"
|
||||
modalWidgetName="MobileLocationModalWidget" />
|
||||
|
|
@ -36,7 +37,7 @@ import { Form } from "vee-validate";
|
|||
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||
import { settleAllPromises } from "@/helpers/layout-helper";
|
||||
import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
|
||||
import { getPricedMobileFeePart } from "@/helpers/service-location-helper";
|
||||
import { getPricedMobileFeePart } from "@/layouts/service-location/helpers/service-location-helper/service-location-helper";
|
||||
import store from "@/store";
|
||||
|
||||
export default {
|
||||
|
|
@ -48,6 +49,7 @@ export default {
|
|||
city: this.getServiceCityFromStore(),
|
||||
state: this.getServiceStateFromStore(),
|
||||
zipCode: this.getServiceZipCodeFromStore(),
|
||||
isVehicleProtected: null,
|
||||
isZipServiceableMobile: null,
|
||||
isZipServiceableInShop: null,
|
||||
mobileFeePart: null,
|
||||
|
|
@ -58,7 +60,16 @@ export default {
|
|||
const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage);
|
||||
|
||||
const serviceZipCode = store.getters.order.serviceLocation.zipCode;
|
||||
const mobileFeePartPromise = getPricedMobileFeePart(serviceZipCode);
|
||||
const serviceType = store.getters.damage.isRepair ? "Repair" : "Replace";
|
||||
const parentAccountNumber = store.getters.payment.parentAccountNumber;
|
||||
const billToAccountNumber = 0;
|
||||
|
||||
const mobileFeePartPromise = getPricedMobileFeePart(
|
||||
serviceZipCode,
|
||||
serviceType,
|
||||
parentAccountNumber,
|
||||
billToAccountNumber
|
||||
);
|
||||
|
||||
// Settle promises and get results
|
||||
const promiseResultMap = [
|
||||
|
|
@ -90,7 +101,7 @@ export default {
|
|||
},
|
||||
set: function (newValue) {
|
||||
if (newValue.zipCode !== this.zipCode) {
|
||||
this.resetMobileLocation(this.zipCode);
|
||||
this.resetMobileLocation(newValue);
|
||||
}
|
||||
|
||||
this.state = newValue.state;
|
||||
|
|
@ -108,7 +119,6 @@ export default {
|
|||
zipCode: this.zipCode,
|
||||
},
|
||||
isVehicleProtected: this.isVehicleProtected,
|
||||
mobileFeePart: this.mobileFeePart,
|
||||
};
|
||||
},
|
||||
set: function (newValue) {
|
||||
|
|
@ -119,7 +129,6 @@ export default {
|
|||
this.state = newValue.addressQuestions.state;
|
||||
this.zipCode = newValue.addressQuestions.zipCode;
|
||||
this.isVehicleProtected = newValue.isVehicleProtected;
|
||||
this.mobileFeePart = newValue.mobileFeePart;
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -149,19 +158,27 @@ export default {
|
|||
return store.getters.order.serviceLocation.zipCode;
|
||||
},
|
||||
resetMobileFeePart(serviceZipCode) {
|
||||
getPricedMobileFeePart(serviceZipCode).then((pricedMobileFeePart) => {
|
||||
const serviceType = store.getters.damage.isRepair ? "Repair" : "Replace";
|
||||
const parentAccountNumber = store.getters.payment.parentAccountNumber;
|
||||
const billToAccountNumber = 0;
|
||||
|
||||
getPricedMobileFeePart(
|
||||
serviceZipCode,
|
||||
serviceType,
|
||||
parentAccountNumber,
|
||||
billToAccountNumber
|
||||
).then((pricedMobileFeePart) => {
|
||||
this.mobileFeePart = pricedMobileFeePart;
|
||||
});
|
||||
},
|
||||
resetMobileLocation(updatedServiceZipCode) {
|
||||
resetMobileLocation(updatedServiceZipCodeInfo) {
|
||||
this.streetAddress = "";
|
||||
this.apartmentNumberOrBusinessName = "";
|
||||
this.city = "";
|
||||
|
||||
this.isVehicleProtected = null;
|
||||
|
||||
this.$refs.mobileLocationModalQuestions.resetComponent();
|
||||
|
||||
this.resetMobileFeePart(updatedServiceZipCode);
|
||||
this.resetMobileFeePart(updatedServiceZipCodeInfo.zipCode);
|
||||
},
|
||||
backButtonAction() {
|
||||
this.$router.navigateWithoutSaving(this.navigationScenarios.CLICKED_BACK, this.$route);
|
||||
|
|
|
|||
|
|
@ -1431,11 +1431,16 @@ export const actions = {
|
|||
context.commit(storeMutations.UPDATE_VAPS, vaps);
|
||||
},
|
||||
// Price order actions
|
||||
async priceOrderItemsAndSaveServerData(context, { availableLineItems, serviceZipCode, ctu }) {
|
||||
async priceOrderItemsAndSaveServerData(
|
||||
context,
|
||||
{ availableLineItems, serviceZipCode, serviceZipCodeCtu }
|
||||
) {
|
||||
const zipCodeToUse = serviceZipCode
|
||||
? serviceZipCode
|
||||
: context.getters.order.serviceLocation.zipCode;
|
||||
const ctuToUse = ctu ? ctu : context.getters.order.serviceLocation.zipCodeCtu;
|
||||
const ctuToUse = serviceZipCodeCtu
|
||||
? serviceZipCodeCtu
|
||||
: context.getters.order.serviceLocation.zipCodeCtu;
|
||||
|
||||
const availableLineItemsFormattedForRequest =
|
||||
getLineItemQueryStringForPricing(availableLineItems);
|
||||
|
|
|
|||
Loading…
Reference in a new issue