Merge remote-tracking branch 'origin/release/2025.07.10' into feature/Digital/CASH-814

This commit is contained in:
Scott Kiener 2025-07-03 10:02:02 -04:00
commit 7bb1060d91
10 changed files with 1155 additions and 84 deletions

View file

@ -1354,23 +1354,6 @@ export default {
}
}
}
.inshop-version {
color: limegreen !important; // TODO - THIS IS TEMPORARY CODE; TO BE REMOVED AS PART OF CASH-845
}
.mobile-version {
color: red !important; // TODO - THIS IS TEMPORARY CODE; TO BE REMOVED AS PART OF CASH-845
}
.radio-wrapper.inshop-version {
&.selectable-day label span {
color: limegreen !important; // TODO - THIS IS TEMPORARY CODE; TO BE REMOVED AS PART OF CASH-845
}
}
.radio-wrapper.mobile-version {
&.selectable-day label span {
color: red !important; // TODO - THIS IS TEMPORARY CODE; TO BE REMOVED AS PART OF CASH-845
}
}
}
}
:deep(.timeslots .button-inner-wrapper) {

View file

@ -50,6 +50,7 @@
:autocomplete="autocomplete" />
<button
v-if="includeSearchIcon"
class="search-icon-button"
type="submit"
aria-label="Search button"
@click="focusSearchInput" />
@ -185,6 +186,7 @@ export default {
// Focus cursor in input when search icon is clicked
const field = document.querySelector("input");
field.focus();
this.$emit("search-icon-click");
},
async imageChanged(e) {
let file = e.target.files[0];

View file

@ -9,6 +9,7 @@ import { GaCategories, GaActions, GaLabels } from "@/constants/analytics";
import { getSessionKeyValue } from "@/helpers/heritage-integration/cookie-helper";
import { headerKeys } from "@/constants/header-keys";
import axiosResponseInterceptorMessages from "@/constants/axios-response-interceptor-messages.js";
import { endpoints } from "@/constants/endpoints";
// Add a response interceptor for global axios error handing.
axios.interceptors.response.use(
@ -100,7 +101,7 @@ export default {
return resolve(response);
},
(error) => {
if (endpoint.toLowerCase().includes("/digitalconsumer-log")) {
if (endpoint.toLowerCase().includes(endpoints.LogDigitalConsumer.url)) {
return resolve({ data: null, error: "Ignore errors when logging" });
} else {
if (logApiCall) {

View file

@ -101,11 +101,6 @@ export default {
baseMixin.methods.filterOutFees(allLineItems)
);
let vapsLineItemsForSelectedPackage = this.lineItems.vaps;
vapsLineItemsForSelectedPackage.forEach((lineItem) => {
price += baseMixin.methods.getTotalLineItemPrice(lineItem);
});
if (this.lineItems.promos) {
let allLineItems = getArrayOfAllLineItemsAndChildParts(this.lineItems);
const promos = getPromosThatMatchLineItemsOnOrder(
@ -119,12 +114,6 @@ export default {
return price;
},
afterpayPrice() {
let price = this.getTierOnePackagePrice;
const adjusted = (price / 4).toFixed(2);
return `$${adjusted}`;
},
showAfterpayExtendedPayOption() {
return (
this.afterpayExtendedPayOptionThreshold &&
@ -175,15 +164,13 @@ export default {
> div:last-of-type {
color: $gray-650;
text-align: left;
@include media-breakpoint-down(md) {
padding-top: 1rem;
}
@include media-breakpoint-up(md) {
padding-left: 1rem;
&#extended-pay-option {
max-width: 28rem;
}
max-width: 26rem;
}
}

View file

@ -76,6 +76,7 @@ import {
} from "@/layouts/schedule/helpers/schedule-helper";
import {
AppointmentTypeStrings,
RouteCodeFlags,
PREMIUM_FEE_PART_TYPE,
PRICING_BY_DAY_PART_TYPE,
} from "@/constants/schedule-constants";
@ -447,7 +448,7 @@ export default {
if (this.isMobileSelected) {
return "Mobile";
} else {
return "Inshop";
return "InshopOrDropoff";
}
},
getServiceMinutesMin() {
@ -569,10 +570,14 @@ export default {
timeSlotInfo.timeSlot.date
)}`;
if (this.appointmentType === AppointmentTypeStrings.IN_SHOP) {
navbarButtonText += ` at ${this.getDisplayTextForMilitaryTime(
timeSlotInfo.timeSlot.startTime
)}`;
if (this.appointmentType === AppointmentTypeStrings.IN_SHOP_OR_DROP_OFF) {
if (
!timeSlotInfo.timeSlot.routeCode.includes(RouteCodeFlags.ALL_DAY_DROP_OFF)
) {
navbarButtonText += ` at ${this.getDisplayTextForMilitaryTime(
timeSlotInfo.timeSlot.startTime
)}`;
}
} else if (
this.appointmentType === AppointmentTypeStrings.MOBILE &&
!timeSlotInfo.isPremiumAppointment

View file

@ -33,7 +33,7 @@
class="mt-4 timeslots"
:class="[
isMobileAppointment ? 'is-mobile-appointment' : '',
isDropOffAppointment ? 'is-drop-off-appointment' : '',
isInshopOrDropOffAppointment ? 'is-inshop-or-drop-off-appointment' : '',
]"
:answers="availableTimeSlots"
groupName="chooseTimeSlot"
@ -354,7 +354,9 @@ export default {
if (this.appointmentType === AppointmentTypeStrings.MOBILE) {
return this.getAvailableTimeSlotsForMobile(this.timeSlotsForSelectedDate.timeSlots);
} else {
return this.getAvailableTimeSlotsForInshop(this.timeSlotsForSelectedDate.timeSlots);
return this.getAvailableTimeSlotsForInshopOrDropOff(
this.timeSlotsForSelectedDate.timeSlots
);
}
},
answersForDropOffQuestion() {
@ -408,8 +410,8 @@ export default {
isMobileAppointment() {
return this.appointmentType === AppointmentTypeStrings.MOBILE;
},
isDropOffAppointment() {
return this.appointmentType === AppointmentTypeStrings.DROP_OFF;
isInshopOrDropOffAppointment() {
return this.appointmentType === AppointmentTypeStrings.IN_SHOP_OR_DROP_OFF;
},
shouldDisplayDropOffAlert() {
return this.isDropOffRouteCode(this.selectedAnswerForDropOff);
@ -472,6 +474,26 @@ export default {
};
});
},
getAvailableTimeSlotsForInshopOrDropOff(timeSlotsForSelectedDate) {
const availableTimeSlots = timeSlotsForSelectedDate.map((timeSlot) => {
let buttonLabelValue;
if (timeSlot.id.includes(RouteCodeFlags.ALL_DAY_DROP_OFF)) {
buttonLabelValue = this.dropoffButtonText;
} else if (timeSlot.id.includes(RouteCodeFlags.OVERNIGHT_DROP_OFF)) {
buttonLabelValue = this.overnightDropoffButtonText;
} else if (this.isSameDay) {
buttonLabelValue = this.sameDayDropoffButtonText;
} else {
buttonLabelValue = militaryToTwelveHourTime(timeSlot.startTime);
}
return {
value: timeSlot.id,
buttonLabel: buttonLabelValue,
};
});
return availableTimeSlots;
},
getAvailableTimeSlotsForMobile(timeSlotsForSelectedDate) {
const availableTimeSlots = timeSlotsForSelectedDate.map((timeSlot) => {
const readableTime = `${militaryToTwelveHourTime(timeSlot.startTime)} - ${militaryToTwelveHourTime(

View file

@ -219,7 +219,13 @@ describe("service-location.vue", () => {
// Arrange
const { wrapper } = setupMocks({});
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
/* ******NOTE: if we ever want to revert back to the old way of service location using shopQuestion instead of shopQuestionPopup uncomment all 30 of the lines in this file that read - wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn(); - , and commment all of the blocks of code referencing shopQuestionPopup directly under them. Also remove reference to shopQuestionPopup in the setupMocks function*/
//wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
const shopQuestionPopupComponent = wrapper.findComponent({ ref: "shopQuestionPopup" });
if (shopQuestionPopupComponent.exists()) {
shopQuestionPopupComponent.vm.initializeComponent = jest.fn();
}
const mobileFeePart = {
partNumber: "MOBILE FEE",
@ -387,7 +393,13 @@ describe("service-location.vue", () => {
const { wrapper } = setupMocks({});
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
//wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
const shopQuestionPopupComponent = wrapper.findComponent({
ref: "shopQuestionPopup",
});
if (shopQuestionPopupComponent.exists()) {
shopQuestionPopupComponent.vm.initializeComponent = jest.fn();
}
// Act
await serviceLocation.beforeRouteEnter.call(
@ -414,7 +426,13 @@ describe("service-location.vue", () => {
const { wrapper } = setupMocks({});
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
//wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
const shopQuestionPopupComponent = wrapper.findComponent({
ref: "shopQuestionPopup",
});
if (shopQuestionPopupComponent.exists()) {
shopQuestionPopupComponent.vm.initializeComponent = jest.fn();
}
// Act
await serviceLocation.beforeRouteEnter.call(
@ -441,7 +459,13 @@ describe("service-location.vue", () => {
const { wrapper } = setupMocks({});
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
//wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
const shopQuestionPopupComponent = wrapper.findComponent({
ref: "shopQuestionPopup",
});
if (shopQuestionPopupComponent.exists()) {
shopQuestionPopupComponent.vm.initializeComponent = jest.fn();
}
// Act
await serviceLocation.beforeRouteEnter.call(
@ -468,7 +492,13 @@ describe("service-location.vue", () => {
const { wrapper } = setupMocks({});
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
//wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
const shopQuestionPopupComponent = wrapper.findComponent({
ref: "shopQuestionPopup",
});
if (shopQuestionPopupComponent.exists()) {
shopQuestionPopupComponent.vm.initializeComponent = jest.fn();
}
// Act
await serviceLocation.beforeRouteEnter.call(
@ -495,7 +525,13 @@ describe("service-location.vue", () => {
const { wrapper } = setupMocks({});
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
//wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
const shopQuestionPopupComponent = wrapper.findComponent({
ref: "shopQuestionPopup",
});
if (shopQuestionPopupComponent.exists()) {
shopQuestionPopupComponent.vm.initializeComponent = jest.fn();
}
// Act
await serviceLocation.beforeRouteEnter.call(
@ -523,7 +559,13 @@ describe("service-location.vue", () => {
const { wrapper } = setupMocks({});
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
//wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
const shopQuestionPopupComponent = wrapper.findComponent({
ref: "shopQuestionPopup",
});
if (shopQuestionPopupComponent.exists()) {
shopQuestionPopupComponent.vm.initializeComponent = jest.fn();
}
// Act
await serviceLocation.beforeRouteEnter.call(
@ -551,7 +593,13 @@ describe("service-location.vue", () => {
const { wrapper } = setupMocks({});
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
//wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
const shopQuestionPopupComponent = wrapper.findComponent({
ref: "shopQuestionPopup",
});
if (shopQuestionPopupComponent.exists()) {
shopQuestionPopupComponent.vm.initializeComponent = jest.fn();
}
// Act
await serviceLocation.beforeRouteEnter.call(
@ -579,7 +627,13 @@ describe("service-location.vue", () => {
const { wrapper } = setupMocks({});
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
//wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
const shopQuestionPopupComponent = wrapper.findComponent({
ref: "shopQuestionPopup",
});
if (shopQuestionPopupComponent.exists()) {
shopQuestionPopupComponent.vm.initializeComponent = jest.fn();
}
// Act
await serviceLocation.beforeRouteEnter.call(
@ -607,7 +661,13 @@ describe("service-location.vue", () => {
const { wrapper } = setupMocks({});
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
//wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
const shopQuestionPopupComponent = wrapper.findComponent({
ref: "shopQuestionPopup",
});
if (shopQuestionPopupComponent.exists()) {
shopQuestionPopupComponent.vm.initializeComponent = jest.fn();
}
// Act
await serviceLocation.beforeRouteEnter.call(
@ -635,7 +695,13 @@ describe("service-location.vue", () => {
const { wrapper } = setupMocks({});
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
//wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
const shopQuestionPopupComponent = wrapper.findComponent({
ref: "shopQuestionPopup",
});
if (shopQuestionPopupComponent.exists()) {
shopQuestionPopupComponent.vm.initializeComponent = jest.fn();
}
// Act
await serviceLocation.beforeRouteEnter.call(
@ -663,7 +729,13 @@ describe("service-location.vue", () => {
const { wrapper } = setupMocks({});
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
//wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
const shopQuestionPopupComponent = wrapper.findComponent({
ref: "shopQuestionPopup",
});
if (shopQuestionPopupComponent.exists()) {
shopQuestionPopupComponent.vm.initializeComponent = jest.fn();
}
// Act
await serviceLocation.beforeRouteEnter.call(
@ -691,7 +763,13 @@ describe("service-location.vue", () => {
const { wrapper } = setupMocks({});
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
//wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
const shopQuestionPopupComponent = wrapper.findComponent({
ref: "shopQuestionPopup",
});
if (shopQuestionPopupComponent.exists()) {
shopQuestionPopupComponent.vm.initializeComponent = jest.fn();
}
// Act
await serviceLocation.beforeRouteEnter.call(
@ -719,7 +797,13 @@ describe("service-location.vue", () => {
const { wrapper } = setupMocks({});
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
//wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
const shopQuestionPopupComponent = wrapper.findComponent({
ref: "shopQuestionPopup",
});
if (shopQuestionPopupComponent.exists()) {
shopQuestionPopupComponent.vm.initializeComponent = jest.fn();
}
// Act
await serviceLocation.beforeRouteEnter.call(
@ -747,7 +831,13 @@ describe("service-location.vue", () => {
const { wrapper } = setupMocks({});
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
//wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
const shopQuestionPopupComponent = wrapper.findComponent({
ref: "shopQuestionPopup",
});
if (shopQuestionPopupComponent.exists()) {
shopQuestionPopupComponent.vm.initializeComponent = jest.fn();
}
// Act
await serviceLocation.beforeRouteEnter.call(
@ -775,7 +865,13 @@ describe("service-location.vue", () => {
const { wrapper } = setupMocks({});
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
//wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
const shopQuestionPopupComponent = wrapper.findComponent({
ref: "shopQuestionPopup",
});
if (shopQuestionPopupComponent.exists()) {
shopQuestionPopupComponent.vm.initializeComponent = jest.fn();
}
// Act
await serviceLocation.beforeRouteEnter.call(
@ -806,7 +902,13 @@ describe("service-location.vue", () => {
const { wrapper } = setupMocks({});
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
//wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
const shopQuestionPopupComponent = wrapper.findComponent({
ref: "shopQuestionPopup",
});
if (shopQuestionPopupComponent.exists()) {
shopQuestionPopupComponent.vm.initializeComponent = jest.fn();
}
// Act
await serviceLocation.beforeRouteEnter.call(
@ -833,7 +935,13 @@ describe("service-location.vue", () => {
const { wrapper } = setupMocks({});
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
//wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
const shopQuestionPopupComponent = wrapper.findComponent({
ref: "shopQuestionPopup",
});
if (shopQuestionPopupComponent.exists()) {
shopQuestionPopupComponent.vm.initializeComponent = jest.fn();
}
// Act
await serviceLocation.beforeRouteEnter.call(
@ -860,7 +968,13 @@ describe("service-location.vue", () => {
const { wrapper } = setupMocks({});
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
//wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
const shopQuestionPopupComponent = wrapper.findComponent({
ref: "shopQuestionPopup",
});
if (shopQuestionPopupComponent.exists()) {
shopQuestionPopupComponent.vm.initializeComponent = jest.fn();
}
// Act
await serviceLocation.beforeRouteEnter.call(
@ -888,7 +1002,13 @@ describe("service-location.vue", () => {
const { wrapper } = setupMocks({});
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
//wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
const shopQuestionPopupComponent = wrapper.findComponent({
ref: "shopQuestionPopup",
});
if (shopQuestionPopupComponent.exists()) {
shopQuestionPopupComponent.vm.initializeComponent = jest.fn();
}
// Act
await serviceLocation.beforeRouteEnter.call(
@ -916,7 +1036,13 @@ describe("service-location.vue", () => {
const { wrapper } = setupMocks({});
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
//wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
const shopQuestionPopupComponent = wrapper.findComponent({
ref: "shopQuestionPopup",
});
if (shopQuestionPopupComponent.exists()) {
shopQuestionPopupComponent.vm.initializeComponent = jest.fn();
}
// Act
await serviceLocation.beforeRouteEnter.call(
@ -944,7 +1070,13 @@ describe("service-location.vue", () => {
const { wrapper } = setupMocks({});
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
//wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
const shopQuestionPopupComponent = wrapper.findComponent({
ref: "shopQuestionPopup",
});
if (shopQuestionPopupComponent.exists()) {
shopQuestionPopupComponent.vm.initializeComponent = jest.fn();
}
// Act
await serviceLocation.beforeRouteEnter.call(
@ -974,7 +1106,13 @@ describe("service-location.vue", () => {
const { wrapper } = setupMocks({});
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
//wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
const shopQuestionPopupComponent = wrapper.findComponent({
ref: "shopQuestionPopup",
});
if (shopQuestionPopupComponent.exists()) {
shopQuestionPopupComponent.vm.initializeComponent = jest.fn();
}
// Act
await serviceLocation.beforeRouteEnter.call(
@ -1002,7 +1140,13 @@ describe("service-location.vue", () => {
const { wrapper } = setupMocks({});
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
//wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
const shopQuestionPopupComponent = wrapper.findComponent({
ref: "shopQuestionPopup",
});
if (shopQuestionPopupComponent.exists()) {
shopQuestionPopupComponent.vm.initializeComponent = jest.fn();
}
// Act
await serviceLocation.beforeRouteEnter.call(
@ -1030,7 +1174,13 @@ describe("service-location.vue", () => {
const { wrapper } = setupMocks({});
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
//wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
const shopQuestionPopupComponent = wrapper.findComponent({
ref: "shopQuestionPopup",
});
if (shopQuestionPopupComponent.exists()) {
shopQuestionPopupComponent.vm.initializeComponent = jest.fn();
}
// Act
await serviceLocation.beforeRouteEnter.call(
@ -1058,7 +1208,13 @@ describe("service-location.vue", () => {
const { wrapper } = setupMocks({});
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
//wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
const shopQuestionPopupComponent = wrapper.findComponent({
ref: "shopQuestionPopup",
});
if (shopQuestionPopupComponent.exists()) {
shopQuestionPopupComponent.vm.initializeComponent = jest.fn();
}
// Act
await serviceLocation.beforeRouteEnter.call(
@ -1086,7 +1242,13 @@ describe("service-location.vue", () => {
const { wrapper } = setupMocks({});
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
//wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
const shopQuestionPopupComponent = wrapper.findComponent({
ref: "shopQuestionPopup",
});
if (shopQuestionPopupComponent.exists()) {
shopQuestionPopupComponent.vm.initializeComponent = jest.fn();
}
// Act
await serviceLocation.beforeRouteEnter.call(
@ -1114,7 +1276,13 @@ describe("service-location.vue", () => {
const { wrapper } = setupMocks({});
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
//wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
const shopQuestionPopupComponent = wrapper.findComponent({
ref: "shopQuestionPopup",
});
if (shopQuestionPopupComponent.exists()) {
shopQuestionPopupComponent.vm.initializeComponent = jest.fn();
}
// Act
await serviceLocation.beforeRouteEnter.call(
@ -1142,7 +1310,13 @@ describe("service-location.vue", () => {
const { wrapper } = setupMocks({});
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
//wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
const shopQuestionPopupComponent = wrapper.findComponent({
ref: "shopQuestionPopup",
});
if (shopQuestionPopupComponent.exists()) {
shopQuestionPopupComponent.vm.initializeComponent = jest.fn();
}
// Act
await serviceLocation.beforeRouteEnter.call(
@ -1172,7 +1346,13 @@ describe("service-location.vue", () => {
const { wrapper } = setupMocks({});
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
//wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
const shopQuestionPopupComponent = wrapper.findComponent({
ref: "shopQuestionPopup",
});
if (shopQuestionPopupComponent.exists()) {
shopQuestionPopupComponent.vm.initializeComponent = jest.fn();
}
// Act
await serviceLocation.beforeRouteEnter.call(
@ -1199,5 +1379,12 @@ function setupMocks({ mountOptionsMockData = {} }) {
const mountOptions = getMountOptions(mountOptionsMockData);
const wrapper = shallowMount(serviceLocation, mountOptions);
wrapper.vm.setCmsContent = baseMixin.methods.setCmsContent;
if (!wrapper.vm.shopProviderData) {
wrapper.vm.shopProviderData = { shopProviders: [] };
}
const shopQuestionPopupComponent = wrapper.findComponent({ ref: "shopQuestionPopup" });
if (shopQuestionPopupComponent.exists()) {
shopQuestionPopupComponent.vm.initializeComponent = jest.fn();
}
return { wrapper, apiPromise };
}

View file

@ -89,6 +89,19 @@
</div>
<div class="row justify-content-center">
<div class="col-md-6 col-xl-4">
<button-question
v-if="selectedAppointmentType == appointmentTypeStrings.IN_SHOP"
class="shop-question-button"
ref="buttonQuestion"
:answers="selectedShopAnswer"
:modelValue="selectedProvider?.providerNumber"
:isMultiSelect="false"
:readonly="true"
:questionText="questionText"
buttonTypeString="shopListButton"
:buttonTypeObject="shopListButton"
groupName="chooseShop"
textPosition="text-start" />
<div class="text-center">
<textBlock
v-if="mobileFeeApplies && isMobileSelected"
@ -97,14 +110,24 @@
typeStyle="caption"
class="ps-4 pe-4 mt-4" />
</div>
<shopQuestion
ref="shopQuestion"
v-show="isShopQuestionDisplayed"
v-model="selectedProvider"
<shopQuestionPopup
modalWidgetName="ShopQuestionPopupWidget"
:selectedAppointmentType="selectedAppointmentType"
:modelValue="{
zipCode: zipCode,
providerNumber: selectedProvider?.providerNumber,
}"
:preselectedProviderNumber="selectedProvider?.providerNumber"
:shopProviderData="shopProviderData"
:isServiceableInshop="isServiceableInshop"
@updated-serviceability="setServiceabilityDetails"
@shop-selected="onShopSelected"
@update:modelValue="onShopModelUpdated"
@updated-zipcode-ctu="setZipcodeCtu"
@update-shop-provider-data="shopProviderData = $event"
:zipCode="zipCode"
:isDisplayed="isShopQuestionDisplayed"
cmsWidgetName="ShopQuestionWidget" />
cmsWidgetName="YourSafeliteShopWidget" />
<contentGroupModal ref="RecalModal" cmsWidgetName="RecalModal" />
@ -126,6 +149,10 @@ import alert from "@/ux-components/alert/alert";
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 shopQuestion from "@/layouts/service-location/shop-question/shop-question";
import shopQuestionPopup from "@/layouts/service-location/shop-question/shop-question-popup";
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 { getAvailabilityRating } from "@/layouts/service-location/helpers/service-location-helper/service-location-helper";
import funnelHeader from "@/fmg-components/funnel-header/funnel-header";
import navbar from "@/fmg-components/nav-bar/nav-bar";
@ -212,6 +239,7 @@ export default {
shopProviderData: null,
navigatingForward: false,
isMobileAddressValid: true,
shopListButton: shopListButton,
};
},
async beforeRouteEnter(to, from, next) {
@ -421,6 +449,57 @@ export default {
isForwardActionDisabled() {
return this.displayNoShopsAlert || !this.isMobileAddressValid;
},
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 {
availabilityRatingCallback: getAvailabilityRating,
startDate: formattedStartDate,
endDate: formattedEndDate,
shopAppointmentType: this.selectedAppointmentType,
};
},
selectedShopAnswer() {
const toTitleCase = (str) => {
if (!str) return "";
return str.replace(/\w\S*/g, function (txt) {
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
});
};
if (this.selectedProvider && this.selectedProvider.address) {
const provider = this.shopProviderData?.shopProviders?.find(
(p) => p.providerNumber === this.selectedProvider?.providerNumber
);
const streetAddress = toTitleCase(this.selectedProvider.address.streetAddress);
const city = toTitleCase(this.selectedProvider.address.city);
const state = this.selectedProvider.address.state;
const zipCode = this.selectedProvider.address.zipCode;
const distanceInMiles = provider ? Math.round(provider.distanceInMiles * 2) / 2 : 0;
return [
{
buttonLabel: `${city}`,
buttonLabelSubCopy: `${distanceInMiles} mi`,
buttonBodyCopy: `${streetAddress}, ${city}, ${state} ${zipCode}`,
additionalButtonData: this.additionalButtonData,
value: this.selectedProvider.providerNumber,
},
];
}
return [];
},
questionText() {
return this.getCmsContent("YourSafeliteShopWidget", "QuestionText");
},
appointmentTypeStrings() {
return AppointmentTypeStrings;
},
},
methods: {
arePagePrerequisitesValid() {
@ -669,8 +748,6 @@ export default {
this.selectedProvider.address.zipCode = null;
this.selectedProvider.address.zipCodeCtu = null;
} else {
// if we have a provider.zipCodeCtu that's different than the servicelocation.zipCodeCtu then they
// may have selected a shop in a different ctu. change the servicelocation zip/ctu if different
if (this.zipCodeCtu != this.selectedProvider.address.zipCodeCtu) {
ctu = this.selectedProvider.address.zipCodeCtu;
}
@ -756,6 +833,60 @@ export default {
startTime: "08:00",
};
},
onShopSelected(providerObject) {
const current = this.shopProviderData?.shopProviders?.find(
(p) => p.providerNumber === this.selectedProvider?.providerNumber
);
if (current) {
this.selectedProvider = current;
}
let provider = this.shopProviderData?.shopProviders?.find(
(p) => p.providerNumber === providerObject?.providerNumber
);
if (!provider && this.shopProviderData?.shopProviders?.length > 0) {
provider = this.shopProviderData.shopProviders[0];
}
this.selectedProvider = provider;
if (provider && provider.address) {
this.zipCode = provider.address.zipCode;
this.state = provider.address.state;
}
if (
!this.selectedProvider ||
!this.shopProviderData?.shopProviders?.some(
(p) => p.providerNumber === this.selectedProvider?.providerNumber
)
) {
this.selectedProvider = this.shopProviderData?.shopProviders?.[0];
}
if (providerObject && providerObject.address) {
this.zipCode = providerObject.address.zipCode;
this.state = providerObject.address.state;
}
},
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;
}
}
},
},
watch: {
zipCode: {
@ -768,7 +899,7 @@ export default {
this.shopProviderData.mobileProviderNumber
);
} else {
this.selectedProvider = new Provider();
this.isMobileAddressValid = true;
}
});
}
@ -781,7 +912,7 @@ export default {
this.shopProviderData.mobileProviderNumber
);
} else {
this.selectedProvider = new Provider();
this.selectedProvider = this.shopProviderData.shopProviders[0];
}
},
},
@ -796,7 +927,8 @@ export default {
Form,
loadingModal,
contentGroupModal,
shopQuestion,
shopQuestionPopup,
buttonQuestion,
textBlock,
},
};
@ -822,4 +954,8 @@ export default {
color: $black;
}
}
.shop-question-button .list-button-content {
background: $blue-100 !important;
box-shadow: 0 0 0 1px $primary !important;
}
</style>

View file

@ -7,9 +7,11 @@
questionAlignment="center"
cornerStyle="rounded"
mask="#####"
:includeSearchIcon="includeSearchIcon"
@search-icon-click="$emit('search-icon-click')"
:displayQuestionText="false"
isRequired
validationRules="zip-required|zip-format" />
:isRequired="isRequired"
:validationRules="computedValidationRules" />
</template>
<script>
@ -31,6 +33,14 @@ export default {
serviceZipCode: String,
},
cmsWidgetName: String,
isRequired: {
type: Boolean,
default: true,
},
includeSearchIcon: {
type: Boolean,
default: false,
},
},
computed: {
value: {
@ -41,6 +51,9 @@ export default {
this.$emit("update:modelValue", newValue);
},
},
computedValidationRules() {
return this.isRequired ? "zip-required|zip-format" : "zip-format";
},
},
components: {
textboxQuestion,

View file

@ -0,0 +1,735 @@
<template>
<transition name="fade" mode="out-in">
<div v-if="isDisplayed" class="shop-question" aria-live="polite">
<alert
ref="alertDropoffInformation"
v-if="displayDropoffInformation"
class="mb-4 drop-off-alert"
cmsWidgetName="AlertDropoffInformationWidget"
alertClass="alert-info"
v-bind:isDismissible="false" />
<div class="modal-link">
<text-link
linkType="text"
:text="shopQuestionLinkText"
:shopProviderData="shopProviderData"
href="#!"
@click-event="openModal"></text-link>
</div>
</div>
</transition>
<modal
ref="shopQuestionModal"
:headerText="modalHeaderText"
:onModalOpenedCallback="onModalOpened"
:onModalClosedCallback="onModalClosed"
:footerButtonText="modalFooterText"
@footer-button-event="SetZipCodeAndShop">
<div class="zip-search-group">
<div class="zip-search-input">
<serviceZipQuestion
ref="serviceZipQuestion"
class="shop-question-zipcode"
customInputId="serviceZipCode"
v-model="internalModel.zipCode"
v-on="{ 'textboxQuestionEvent.inputIdAssigned': onInputIdAssigned }"
cmsWidgetName="ServiceZipQuestionWidget"
@search-icon-click="onSearchZip"
@keydown.enter="onSearchZip"
includeSearchIcon
:isRequired="false" />
</div>
</div>
<alert
ref="alertMilitaryBaseZip"
class="my-5"
cmsWidgetName="AlertMilitaryBaseZipWidget"
v-if="displayMilitaryZipAlert"
alertClass="alert-warning" />
<alert
ref="alertMobileOnly"
class="my-5"
cmsWidgetName="AlertMobileOnlyWidget"
v-if="displayServiceableMobileOnly"
alertClass="alert-warning" />
<alert
ref="alertRecalNoMobile"
class="my-5"
cmsWidgetName="AlertRecalNoMobileWidget"
v-if="displayRecalibrationWarning"
@text-link-clicked="openModalAction"
alertClass="alert-warning" />
<alert
ref="alertInshopOnly"
class="my-5"
cmsWidgetName="AlertInshopOnlyWidget"
v-if="displayServiceableInshopOnly"
alertClass="alert-warning" />
<alert
ref="alertNoShops"
class="my-5"
cmsWidgetName="AlertNoShopsWidget"
v-if="displayNoShopsAlert"
alertClass="alert-warning" />
<alert
ref="alertMobileFeeFree"
class="my-5"
cmsWidgetName="AlertMobileFeeFreeWidget"
v-if="showMobileFreeAlert"
alertClass="alert-success" />
<buttonQuestion
ref="buttonQuestion"
buttonTypeString="shopListButton"
:buttonTypeObject="shopListButton"
class="radioQuestion"
:questionText="questionText"
:answers="answers"
groupName="chooseShop"
textPosition="text-start"
v-model="selectedProviderNumber"
isRequired
validationRules="option-required" />
<div class="show-more-shops-link">
<textLink
v-if="displaySeeMoreLocationsLink"
ref="showMoreShopsLink"
id="showMoreShopsId2"
cmsWidgetName="ShowMoreShopsLinkWidget"
linkType="text"
:text="showMoreShopsLinkText"
href="#!"
@click-event="getNextShopsFromList(3)"
:aria-label="showMoreShopsLinkText" />
</div>
</modal>
</template>
<script>
// Components
import modal from "@/digital-components/modal/modal";
import textLink from "@/ux-components/text-link/text-link.vue";
import serviceZipQuestion from "@/layouts/service-location/service-zip-modal-question/service-zip-question/service-zip-question.vue";
import buttonQuestion from "@/digital-components/button-question/button-question.vue";
import alert from "@/ux-components/alert/alert.vue";
import shopListButton from "./shop-list-button/shop-list-button";
import loadingModal from "@/fmg-components/loading-modal/loading-modal.vue";
// Supporting files
import { defineRule } from "vee-validate";
import { required } from "@/helpers/validation-rules";
import { errorMessages } from "@/constants/error-messages";
import baseMixin from "@/mixins/base-mixin.js";
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 { AppointmentTypeStrings } from "@/constants/schedule-constants";
import {
getPricedMobileFeePart,
getPricedRecycleFeePart,
getServiceabilityDetails,
getBillToAccountNumber,
getClosestApplicableShops,
getShopProviderData,
getZipCodeData,
} from "@/layouts/service-location/helpers/service-location-helper/service-location-helper";
defineRule("option-required", required(errorMessages.OPTION_REQUIRED));
function 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);
}
export default {
name: "shop-question-popup",
mixins: [baseMixin],
data() {
return {
isShopQuestionDisplayed: true,
internalModel: this.copyModel(this.modelValue),
answers: [],
shopListButton: shopListButton,
shopIndex: 0,
displaySeeMoreLocationsLink: false,
serviceZipCodeTextInputId: "",
localShopProviderData: this.shopProviderData,
selectedProviderNumber: null,
isLoadingShops: false,
lastSearchedZip: "",
lastSuccessfulZip: "",
hasLoadedShopData: false,
};
},
props: {
selectedAppointmentType: String,
shopProviderData: Object,
modelValue: {
type: Object,
default: () => ({
state: "",
zipCode: "",
}),
},
isDisplayed: Boolean,
modelWidgetName: {
type: String,
required: true,
},
isServiceableInshop: {
type: Boolean,
default: false,
},
zipCode: {
type: String,
default: "",
},
preSelectedProviderNumber: {
type: [String, Number],
default: null,
},
},
computed: {
shopQuestionLinkText() {
return this.getCmsContent("ChangeMyLocationLinkWidget", "Text");
},
modalName() {
return this.modelWidgetName;
},
modalHeaderText() {
return this.getCmsContent("ShopQuestionWidget", "QuestionText");
},
selectShopText() {
return this.getCmsContent("YourSafeliteShopWidget", "QuestionText");
},
modal() {
return this.$refs.shopQuestionModal;
},
shopProviders() {
return this.localShopProviderData?.shopProviders ?? [];
},
modalFooterText() {
return this.getCmsContent("SaveLocationWidget", "Text");
},
selectedValue: {
get: function () {
return this.modelValue;
},
set: function (newValue) {},
},
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 {
availabilityRatingCallback: getAvailabilityRating,
startDate: formattedStartDate,
endDate: formattedEndDate,
shopAppointmentType: this.selectedAppointmentType,
};
},
showMoreShopsLinkText() {
return this.getCmsContent("ShowMoreShopsLinkWidget", "Text");
},
displayNoShopsAlert() {
return this.hasLoadedShopData && this.answers.length === 0;
},
},
methods: {
openModal(event) {
if (event) event.preventDefault();
if (
!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) {
const provider =
this.shopProviders?.find((provider) => provider.providerNumber == providerNumber) ??
new Provider();
return provider;
},
async getNextShopsFromList(numberToGet = 3) {
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 zipCode = shopProvider.address.zipCode;
const distanceInMiles = Math.round(shopProvider.distanceInMiles * 2) / 2;
return {
buttonLabel: city,
buttonLabelSubCopy: `${distanceInMiles} mi`,
buttonBodyCopy: `${streetAddress}, ${city}, ${state} ${zipCode}`,
additionalButtonData: this.additionalButtonData,
value: shopProvider.providerNumber,
};
});
var gaAction = this.GaActions.MORE_LOCATIONS_CLICKED;
if (logFirstShopsDisplayed) {
gaAction = this.GaActions.SHOPS_FIRST_DISPLAYED;
}
if (
this.selectedAppointmentType === AppointmentTypeStrings.IN_SHOP ||
this.selectedAppointmentType === AppointmentTypeStrings.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();
if (selectedShopIndex >= 3) {
await this.getNextShopsFromList(selectedShopIndex + 1);
} else {
await this.getNextShopsFromList();
await nextTick();
}
},
copyModel(modelToCopy) {
return {
state: modelToCopy.state,
zipCode: modelToCopy.zipCode,
};
},
resetAnswers() {
this.answers = [];
this.shopIndex = 0;
},
onModalOpened() {
this.shopIndex = 3;
this.internalModel = this.copyModel(this.modelValue);
this.internalModel.zipCode = this.zipCode;
this.localShopProviderData = this.shopProviderData;
this.answers = [];
this.updateShopsForZip(this.internalModel.zipCode);
this.focusOnZipInput();
this.selectedProviderNumber = this.preSelectedProviderNumber;
if (this.internalModel.zipCode && this.shopProviders.length > 0) {
this.updateShopsForZip(this.internalModel.zipCode);
} else {
this.internalModel.zipCode = this.zipCode;
this.updateShopsForZip(this.internalModel.zipCode);
}
if (
(!this.selectedProviderNumber ||
!this.shopProviders.some(
(p) => p.providerNumber == this.selectedProviderNumber
)) &&
this.shopProviders.length > 0
) {
this.selectedProviderNumber = this.shopProviders[0].providerNumber;
}
},
closeModal() {
this.modal.closeModal();
},
onModalClosed() {
this.internalModel = this.copyModel(this.modelValue);
this.selectedProviderNumber = this.preSelectedProviderNumber;
this.resetsOnZipInput();
let selectedProvider = this.shopProviders.find(
(provider) => provider.providerNumber == this.selectedProviderNumber
);
if (!selectedProvider && this.shopProviders.length > 0) {
selectedProvider = this.shopProviders[0];
}
},
resetsOnZipInput() {
this.resetAlerts();
},
async onSearchZip(event) {
if (event) {
event.preventDefault();
event.stopPropagation();
}
const zip = this.internalModel.zipCode;
if (this.isVehicleHeavyTruck) {
// check the location endpoint to verify this zip can service a heavy truck
const closestShops = await getClosestApplicableShops(
this.internalModel.zipCode,
this.carId,
"service-location"
);
if (!closestShops || closestShops.providers?.length === 0) {
this.displayNoServiceAlert = true;
this.focusOnZipInput();
this.resetModalButtonStyle();
return null;
}
} else {
getShopProviderData(zip).then(async (result) => {
this.localShopProviderData = result.data;
this.updateShopsForZip(this.internalModel.zipCode);
if (this.selectedAppointmentType === "Mobile") {
this.selectedProvider = new Provider(
this.shopProviderData.mobileProviderNumber
);
} else {
this.selectedProvider = new Provider();
}
});
}
},
async updateShopsForZip(zipCode) {
if (!this.selectedAppointmentType) {
this.answers = [];
this.isLoadingShops = false;
return;
}
this.lastSearchedZip = zipCode;
if (!zipCode) {
this.isLoadingShops = false;
this.answers = [];
return;
}
this.isLoadingShops = true;
if (!zipCode || !this.shopProviders.length) {
this.handleUpdate();
return;
}
const sortedShops = getShopsByZip(zipCode, this.shopProviders, 3);
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.selectedAppointmentType,
},
value: shopProvider.providerNumber,
};
});
this.answers = mappedData;
this.shopIndex = mappedData.length;
this.displaySeeMoreLocationsLink = this.shopIndex < this.shopProviders.length;
if (mappedData.length > 0) {
this.lastSuccessfulZip = zipCode;
}
this.isLoadingShops = false;
},
onInputIdAssigned(inputId) {
this.serviceZipCodeTextInputId = inputId;
},
focusOnZipInput() {
const input = document.getElementById(this.serviceZipCodeTextInputId);
input?.focus();
},
resetAlerts() {
this.displayInvalidZipAlert = false;
this.displayNoServiceAlert = false;
},
async SetZipCodeAndShop() {
const selectedProvider = this.shopProviders.find(
(provider) => provider.providerNumber == this.selectedProviderNumber
);
this.resetAlerts();
const zipCodeData = await this.getZipCodeData(
selectedProvider.address.zipCode,
"service-location"
);
if (!zipCodeData.isValid) {
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.state = zipCodeData.state;
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.$emit("shop-selected", selectedProvider);
this.closeModal();
}
},
},
watch: {
shopProviderData: {
immediate: true,
handler(newVal) {
this.localShopProviderData = newVal;
this.hasLoadedShopData = true;
if (this.internalModel && this.internalModel.zipCode) {
this.updateShopsForZip(this.internalModel.zipCode);
}
},
},
preSelectedProviderNumber(newVal) {
this.selectedProviderNumber = newVal;
},
},
components: {
modal,
textLink,
serviceZipQuestion,
buttonQuestion,
alert,
},
mounted() {
this.handleUpdate();
},
};
</script>
<style lang="scss">
.shop-question {
margin-top: 1rem;
text-align: center;
a {
@include responsive-font-size-md(0.875rem, 1rem);
}
.button-question {
.question-text {
margin-top: 0.5rem;
}
}
}
.drop-off-alert {
background: url("data:image/svg+xml,%3Csvg viewBox='0 0 12 12' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cg clip-path='url(%23clip0_13957_112512)'%3E%3Cpath d='M5.99865 0C4.81147 4.82643e-07 3.65095 0.352111 2.66392 1.01179C1.67688 1.67146 0.907678 2.60907 0.45361 3.70599C-0.000459241 4.80291 -0.11899 6.00986 0.113013 7.17415C0.345015 8.33845 0.917126 9.40778 1.75697 10.2469C2.59682 11.086 3.66666 11.6571 4.83117 11.8881C5.99567 12.119 7.20251 11.9994 8.29902 11.5443C9.39553 11.0893 10.3324 10.3192 10.9912 9.33159C11.65 8.34396 12.0011 7.18313 12 5.99594C11.9971 4.40566 11.3638 2.88142 10.2388 1.75742C9.11375 0.633431 7.58894 0.00143011 5.99865 0V0ZM5.99865 11.2478C4.96135 11.2473 3.94748 10.9392 3.08518 10.3627C2.22288 9.7861 1.55085 8.96685 1.15401 8.00846C0.75718 7.05006 0.653353 5.99554 0.855656 4.97815C1.05796 3.96077 1.55731 3.02618 2.29061 2.29251C3.0239 1.55884 3.95823 1.059 4.97551 0.856176C5.99279 0.653349 7.04737 0.756633 8.00597 1.15297C8.96457 1.54931 9.78416 2.22092 10.3612 3.08293C10.9382 3.94493 11.2467 4.95864 11.2478 5.99594C11.2478 7.38835 10.6949 8.72377 9.71053 9.70861C8.7262 10.6934 7.39106 11.2471 5.99865 11.2478V11.2478Z' fill='%2306577C'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M6.22736 8.84695C6.30613 8.76818 6.35038 8.66135 6.35038 8.54996V5.30996C6.35038 5.19857 6.30613 5.09174 6.22736 5.01298C6.1486 4.93421 6.04177 4.88996 5.93038 4.88996C5.81899 4.88996 5.71216 4.93421 5.63339 5.01298C5.55463 5.09174 5.51038 5.19857 5.51038 5.30996V8.54996C5.51038 8.66135 5.55463 8.76818 5.63339 8.84695C5.71216 8.92571 5.81899 8.96996 5.93038 8.96996C6.04177 8.96996 6.1486 8.92571 6.22736 8.84695ZM5.69704 3.97918C5.76611 4.02533 5.84731 4.04996 5.93038 4.04996C5.98558 4.05012 6.04026 4.03936 6.09129 4.01831C6.14232 3.99726 6.18868 3.96633 6.22771 3.9273C6.26675 3.88827 6.29768 3.8419 6.31873 3.79088C6.33978 3.73985 6.35053 3.68516 6.35038 3.62996C6.35038 3.54689 6.32574 3.46569 6.27959 3.39662C6.23344 3.32755 6.16785 3.27372 6.0911 3.24193C6.01436 3.21014 5.92991 3.20183 5.84844 3.21803C5.76697 3.23424 5.69213 3.27424 5.63339 3.33298C5.57465 3.39171 5.53465 3.46655 5.51845 3.54802C5.50224 3.6295 5.51056 3.71394 5.54235 3.79069C5.57414 3.86743 5.62797 3.93303 5.69704 3.97918Z' fill='%2306577C'/%3E%3C/g%3E%3Cdefs%3E%3CclipPath id='clip0_13957_112512'%3E%3Crect width='12' height='12' fill='white'/%3E%3C/clipPath%3E%3C/defs%3E%3C/svg%3E%0A");
background-repeat: no-repeat;
background-size: 0.75rem;
background-position: 0.5rem 0.75rem;
border-radius: 0.5rem;
display: flex;
flex-direction: row;
padding: 0.5rem 0.5rem 0.5rem 1.5rem;
gap: 0.25rem;
.alert-heading {
text-align: left;
font-size: 0.75rem;
line-height: 1.25rem;
}
}
.modal-link {
text-align: center;
}
.modal.modal-component .modal-dialog .modal-content .modal-body .textbox-question {
padding: 0;
}
.zip-search-group {
display: flex;
flex-direction: row;
align-items: stretch;
width: 100%;
height: 48px;
border-radius: 48px;
padding-left: 16px;
border: 1px solid #ccc;
background: #fff;
margin-bottom: 20px;
@include media-breakpoint-up(md) {
height: auto;
padding-left: 0;
}
.zip-search-input {
align-items: stretch;
flex: 1 1 0;
height: 100%;
margin-bottom: 0;
input {
width: 100%;
height: 100%;
border: none;
padding-top: 0;
padding-bottom: 0;
outline: none;
font-size: 1rem;
background: transparent;
box-sizing: border-box;
border-radius: 48px 0 0 48px;
padding-left: 0;
}
}
.btn {
display: flex;
align-items: center;
background-color: #e5f1fa;
justify-content: center;
width: 48px;
height: 48px;
border: none;
border-radius: 0 48px 48px 0;
cursor: pointer;
}
}
@media (max-width: 767.98px) {
.zip-search-group {
padding-left: 0;
}
}
.show-more-shops-link a {
text-align: center;
display: block;
margin-left: auto;
margin-right: auto;
}
.shop-question-zipcode .search-icon-button {
background-color: #e5f1fa !important;
border-radius: 0 2.5rem 2.5rem 0;
}
.shop-question-zipcode .search-icon-button:hover,
.shop-question-zipcode .search-icon-button:focus {
border: 1px solid $gray-500;
box-shadow: 0 0 0 4px $blue-300;
background-color: #e5f1fa;
outline: none;
}
.shop-question-zipcode .input-wrapper.has-search-icon .form-control {
border-radius: 2.5rem;
padding-left: 1.5rem;
}
</style>