Merge branch 'release/2025.07.31' into feature/CASH-845-stage-6
This commit is contained in:
commit
5a8bc3ef56
9 changed files with 190 additions and 605 deletions
|
|
@ -182,11 +182,11 @@ export default {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
focusSearchInput() {
|
focusSearchInput(event) {
|
||||||
// Focus cursor in input when search icon is clicked
|
// Focus cursor in input when search icon is clicked
|
||||||
const field = document.querySelector("input");
|
const field = document.querySelector("input");
|
||||||
field.focus();
|
field.focus();
|
||||||
this.$emit("search-icon-click");
|
this.$emit("search-icon-click", event);
|
||||||
},
|
},
|
||||||
async imageChanged(e) {
|
async imageChanged(e) {
|
||||||
let file = e.target.files[0];
|
let file = e.target.files[0];
|
||||||
|
|
|
||||||
|
|
@ -105,23 +105,11 @@
|
||||||
class="ps-4 pe-4 mt-4" />
|
class="ps-4 pe-4 mt-4" />
|
||||||
</div>
|
</div>
|
||||||
<shopQuestionPopup
|
<shopQuestionPopup
|
||||||
modalWidgetName="ShopQuestionPopupWidget"
|
v-if="isShopQuestionDisplayed"
|
||||||
:appointmentType="appointmentType"
|
:selectedProviderNumberFromParent="selectedProvider?.providerNumber"
|
||||||
:modelValue="{
|
:shopProviderDataFromParent="shopProviderData"
|
||||||
zipCode: zipCode,
|
|
||||||
state: state,
|
|
||||||
}"
|
|
||||||
:preSelectedProviderNumber="selectedProvider?.providerNumber"
|
|
||||||
:shopProviderData="shopProviderData"
|
|
||||||
:isServiceableInshop="isServiceableInshop"
|
|
||||||
@updated-serviceability="setServiceabilityDetails"
|
|
||||||
@shop-selected="onShopSelected"
|
@shop-selected="onShopSelected"
|
||||||
@update:modelValue="onShopModelUpdated"
|
:zipCodeFromParent="zipCode"
|
||||||
@updated-zipcode-ctu="setZipcodeCtu"
|
|
||||||
@update-shop-provider-data="shopProviderData = $event"
|
|
||||||
@updated-contains-military-base="setContainsMilitaryBase"
|
|
||||||
:zipCode="zipCode"
|
|
||||||
:isDisplayed="isShopQuestionDisplayed"
|
|
||||||
cmsWidgetName="YourSafeliteShopWidget" />
|
cmsWidgetName="YourSafeliteShopWidget" />
|
||||||
|
|
||||||
<contentGroupModal ref="RecalModal" cmsWidgetName="RecalModal" />
|
<contentGroupModal ref="RecalModal" cmsWidgetName="RecalModal" />
|
||||||
|
|
@ -174,7 +162,6 @@ import shopQuestion from "@/layouts/service-location/shop-question/shop-question
|
||||||
import shopQuestionPopup from "@/layouts/service-location/shop-question/shop-question-popup";
|
import shopQuestionPopup from "@/layouts/service-location/shop-question/shop-question-popup";
|
||||||
import buttonQuestion from "@/digital-components/button-question/button-question.vue";
|
import buttonQuestion from "@/digital-components/button-question/button-question.vue";
|
||||||
import shopListButton from "@/layouts/service-location/shop-question/shop-list-button/shop-list-button";
|
import shopListButton from "@/layouts/service-location/shop-question/shop-list-button/shop-list-button";
|
||||||
import { getAvailabilityRating } from "@/layouts/service-location/helpers/service-location-helper/service-location-helper";
|
|
||||||
|
|
||||||
import funnelHeader from "@/fmg-components/funnel-header/funnel-header";
|
import funnelHeader from "@/fmg-components/funnel-header/funnel-header";
|
||||||
import navbar from "@/fmg-components/nav-bar/nav-bar";
|
import navbar from "@/fmg-components/nav-bar/nav-bar";
|
||||||
|
|
@ -804,17 +791,7 @@ export default {
|
||||||
return this.displayNoShopsAlert || !this.isMobileAddressValid;
|
return this.displayNoShopsAlert || !this.isMobileAddressValid;
|
||||||
},
|
},
|
||||||
additionalButtonData() {
|
additionalButtonData() {
|
||||||
const startDate = new Date();
|
|
||||||
const endDate = new Date();
|
|
||||||
endDate.setDate(startDate.getDate() + 6);
|
|
||||||
|
|
||||||
const formattedStartDate = startDate.toISOString().split("T")[0];
|
|
||||||
const formattedEndDate = endDate.toISOString().split("T")[0];
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
availabilityRatingCallback: getAvailabilityRating,
|
|
||||||
startDate: formattedStartDate,
|
|
||||||
endDate: formattedEndDate,
|
|
||||||
shopAppointmentType: this.appointmentType,
|
shopAppointmentType: this.appointmentType,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
@ -1163,43 +1140,20 @@ export default {
|
||||||
startTime: "08:00",
|
startTime: "08:00",
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
onShopSelected(providerObject) {
|
onShopSelected(shopQuestionPopUpData) {
|
||||||
const current = this.shopProviderData?.shopProviders?.find(
|
this.shopProviderData = shopQuestionPopUpData.shopProviderData;
|
||||||
(p) => p.providerNumber === this.selectedProvider?.providerNumber
|
this.setServiceabilityDetails(shopQuestionPopUpData.serviceabilityDetails);
|
||||||
);
|
this.selectedProvider = this.shopProviderData.shopProviders.find((shopProvider) => {
|
||||||
if (current) {
|
return shopProvider.providerNumber === shopQuestionPopUpData.selectedProviderNumber;
|
||||||
this.selectedProvider = current;
|
});
|
||||||
}
|
|
||||||
|
|
||||||
let provider = this.shopProviderData?.shopProviders?.find(
|
// No zipCodeData comes back if zip was not changed
|
||||||
(p) => p.providerNumber === providerObject?.providerNumber
|
if (shopQuestionPopUpData.zipCodeData) {
|
||||||
);
|
this.zipCode = shopQuestionPopUpData.zipCodeData.zipCode;
|
||||||
this.selectedProvider = provider;
|
this.state = shopQuestionPopUpData.zipCodeData.state;
|
||||||
if (providerObject && providerObject.address) {
|
this.zipCodeCtu = shopQuestionPopUpData.zipCodeData.zipCodeCtu;
|
||||||
this.zipCode = providerObject.searchedZip;
|
this.zipContainsMilitaryBase =
|
||||||
this.state = providerObject.searchedState;
|
shopQuestionPopUpData.zipCodeData.containsMilitaryBase;
|
||||||
}
|
|
||||||
},
|
|
||||||
setZipcodeCtu(zipcodeCtu) {
|
|
||||||
this.zipCodeCtu = zipcodeCtu;
|
|
||||||
},
|
|
||||||
onShopModelUpdated(newModel) {
|
|
||||||
if (newModel.zipCode) {
|
|
||||||
this.zipCode = newModel.zipCode;
|
|
||||||
}
|
|
||||||
if (newModel.zipCodeCtu) {
|
|
||||||
this.zipCodeCtu = newModel.zipCodeCtu;
|
|
||||||
}
|
|
||||||
if (newModel.state) {
|
|
||||||
this.state = newModel.state;
|
|
||||||
}
|
|
||||||
if (this.shopProviderData && newModel.selectedProviderNumber) {
|
|
||||||
const provider = this.shopProviderData.shopProviders.find(
|
|
||||||
(p) => p.providerNumber === newModel.selectedProviderNumber
|
|
||||||
);
|
|
||||||
if (provider) {
|
|
||||||
this.selectedProvider = provider;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async getMoreScheduleData(startDate, endDate) {
|
async getMoreScheduleData(startDate, endDate) {
|
||||||
|
|
|
||||||
|
|
@ -122,35 +122,6 @@ export async function getShopProviderData(serviceZipCode) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getAvailabilityRating(
|
|
||||||
startDate,
|
|
||||||
endDate,
|
|
||||||
shopAppointmentType,
|
|
||||||
providerNumber
|
|
||||||
) {
|
|
||||||
// For a given shop provider number and date range, get the appointment time slots available
|
|
||||||
const shopTimeSlots = await baseMixin.methods.dispatchStoreActionWithLogging(
|
|
||||||
storeActions.GET_SHOP_TIME_SLOTS,
|
|
||||||
{
|
|
||||||
providerNumber: providerNumber,
|
|
||||||
startDate: startDate,
|
|
||||||
endDate: endDate,
|
|
||||||
shopAppointmentType: shopAppointmentType,
|
|
||||||
},
|
|
||||||
"service-location",
|
|
||||||
false
|
|
||||||
);
|
|
||||||
|
|
||||||
const numberOfDaysToEvaluate = 2;
|
|
||||||
const isGoodAvailability =
|
|
||||||
shopTimeSlots.data.days.filter((x) => x.timeSlots.length > 0).length >=
|
|
||||||
numberOfDaysToEvaluate;
|
|
||||||
|
|
||||||
const shopStatus = isGoodAvailability ? "high" : "low";
|
|
||||||
|
|
||||||
return shopStatus;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getClosestApplicableShops(serviceZipCode, carId, pageNameToLog) {
|
export async function getClosestApplicableShops(serviceZipCode, carId, pageNameToLog) {
|
||||||
if (!serviceZipCode) {
|
if (!serviceZipCode) {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,4 @@
|
||||||
import {
|
import { getPricedMobileFeePart, getServiceabilityDetails } from "./service-location-helper";
|
||||||
getPricedMobileFeePart,
|
|
||||||
getServiceabilityDetails,
|
|
||||||
getAvailabilityRating,
|
|
||||||
} from "./service-location-helper";
|
|
||||||
import { storeActions } from "@/constants/store-actions";
|
import { storeActions } from "@/constants/store-actions";
|
||||||
|
|
||||||
jest.mock("@/store", () => ({
|
jest.mock("@/store", () => ({
|
||||||
|
|
@ -320,38 +316,4 @@ describe("service-location-helper.js", () => {
|
||||||
expect(result).toEqual(expected);
|
expect(result).toEqual(expected);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("getAvailabilityRating", () => {
|
|
||||||
it("Should return a 'high' rating", async () => {
|
|
||||||
// Arrange
|
|
||||||
const providerNumber = "0000001";
|
|
||||||
const expected = "high";
|
|
||||||
// Act
|
|
||||||
const result = await getAvailabilityRating(
|
|
||||||
"2023-06-30",
|
|
||||||
"2023-07-06",
|
|
||||||
"Inshop",
|
|
||||||
providerNumber
|
|
||||||
);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
expect(result).toEqual(expected);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Should return a 'low' rating", async () => {
|
|
||||||
// Arrange
|
|
||||||
const providerNumber = "0000000";
|
|
||||||
const expected = "low";
|
|
||||||
// Act
|
|
||||||
const result = await getAvailabilityRating(
|
|
||||||
"2023-06-30",
|
|
||||||
"2023-07-06",
|
|
||||||
"Inshop",
|
|
||||||
providerNumber
|
|
||||||
);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
expect(result).toEqual(expected);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -148,11 +148,9 @@
|
||||||
import alert from "@/ux-components/alert/alert";
|
import alert from "@/ux-components/alert/alert";
|
||||||
import serviceZipModalQuestion from "@/layouts/service-location/service-zip-modal-question/service-zip-modal-question";
|
import serviceZipModalQuestion from "@/layouts/service-location/service-zip-modal-question/service-zip-modal-question";
|
||||||
import appointmentTypeQuestion from "@/layouts/service-location/appointment-type-question/appointment-type-question";
|
import appointmentTypeQuestion from "@/layouts/service-location/appointment-type-question/appointment-type-question";
|
||||||
import shopQuestion from "@/layouts/service-location/shop-question/shop-question";
|
|
||||||
import shopQuestionPopup from "@/layouts/service-location/shop-question/shop-question-popup";
|
import shopQuestionPopup from "@/layouts/service-location/shop-question/shop-question-popup";
|
||||||
import buttonQuestion from "@/digital-components/button-question/button-question.vue";
|
import buttonQuestion from "@/digital-components/button-question/button-question.vue";
|
||||||
import shopListButton from "@/layouts/service-location/shop-question/shop-list-button/shop-list-button";
|
import shopListButton from "@/layouts/service-location/shop-question/shop-list-button/shop-list-button";
|
||||||
import { getAvailabilityRating } from "@/layouts/service-location/helpers/service-location-helper/service-location-helper";
|
|
||||||
|
|
||||||
import funnelHeader from "@/fmg-components/funnel-header/funnel-header";
|
import funnelHeader from "@/fmg-components/funnel-header/funnel-header";
|
||||||
import navbar from "@/fmg-components/nav-bar/nav-bar";
|
import navbar from "@/fmg-components/nav-bar/nav-bar";
|
||||||
|
|
@ -163,7 +161,6 @@ import contentGroupModal from "@/fmg-components/content-group-modal/content-grou
|
||||||
import textBlock from "@/digital-components/text-block/text-block";
|
import textBlock from "@/digital-components/text-block/text-block";
|
||||||
|
|
||||||
// Supporting files
|
// Supporting files
|
||||||
import baseMixin from "@/mixins/base-mixin.js";
|
|
||||||
import experimentMixin from "@/mixins/experiment-mixin.js";
|
import experimentMixin from "@/mixins/experiment-mixin.js";
|
||||||
import { experimentSettings } from "@/constants/experiments";
|
import { experimentSettings } from "@/constants/experiments";
|
||||||
import { AppointmentTypeStrings } from "@/constants/schedule-constants";
|
import { AppointmentTypeStrings } from "@/constants/schedule-constants";
|
||||||
|
|
@ -450,17 +447,7 @@ export default {
|
||||||
return this.displayNoShopsAlert || !this.isMobileAddressValid;
|
return this.displayNoShopsAlert || !this.isMobileAddressValid;
|
||||||
},
|
},
|
||||||
additionalButtonData() {
|
additionalButtonData() {
|
||||||
const startDate = new Date();
|
|
||||||
const endDate = new Date();
|
|
||||||
endDate.setDate(startDate.getDate() + 6);
|
|
||||||
|
|
||||||
const formattedStartDate = startDate.toISOString().split("T")[0];
|
|
||||||
const formattedEndDate = endDate.toISOString().split("T")[0];
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
availabilityRatingCallback: getAvailabilityRating,
|
|
||||||
startDate: formattedStartDate,
|
|
||||||
endDate: formattedEndDate,
|
|
||||||
shopAppointmentType: this.selectedAppointmentType,
|
shopAppointmentType: this.selectedAppointmentType,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@
|
||||||
cornerStyle="rounded"
|
cornerStyle="rounded"
|
||||||
mask="#####"
|
mask="#####"
|
||||||
:includeSearchIcon="includeSearchIcon"
|
:includeSearchIcon="includeSearchIcon"
|
||||||
@search-icon-click="$emit('search-icon-click')"
|
|
||||||
:displayQuestionText="false"
|
:displayQuestionText="false"
|
||||||
:isRequired="isRequired"
|
:isRequired="isRequired"
|
||||||
:validationRules="computedValidationRules" />
|
:validationRules="computedValidationRules" />
|
||||||
|
|
|
||||||
|
|
@ -58,15 +58,22 @@ export default {
|
||||||
beforeMount() {
|
beforeMount() {
|
||||||
if (this.displayAvailabilityIndicators) {
|
if (this.displayAvailabilityIndicators) {
|
||||||
this.displayLoader();
|
this.displayLoader();
|
||||||
const startDate = this.additionalButtonData.startDate;
|
const startDate = new Date();
|
||||||
const endDate = this.additionalButtonData.endDate;
|
const endDate = new Date();
|
||||||
|
endDate.setDate(startDate.getDate() + 6);
|
||||||
|
const formattedStartDate = startDate.toISOString().split("T")[0];
|
||||||
|
const formattedEndDate = endDate.toISOString().split("T")[0];
|
||||||
|
|
||||||
const shopAppointmentType = this.additionalButtonData.shopAppointmentType;
|
const shopAppointmentType = this.additionalButtonData.shopAppointmentType;
|
||||||
|
|
||||||
this.additionalButtonData
|
this.getAvailabilityRating(
|
||||||
.availabilityRatingCallback(startDate, endDate, shopAppointmentType, this.value)
|
formattedStartDate,
|
||||||
.then((data) => {
|
formattedEndDate,
|
||||||
this.availabilityRating = data;
|
shopAppointmentType,
|
||||||
});
|
this.value
|
||||||
|
).then((data) => {
|
||||||
|
this.availabilityRating = data;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
|
|
@ -103,6 +110,29 @@ export default {
|
||||||
displayLoader() {
|
displayLoader() {
|
||||||
this.isLoaderDisplayed = true;
|
this.isLoaderDisplayed = true;
|
||||||
},
|
},
|
||||||
|
async getAvailabilityRating(startDate, endDate, shopAppointmentType, providerNumber) {
|
||||||
|
// For a given shop provider number and date range, get the appointment time slots available
|
||||||
|
const shopTimeSlots = await this.dispatchStoreActionWithLogging(
|
||||||
|
this.storeActions.GET_SHOP_TIME_SLOTS,
|
||||||
|
{
|
||||||
|
providerNumber: providerNumber,
|
||||||
|
startDate: startDate,
|
||||||
|
endDate: endDate,
|
||||||
|
shopAppointmentType: shopAppointmentType,
|
||||||
|
},
|
||||||
|
"service-location",
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
const numberOfDaysToEvaluate = 2;
|
||||||
|
const isGoodAvailability =
|
||||||
|
shopTimeSlots.data.days.filter((x) => x.timeSlots.length > 0).length >=
|
||||||
|
numberOfDaysToEvaluate;
|
||||||
|
|
||||||
|
const shopStatus = isGoodAvailability ? "high" : "low";
|
||||||
|
|
||||||
|
return shopStatus;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
loader,
|
loader,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<transition name="fade" mode="out-in">
|
<transition name="fade" mode="out-in">
|
||||||
<div v-if="isDisplayed" class="shop-question" aria-live="polite">
|
<div class="shop-question" aria-live="polite">
|
||||||
<div class="modal-link">
|
<div class="modal-link">
|
||||||
<text-link
|
<text-link
|
||||||
linkType="text"
|
linkType="text"
|
||||||
|
|
@ -13,20 +13,17 @@
|
||||||
<modal
|
<modal
|
||||||
ref="shopQuestionModal"
|
ref="shopQuestionModal"
|
||||||
:headerText="modalHeaderText"
|
:headerText="modalHeaderText"
|
||||||
:onModalOpenedCallback="onModalOpened"
|
:onModalOpenedCallback="updateSelectedAnswer"
|
||||||
:onModalClosedCallback="onModalClosed"
|
:onModalClosedCallback="resetZipCodeData"
|
||||||
:footerButtonText="modalFooterText"
|
:footerButtonText="modalFooterText"
|
||||||
:footerButtonDisabled="
|
:footerButtonDisabled="isModalFooterButtonDisabled"
|
||||||
displayInvalidZipAlert || displayNoShopsAlert || !selectedProviderNumber
|
|
||||||
"
|
|
||||||
@footer-button-event="setZipCodeAndShop">
|
@footer-button-event="setZipCodeAndShop">
|
||||||
<form @submit.prevent>
|
<form @submit.prevent>
|
||||||
<serviceZipQuestion
|
<serviceZipQuestion
|
||||||
ref="serviceZipQuestion"
|
ref="serviceZipQuestion"
|
||||||
class="shop-question-zipcode"
|
class="shop-question-zipcode"
|
||||||
customInputId="serviceZipCode"
|
customInputId="serviceZipCode"
|
||||||
v-model="internalModel.zipCode"
|
v-model="localZipCode"
|
||||||
v-on="{ 'textboxQuestionEvent.inputIdAssigned': onInputIdAssigned }"
|
|
||||||
cmsWidgetName="ServiceZipQuestionWidget"
|
cmsWidgetName="ServiceZipQuestionWidget"
|
||||||
@search-icon-click="onSearchZip"
|
@search-icon-click="onSearchZip"
|
||||||
@keydown.enter="onSearchZip"
|
@keydown.enter="onSearchZip"
|
||||||
|
|
@ -57,9 +54,9 @@
|
||||||
:answers="answers"
|
:answers="answers"
|
||||||
groupName="chooseShop"
|
groupName="chooseShop"
|
||||||
textPosition="text-start"
|
textPosition="text-start"
|
||||||
v-model="selectedProviderNumber"
|
v-model="localSelectedProviderNumber"
|
||||||
isRequired
|
isRequired
|
||||||
:validationRules="this.selectedProviderNumber ? '' : 'option-required'" />
|
:validationRules="this.localSelectedProviderNumber ? '' : 'option-required'" />
|
||||||
<div class="show-more-shops-link">
|
<div class="show-more-shops-link">
|
||||||
<textLink
|
<textLink
|
||||||
v-if="displaySeeMoreLocationsLink"
|
v-if="displaySeeMoreLocationsLink"
|
||||||
|
|
@ -69,7 +66,7 @@
|
||||||
linkType="text"
|
linkType="text"
|
||||||
:text="showMoreShopsLinkText"
|
:text="showMoreShopsLinkText"
|
||||||
href="#!"
|
href="#!"
|
||||||
@click-event="getNextShopsFromList(3)"
|
@click-event="updateShopListWithNextShops()"
|
||||||
:aria-label="showMoreShopsLinkText" />
|
:aria-label="showMoreShopsLinkText" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -84,81 +81,43 @@ import serviceZipQuestion from "@/layouts/service-location/service-zip-modal-que
|
||||||
import buttonQuestion from "@/digital-components/button-question/button-question.vue";
|
import buttonQuestion from "@/digital-components/button-question/button-question.vue";
|
||||||
import alert from "@/ux-components/alert/alert.vue";
|
import alert from "@/ux-components/alert/alert.vue";
|
||||||
import shopListButton from "./shop-list-button/shop-list-button";
|
import shopListButton from "./shop-list-button/shop-list-button";
|
||||||
import loadingModal from "@/fmg-components/loading-modal/loading-modal.vue";
|
|
||||||
|
|
||||||
// Supporting files
|
// Supporting files
|
||||||
import { defineRule } from "vee-validate";
|
import { defineRule } from "vee-validate";
|
||||||
import { required } from "@/helpers/validation-rules";
|
import { required } from "@/helpers/validation-rules";
|
||||||
import { errorMessages } from "@/constants/error-messages";
|
import { errorMessages } from "@/constants/error-messages";
|
||||||
import baseMixin from "@/mixins/base-mixin.js";
|
import baseMixin from "@/mixins/base-mixin.js";
|
||||||
import { nextTick } from "vue";
|
|
||||||
import { regex } from "@/helpers/validation-rules";
|
|
||||||
|
|
||||||
import { getAvailabilityRating } from "@/layouts/service-location/helpers/service-location-helper/service-location-helper";
|
|
||||||
|
|
||||||
import { Provider } from "@/layouts/service-location/classes/provider";
|
|
||||||
import { AppointmentTypeStrings } from "@/constants/schedule-constants";
|
import { AppointmentTypeStrings } from "@/constants/schedule-constants";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getPricedMobileFeePart,
|
|
||||||
getPricedRecycleFeePart,
|
|
||||||
getServiceabilityDetails,
|
getServiceabilityDetails,
|
||||||
getBillToAccountNumber,
|
|
||||||
getClosestApplicableShops,
|
getClosestApplicableShops,
|
||||||
getShopProviderData,
|
getShopProviderData,
|
||||||
getZipCodeData,
|
|
||||||
} from "@/layouts/service-location/helpers/service-location-helper/service-location-helper";
|
} from "@/layouts/service-location/helpers/service-location-helper/service-location-helper";
|
||||||
|
|
||||||
defineRule("option-required", required(errorMessages.OPTION_REQUIRED));
|
defineRule("option-required", required(errorMessages.OPTION_REQUIRED));
|
||||||
defineRule("zip-format", regex(/(^\d{5}$)|(^\d{5}-\d{4}$)/, errorMessages.SERVICE_ZIP_FORMAT));
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "shop-question-popup",
|
name: "shop-question-popup",
|
||||||
mixins: [baseMixin],
|
mixins: [baseMixin],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
isShopQuestionDisplayed: true,
|
localZipCode: this.zipCodeFromParent,
|
||||||
internalModel: this.copyModel(this.modelValue),
|
|
||||||
answers: [],
|
|
||||||
shopListButton: shopListButton,
|
shopListButton: shopListButton,
|
||||||
shopIndex: 0,
|
numberOfShopsToDisplay: 0,
|
||||||
displaySeeMoreLocationsLink: false,
|
localShopProviderData: this.shopProviderDataFromParent,
|
||||||
serviceZipCodeTextInputId: "",
|
localSelectedProviderNumber: null,
|
||||||
localShopProviderData: this.shopProviderData,
|
zipCodeData: null,
|
||||||
selectedProviderNumber: null,
|
|
||||||
isLoadingShops: false,
|
|
||||||
lastSearchedZip: "",
|
|
||||||
lastSuccessfulZip: "",
|
|
||||||
hasLoadedShopData: false,
|
|
||||||
displayInvalidZipAlert: false,
|
|
||||||
areShopsAvialable: true,
|
|
||||||
displayNoShopsAlert: false,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
appointmentType: String,
|
shopProviderDataFromParent: Object,
|
||||||
shopProviderData: Object,
|
zipCodeFromParent: {
|
||||||
modelValue: {
|
|
||||||
type: Object,
|
|
||||||
default: () => ({
|
|
||||||
state: "",
|
|
||||||
zipCode: "",
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
isDisplayed: Boolean,
|
|
||||||
modelWidgetName: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
isServiceableInshop: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
zipCode: {
|
|
||||||
type: String,
|
type: String,
|
||||||
default: "",
|
default: "",
|
||||||
},
|
},
|
||||||
preSelectedProviderNumber: {
|
selectedProviderNumberFromParent: {
|
||||||
type: String,
|
type: String,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
|
|
@ -167,14 +126,20 @@ export default {
|
||||||
shopQuestionLinkText() {
|
shopQuestionLinkText() {
|
||||||
return this.getCmsContent("ChangeMyLocationLinkWidget", "Text");
|
return this.getCmsContent("ChangeMyLocationLinkWidget", "Text");
|
||||||
},
|
},
|
||||||
modalName() {
|
|
||||||
return this.modelWidgetName;
|
|
||||||
},
|
|
||||||
modalHeaderText() {
|
modalHeaderText() {
|
||||||
return this.getCmsContent("ShopQuestionWidget", "QuestionText");
|
return this.getCmsContent("ShopQuestionWidget", "QuestionText");
|
||||||
},
|
},
|
||||||
selectShopText() {
|
modalFooterText() {
|
||||||
return this.getCmsContent("YourSafeliteShopWidget", "QuestionText");
|
return this.getCmsContent("SaveLocationWidget", "Text");
|
||||||
|
},
|
||||||
|
showMoreShopsLinkText() {
|
||||||
|
return this.getCmsContent("ShowMoreShopsLinkWidget", "Text");
|
||||||
|
},
|
||||||
|
displayInvalidZipAlert() {
|
||||||
|
return this.zipCodeData && !this.zipCodeData.isValid;
|
||||||
|
},
|
||||||
|
displayNoShopsAlert() {
|
||||||
|
return !this.shopProviders.length && !this.displayInvalidZipAlert;
|
||||||
},
|
},
|
||||||
modal() {
|
modal() {
|
||||||
return this.$refs.shopQuestionModal;
|
return this.$refs.shopQuestionModal;
|
||||||
|
|
@ -182,79 +147,32 @@ export default {
|
||||||
shopProviders() {
|
shopProviders() {
|
||||||
return this.localShopProviderData?.shopProviders ?? [];
|
return this.localShopProviderData?.shopProviders ?? [];
|
||||||
},
|
},
|
||||||
modalFooterText() {
|
|
||||||
return this.getCmsContent("SaveLocationWidget", "Text");
|
|
||||||
},
|
|
||||||
selectedValue: {
|
|
||||||
get: function () {
|
|
||||||
return this.modelValue;
|
|
||||||
},
|
|
||||||
set: function (newValue) {},
|
|
||||||
},
|
|
||||||
additionalButtonData() {
|
additionalButtonData() {
|
||||||
const startDate = new Date();
|
|
||||||
const endDate = new Date();
|
|
||||||
endDate.setDate(startDate.getDate() + 6);
|
|
||||||
|
|
||||||
const formattedStartDate = startDate.toISOString().split("T")[0];
|
|
||||||
const formattedEndDate = endDate.toISOString().split("T")[0];
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
availabilityRatingCallback: getAvailabilityRating,
|
// Used for availability indicators, could be refactored out of any availability indicator
|
||||||
startDate: formattedStartDate,
|
// logic if/when we are sure we'll never have separate drop off/ in shop logic
|
||||||
endDate: formattedEndDate,
|
shopAppointmentType: AppointmentTypeStrings.IN_SHOP_OR_DROP_OFF,
|
||||||
shopAppointmentType: this.appointmentType,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
showMoreShopsLinkText() {
|
displaySeeMoreLocationsLink() {
|
||||||
return this.getCmsContent("ShowMoreShopsLinkWidget", "Text");
|
if (!this.shopProviders.length) return false;
|
||||||
|
return this.numberOfShopsToDisplay < this.shopProviders.length;
|
||||||
},
|
},
|
||||||
},
|
isModalFooterButtonDisabled() {
|
||||||
methods: {
|
return (
|
||||||
openModal(event) {
|
this.displayInvalidZipAlert ||
|
||||||
if (event) event.preventDefault();
|
this.displayNoShopsAlert ||
|
||||||
if (
|
!this.localSelectedProviderNumber
|
||||||
!this.shopProviderData ||
|
);
|
||||||
!this.shopProviderData.shopProviders ||
|
|
||||||
!this.shopProviderData.shopProviders.length
|
|
||||||
) {
|
|
||||||
// Optionally show a loading message or disable the link
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.$refs.shopQuestionModal.openModal();
|
|
||||||
},
|
},
|
||||||
getSelectedProviderObject(providerNumber) {
|
answers() {
|
||||||
const provider =
|
const updatedProvidersToDisplay = this.shopProviders.slice(
|
||||||
this.shopProviders?.find((provider) => provider.providerNumber == providerNumber) ??
|
0,
|
||||||
new Provider();
|
this.numberOfShopsToDisplay
|
||||||
|
);
|
||||||
return provider;
|
return updatedProvidersToDisplay.map((shopProvider) => {
|
||||||
},
|
const streetAddress = this.toTitleCase(shopProvider.address.streetAddress);
|
||||||
async getNextShopsFromList(numberToGet = 3) {
|
const city = this.toTitleCase(shopProvider.address.city);
|
||||||
const logFirstShopsDisplayed = this.shopIndex == 0;
|
|
||||||
|
|
||||||
const shopIterator = (array, n) => {
|
|
||||||
const l = array.length;
|
|
||||||
return () => {
|
|
||||||
const end = this.shopIndex + n;
|
|
||||||
const part = array.slice(this.shopIndex, end);
|
|
||||||
this.shopIndex = end < l ? end : this.shopProviders.length;
|
|
||||||
return part;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const toTitleCase = (str) => {
|
|
||||||
return str.replace(/\w\S*/g, function (txt) {
|
|
||||||
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const nextShop = shopIterator(this.shopProviders, numberToGet);
|
|
||||||
|
|
||||||
// Map API result data
|
|
||||||
const mappedData = nextShop().map((shopProvider) => {
|
|
||||||
const streetAddress = toTitleCase(shopProvider.address.streetAddress);
|
|
||||||
const city = toTitleCase(shopProvider.address.city);
|
|
||||||
const state = shopProvider.address.state;
|
const state = shopProvider.address.state;
|
||||||
const zipCode = shopProvider.address.zipCode;
|
const zipCode = shopProvider.address.zipCode;
|
||||||
const distanceInMiles = Math.round(shopProvider.distanceInMiles * 2) / 2;
|
const distanceInMiles = Math.round(shopProvider.distanceInMiles * 2) / 2;
|
||||||
|
|
@ -267,354 +185,130 @@ export default {
|
||||||
value: shopProvider.providerNumber,
|
value: shopProvider.providerNumber,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
var gaAction = this.GaActions.MORE_LOCATIONS_CLICKED;
|
|
||||||
if (logFirstShopsDisplayed) {
|
|
||||||
gaAction = this.GaActions.SHOPS_FIRST_DISPLAYED;
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
this.appointmentType === AppointmentTypeStrings.IN_SHOP ||
|
|
||||||
this.appointmentType === AppointmentTypeStrings.DROP_OFF ||
|
|
||||||
this.appointmentType === AppointmentTypeStrings.IN_SHOP_OR_DROP_OFF
|
|
||||||
) {
|
|
||||||
var shops = mappedData.map((shop) => {
|
|
||||||
if (shop.value.length > 5 && shop.value.startsWith("00")) {
|
|
||||||
return shop.value.substring(1);
|
|
||||||
} else {
|
|
||||||
return shop.value;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var joinedShops = shops.join(",");
|
|
||||||
if (!joinedShops) {
|
|
||||||
joinedShops = "no-shops";
|
|
||||||
}
|
|
||||||
|
|
||||||
this.pushEventToGA(this.GaCategories.SERVICE_LOCATION, gaAction, joinedShops, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.answers.length === 0) {
|
|
||||||
this.answers = mappedData;
|
|
||||||
} else {
|
|
||||||
mappedData.forEach((shop) => {
|
|
||||||
this.answers.push(shop);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
await nextTick();
|
|
||||||
|
|
||||||
if (this.shopIndex == this.shopProviders.length) {
|
|
||||||
this.displaySeeMoreLocationsLink = false;
|
|
||||||
} else {
|
|
||||||
this.displaySeeMoreLocationsLink = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
await nextTick();
|
|
||||||
|
|
||||||
this.scrollToPageBottom();
|
|
||||||
},
|
},
|
||||||
async handleUpdate(selectedShopIndex = null) {
|
},
|
||||||
this.resetAnswers();
|
methods: {
|
||||||
if (selectedShopIndex >= 3) {
|
openModal() {
|
||||||
await this.getNextShopsFromList(selectedShopIndex + 1);
|
// Reset local state from parent
|
||||||
} else {
|
this.zipCodeData = null;
|
||||||
await this.getNextShopsFromList();
|
this.localShopProviderData = this.shopProviderDataFromParent;
|
||||||
await nextTick();
|
this.localZipCode = this.zipCodeFromParent;
|
||||||
}
|
// This will be set once the modal is open (updateSelectedAnswer()) to ensure that the
|
||||||
},
|
// correct answer is selected
|
||||||
copyModel(modelToCopy) {
|
this.localSelectedProviderNumber = null;
|
||||||
return {
|
|
||||||
state: modelToCopy.state,
|
const selectedProviderNumberFromParentIndex = this.shopProviders.findIndex(
|
||||||
zipCode: modelToCopy.zipCode,
|
(provider) => provider.providerNumber == this.selectedProviderNumberFromParent
|
||||||
};
|
|
||||||
},
|
|
||||||
resetAnswers() {
|
|
||||||
this.answers = [];
|
|
||||||
this.shopIndex = 0;
|
|
||||||
},
|
|
||||||
onModalOpened() {
|
|
||||||
const preSelectedIndex = this.shopProviders.findIndex(
|
|
||||||
(provider) => provider.providerNumber == this.preSelectedProviderNumber
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (preSelectedIndex > -1) {
|
if (selectedProviderNumberFromParentIndex > -1) {
|
||||||
this.shopIndex = Math.ceil((preSelectedIndex + 1) / 3) * 3;
|
this.numberOfShopsToDisplay =
|
||||||
|
Math.ceil((selectedProviderNumberFromParentIndex + 1) / 3) * 3;
|
||||||
} else {
|
} else {
|
||||||
this.shopIndex = 3;
|
this.numberOfShopsToDisplay = 3;
|
||||||
}
|
}
|
||||||
this.internalModel = this.copyModel(this.modelValue);
|
|
||||||
this.internalModel.zipCode = this.zipCode;
|
this.updateShopsForZip();
|
||||||
this.localShopProviderData = this.shopProviderData;
|
this.$refs.shopQuestionModal.openModal();
|
||||||
this.answers = [];
|
},
|
||||||
this.onSearchZip();
|
updateSelectedAnswer() {
|
||||||
this.focusOnZipInput();
|
this.localSelectedProviderNumber = this.selectedProviderNumberFromParent;
|
||||||
this.selectedProviderNumber = this.preSelectedProviderNumber;
|
},
|
||||||
if (
|
toTitleCase(str) {
|
||||||
(!this.selectedProviderNumber ||
|
return str.replace(/\w\S*/g, function (txt) {
|
||||||
!this.shopProviders.some(
|
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
|
||||||
(p) => p.providerNumber == this.selectedProviderNumber
|
});
|
||||||
)) &&
|
},
|
||||||
this.shopProviders.length > 0
|
updateShopListWithNextShops() {
|
||||||
) {
|
//gaAction = this.GaActions.SHOPS_FIRST_DISPLAYED; --This needs to actually fire on initial display, can never happen here
|
||||||
this.selectedProviderNumber = this.shopProviders[0].providerNumber;
|
this.numberOfShopsToDisplay += 3;
|
||||||
|
this.pushShopsDisplayedGAEvent(this.answers);
|
||||||
|
},
|
||||||
|
pushShopsDisplayedGAEvent(nextShopListAnswers) {
|
||||||
|
var shops = nextShopListAnswers.map((shop) => {
|
||||||
|
if (shop.value.length > 5 && shop.value.startsWith("00")) {
|
||||||
|
return shop.value.substring(1);
|
||||||
|
} else {
|
||||||
|
return shop.value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var joinedShops = shops.join(",");
|
||||||
|
if (!joinedShops) {
|
||||||
|
joinedShops = "no-shops";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.pushEventToGA(
|
||||||
|
this.GaCategories.SERVICE_LOCATION,
|
||||||
|
this.GaActions.MORE_LOCATIONS_CLICKED,
|
||||||
|
joinedShops,
|
||||||
|
true
|
||||||
|
);
|
||||||
},
|
},
|
||||||
closeModal() {
|
closeModal() {
|
||||||
this.modal.closeModal();
|
this.modal.closeModal();
|
||||||
},
|
},
|
||||||
onModalClosed() {
|
|
||||||
this.internalModel = this.copyModel(this.modelValue);
|
|
||||||
this.resetsOnZipInput();
|
|
||||||
},
|
|
||||||
resetsOnZipInput() {
|
|
||||||
this.resetAlerts();
|
|
||||||
},
|
|
||||||
async onSearchZip(event) {
|
async onSearchZip(event) {
|
||||||
if (event) {
|
event.preventDefault();
|
||||||
event.preventDefault();
|
this.zipCodeData = await this.getZipCodeData(this.localZipCode, "service-location");
|
||||||
event.stopPropagation();
|
// Add zipCode to zipCodeData as it does not come back from endpoint
|
||||||
}
|
this.zipCodeData.zipCode = this.localZipCode;
|
||||||
this.resetsOnZipInput();
|
if (this.zipCodeData.isValid) {
|
||||||
this.displayNoShopsAlert = false;
|
|
||||||
const zip = this.internalModel.zipCode;
|
|
||||||
const zipCodeData = await this.getZipCodeData(
|
|
||||||
this.internalModel.zipCode,
|
|
||||||
"service-location"
|
|
||||||
);
|
|
||||||
if (!zipCodeData.isValid) {
|
|
||||||
this.displayInvalidZipAlert = true;
|
|
||||||
//this.selectedProviderNumber = null;
|
|
||||||
this.focusOnZipInput();
|
|
||||||
this.resetModalButtonStyle();
|
|
||||||
} else {
|
|
||||||
if (this.isVehicleHeavyTruck) {
|
if (this.isVehicleHeavyTruck) {
|
||||||
// check the location endpoint to verify this zip can service a heavy truck
|
// check the location endpoint to verify this zip can service a heavy truck
|
||||||
const closestShops = await getClosestApplicableShops(
|
const closestShops = await getClosestApplicableShops(
|
||||||
this.internalModel.zipCode,
|
this.localZipCode,
|
||||||
this.carId,
|
this.carId,
|
||||||
"service-location"
|
"service-location"
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!closestShops || closestShops.providers?.length === 0) {
|
if (!closestShops || closestShops.providers?.length === 0) {
|
||||||
this.displayNoServiceAlert = true;
|
// We need an alert for heavy trucks
|
||||||
this.focusOnZipInput();
|
|
||||||
this.resetModalButtonStyle();
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
getShopProviderData(zip).then(async (result) => {
|
getShopProviderData(this.localZipCode).then(async (result) => {
|
||||||
if (this.displayNoShopsAlert === true) {
|
|
||||||
this.displayNoShopsAlert = false;
|
|
||||||
}
|
|
||||||
this.localShopProviderData = result.data;
|
this.localShopProviderData = result.data;
|
||||||
this.updateShopsForZip(this.internalModel.zipCode);
|
this.numberOfShopsToDisplay = 3;
|
||||||
if (this.appointmentType === AppointmentTypeStrings.MOBILE) {
|
this.updateShopsForZip();
|
||||||
this.selectedProvider = new Provider(
|
|
||||||
this.shopProviderData.mobileProviderNumber
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
this.selectedProvider = new Provider();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async updateShopsForZip(zipCode) {
|
updateShopsForZip() {
|
||||||
if (!this.appointmentType) {
|
if (!this.shopProviders.length) {
|
||||||
this.answers = [];
|
|
||||||
this.isLoadingShops = false;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.lastSearchedZip = zipCode;
|
|
||||||
if (!zipCode) {
|
|
||||||
this.isLoadingShops = false;
|
|
||||||
this.answers = [];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.isLoadingShops = true;
|
|
||||||
if (!zipCode || !this.shopProviders.length) {
|
|
||||||
this.answers = [];
|
|
||||||
this.displayNoShopsAlert = true;
|
|
||||||
this.displaySeeMoreLocationsLink = false;
|
|
||||||
this.isLoadingShops = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const selectedIndex = this.shopProviders.findIndex(
|
|
||||||
(provider) => provider.providerNumber == this.selectedProviderNumber
|
|
||||||
);
|
|
||||||
|
|
||||||
// If not found, only show the first 3 shops
|
|
||||||
if (selectedIndex === -1) {
|
|
||||||
this.shopIndex = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
const sortedShops = this.getShopsByZip(zipCode, this.shopProviders, this.shopIndex);
|
|
||||||
|
|
||||||
const toTitleCase = (str) => {
|
|
||||||
return str.replace(/\w\S*/g, function (txt) {
|
|
||||||
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const mappedData = sortedShops.map((shopProvider) => {
|
|
||||||
const streetAddress = toTitleCase(shopProvider.address.streetAddress);
|
|
||||||
const city = toTitleCase(shopProvider.address.city);
|
|
||||||
const state = shopProvider.address.state;
|
|
||||||
const zipCode = shopProvider.address.zipCode;
|
|
||||||
const distanceInMiles = Math.round(shopProvider.distanceInMiles * 2) / 2;
|
|
||||||
|
|
||||||
const startDate = new Date();
|
|
||||||
const endDate = new Date();
|
|
||||||
endDate.setDate(startDate.getDate() + 6);
|
|
||||||
|
|
||||||
const formattedStartDate = startDate.toISOString().split("T")[0];
|
|
||||||
const formattedEndDate = endDate.toISOString().split("T")[0];
|
|
||||||
|
|
||||||
return {
|
|
||||||
buttonLabel: city,
|
|
||||||
buttonLabelSubCopy: `${distanceInMiles} mi`,
|
|
||||||
buttonBodyCopy: `${streetAddress}, ${city}, ${state} ${zipCode}`,
|
|
||||||
additionalButtonData: {
|
|
||||||
availabilityRatingCallback: getAvailabilityRating,
|
|
||||||
startDate: formattedStartDate,
|
|
||||||
endDate: formattedEndDate,
|
|
||||||
shopAppointmentType: this.appointmentType,
|
|
||||||
},
|
|
||||||
value: shopProvider.providerNumber,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
this.answers = mappedData;
|
|
||||||
if (
|
if (
|
||||||
this.selectedProviderNumber &&
|
!this.answers.some(
|
||||||
!this.answers.some((a) => String(a.value) === String(this.selectedProviderNumber))
|
(a) => String(a.value) === String(this.localSelectedProviderNumber)
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
this.selectedProviderNumber = null;
|
this.localSelectedProviderNumber = null;
|
||||||
}
|
}
|
||||||
this.shopIndex = mappedData.length;
|
// I think we should call our new pushGA Event here with new mapped data
|
||||||
this.displaySeeMoreLocationsLink = this.shopIndex < this.shopProviders.length;
|
|
||||||
if (mappedData.length > 0) {
|
|
||||||
this.lastSuccessfulZip = zipCode;
|
|
||||||
}
|
|
||||||
this.isLoadingShops = false;
|
|
||||||
this.displayNoShopsAlert = this.answers.length === 0;
|
|
||||||
},
|
|
||||||
resetModalButtonStyle() {
|
|
||||||
this.modal.resetButtonStyle();
|
|
||||||
},
|
|
||||||
onInputIdAssigned(inputId) {
|
|
||||||
this.serviceZipCodeTextInputId = inputId;
|
|
||||||
},
|
|
||||||
focusOnZipInput() {
|
|
||||||
const input = document.getElementById(this.serviceZipCodeTextInputId);
|
|
||||||
input?.focus();
|
|
||||||
},
|
|
||||||
resetAlerts() {
|
|
||||||
this.displayInvalidZipAlert = false;
|
|
||||||
this.displayNoServiceAlert = false;
|
|
||||||
this.displayNoShopsAlert = false;
|
|
||||||
},
|
},
|
||||||
async setZipCodeAndShop() {
|
async setZipCodeAndShop() {
|
||||||
if (
|
|
||||||
this.displayInvalidZipAlert ||
|
|
||||||
this.displayNoShopsAlert ||
|
|
||||||
!this.selectedProviderNumber
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const zipToEmit = this.lastSearchedZip;
|
|
||||||
|
|
||||||
const selectedProvider = this.shopProviders.find(
|
const selectedProvider = this.shopProviders.find(
|
||||||
(provider) => provider.providerNumber == this.selectedProviderNumber
|
(provider) => provider.providerNumber == this.localSelectedProviderNumber
|
||||||
);
|
);
|
||||||
this.resetAlerts();
|
const serviceZipCode = selectedProvider.address.zipCode;
|
||||||
|
|
||||||
const zipCodeData = await this.getZipCodeData(zipToEmit, "service-location");
|
// retrieve serviceability details
|
||||||
|
const serviceabilityDetails = await getServiceabilityDetails(
|
||||||
|
serviceZipCode,
|
||||||
|
null,
|
||||||
|
"service-location"
|
||||||
|
);
|
||||||
|
const shopQuestionPopUpData = {
|
||||||
|
serviceabilityDetails: serviceabilityDetails.data,
|
||||||
|
zipCodeData: this.zipCodeData,
|
||||||
|
shopProviderData: this.localShopProviderData,
|
||||||
|
selectedProviderNumber: this.localSelectedProviderNumber,
|
||||||
|
};
|
||||||
|
this.$emit("shop-selected", shopQuestionPopUpData);
|
||||||
|
|
||||||
if (!zipCodeData.isValid) {
|
this.closeModal();
|
||||||
this.displayInvalidZipAlert = true;
|
|
||||||
this.focusOnZipInput();
|
|
||||||
this.resetModalButtonStyle();
|
|
||||||
} else {
|
|
||||||
if (this.isVehicleHeavyTruck) {
|
|
||||||
// check the location endpoint to verify this zip can service a heavy truck
|
|
||||||
const closestShops = await getClosestApplicableShops(
|
|
||||||
selectedProvider.address.zipCode,
|
|
||||||
this.carId,
|
|
||||||
"service-location"
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!closestShops || closestShops.providers?.length === 0) {
|
|
||||||
this.displayNoServiceAlert = true;
|
|
||||||
this.focusOnZipInput();
|
|
||||||
this.resetModalButtonStyle();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
selectedProvider.address.zipCodeCtu = zipCodeData.zipCodeCtu;
|
|
||||||
|
|
||||||
// retrieve mobile fee part
|
|
||||||
const serviceZipCode = selectedProvider.address.zipCode;
|
|
||||||
const mobileFeePart = await getPricedMobileFeePart(
|
|
||||||
serviceZipCode,
|
|
||||||
"service-location"
|
|
||||||
);
|
|
||||||
|
|
||||||
// retrieve recycle fee part
|
|
||||||
const recycleFeePart = await getPricedRecycleFeePart(
|
|
||||||
serviceZipCode,
|
|
||||||
"service-location"
|
|
||||||
);
|
|
||||||
|
|
||||||
// retrieve serviceability details
|
|
||||||
const serviceabilityDetails = await getServiceabilityDetails(
|
|
||||||
serviceZipCode,
|
|
||||||
null,
|
|
||||||
"service-location"
|
|
||||||
);
|
|
||||||
|
|
||||||
const billToAccountNumber = await getBillToAccountNumber(
|
|
||||||
selectedProvider.address.zipCodeCtu
|
|
||||||
);
|
|
||||||
|
|
||||||
// update content related to service zip code
|
|
||||||
this.$emit("updated-mobile-fee-part", mobileFeePart);
|
|
||||||
this.$emit("updated-recycle-fee-part", recycleFeePart);
|
|
||||||
this.$emit("updated-serviceability", serviceabilityDetails.data);
|
|
||||||
this.$emit("updated-contains-military-base", zipCodeData.containsMilitaryBase);
|
|
||||||
this.$emit("updated-bill-to-account-number", billToAccountNumber);
|
|
||||||
this.$emit("updated-zipcode-ctu", selectedProvider.address.zipCodeCtu);
|
|
||||||
|
|
||||||
this.$emit("update:modelValue", this.selectedValue);
|
|
||||||
this.$emit("update-shop-provider-data", this.localShopProviderData);
|
|
||||||
this.internalModel.state = zipCodeData.state;
|
|
||||||
this.$emit("shop-selected", {
|
|
||||||
providerNumber: selectedProvider?.providerNumber,
|
|
||||||
address: selectedProvider?.address,
|
|
||||||
searchedZip: zipToEmit,
|
|
||||||
searchedState: this.internalModel.state,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.closeModal();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
getShopsByZip(zipCode, shops, numberToGet = 3) {
|
|
||||||
return shops
|
|
||||||
.filter(
|
|
||||||
(shop) => shop.address && shop.address.zipCode && shop.address.zipCode !== ""
|
|
||||||
)
|
|
||||||
.sort((a, b) => a.distanceInMiles - b.distanceInMiles)
|
|
||||||
.slice(0, numberToGet);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
shopProviderData: {
|
|
||||||
immediate: true,
|
|
||||||
handler(newVal) {
|
|
||||||
this.localShopProviderData = newVal;
|
|
||||||
this.hasLoadedShopData = true;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
|
|
|
||||||
|
|
@ -49,8 +49,6 @@ import { errorMessages } from "@/constants/error-messages";
|
||||||
import baseMixin from "@/mixins/base-mixin.js";
|
import baseMixin from "@/mixins/base-mixin.js";
|
||||||
import { nextTick } from "vue";
|
import { nextTick } from "vue";
|
||||||
|
|
||||||
import { getAvailabilityRating } from "@/layouts/service-location/helpers/service-location-helper/service-location-helper";
|
|
||||||
|
|
||||||
import { Provider } from "@/layouts/service-location/classes/provider";
|
import { Provider } from "@/layouts/service-location/classes/provider";
|
||||||
import { AppointmentTypeStrings } from "@/constants/schedule-constants";
|
import { AppointmentTypeStrings } from "@/constants/schedule-constants";
|
||||||
|
|
||||||
|
|
@ -109,17 +107,7 @@ export default {
|
||||||
return this.getCmsContent("ShowMoreShopsLinkWidget", "Text");
|
return this.getCmsContent("ShowMoreShopsLinkWidget", "Text");
|
||||||
},
|
},
|
||||||
additionalButtonData() {
|
additionalButtonData() {
|
||||||
const startDate = new Date();
|
|
||||||
const endDate = new Date();
|
|
||||||
endDate.setDate(startDate.getDate() + 6);
|
|
||||||
|
|
||||||
const formattedStartDate = startDate.toISOString().split("T")[0];
|
|
||||||
const formattedEndDate = endDate.toISOString().split("T")[0];
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
availabilityRatingCallback: getAvailabilityRating,
|
|
||||||
startDate: formattedStartDate,
|
|
||||||
endDate: formattedEndDate,
|
|
||||||
shopAppointmentType: this.selectedAppointmentType,
|
shopAppointmentType: this.selectedAppointmentType,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue