CASH-1230 simplifed code back to the way it was in setDisplayWaitlist, only need to call this method on an appointment type change
1758 lines
73 KiB
Vue
1758 lines
73 KiB
Vue
<template>
|
|
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }">
|
|
<loadingModal ref="loadingModal" />
|
|
<div class="container-fluid page-container-grouped-styles page-schedule">
|
|
<div class="row justify-content-center">
|
|
<div class="col-md-6">
|
|
<funnelHeader cmsWidgetName="FunnelHeaderWidget" ref="funnelHeader" />
|
|
</div>
|
|
</div>
|
|
<div class="row justify-content-center">
|
|
<div class="col-md-6 col-xl-4">
|
|
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" class="mt-4" />
|
|
<serviceZipModalQuestion
|
|
editScreenReaderTextCmsWidgetName="ScreenReaderZipEditWidget"
|
|
v-model="serviceZipCodeQuestion"
|
|
ref="serviceZipCodeQuestion"
|
|
:carId="carId"
|
|
:isVehicleHeavyTruck="isVehicleHeavyTruck"
|
|
:mobileFeePart="mobileFeePart"
|
|
@updated-mobile-fee-part="setMobileFeePart"
|
|
@updated-recycle-fee-part="setRecycleFeePart"
|
|
@updated-serviceability="setServiceabilityDetails"
|
|
@updated-contains-military-base="setContainsMilitaryBase"
|
|
@updated-bill-to-account-number="setBillToAccountNumber"
|
|
@reset-waitlist="resetWaitlist"
|
|
linkWidgetName="ServiceZipLinkWidget"
|
|
modalWidgetName="ServiceZipModalWidget" />
|
|
<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" />
|
|
</div>
|
|
</div>
|
|
<div class="row justify-content-center appointment-type">
|
|
<div class="col-md-6">
|
|
<appointmentTypeQuestion
|
|
v-model="appointmentTypeFromAppointmentTypeQuestion"
|
|
v-show="isAppointmentTypeDisplayed"
|
|
:isServiceableMobile="isServiceableMobile"
|
|
:isServiceableInshop="isServiceableInshop"
|
|
:isDisplayed="isAppointmentTypeDisplayed"
|
|
:mobileFeeApplies="mobileFeeApplies"
|
|
:zipCode="zipCode"
|
|
ref="appointmentTypeQuestion"
|
|
groupName="appointmentTypeQuestion"
|
|
cmsWidgetName="AppointmentTypeQuestionWidget"
|
|
validationRules="option-required"
|
|
labelBold="true" />
|
|
</div>
|
|
</div>
|
|
<div
|
|
class="row justify-content-center your-shop-location"
|
|
v-if="appointmentType && appointmentType !== appointmentTypeStrings.MOBILE">
|
|
<div class="col-md-6 col-xl-4">
|
|
<button-question
|
|
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"
|
|
:customText="mobileFeeText"
|
|
cmsWidgetName="MobileFeeDisclaimerWidget"
|
|
typeStyle="caption"
|
|
class="ps-4 pe-4 mt-4" />
|
|
</div>
|
|
<shopQuestionPopup
|
|
v-if="isShopQuestionDisplayed"
|
|
:selectedProviderNumberFromParent="selectedProvider?.providerNumber"
|
|
:shopProviderDataFromParent="shopProviderData"
|
|
@shop-selected="onShopSelected"
|
|
:zipCodeFromParent="zipCode"
|
|
cmsWidgetName="YourSafeliteShopWidget" />
|
|
|
|
<contentGroupModal ref="RecalModal" cmsWidgetName="RecalModal" />
|
|
</div>
|
|
</div>
|
|
<div class="row justify-content-center date-picker-wrapper">
|
|
<div class="col-md-6 col-xl-4">
|
|
<locationAlerts cmsWidgetPrefix="LocationAlert-" ref="locationAlerts" />
|
|
<datePicker
|
|
:currentZip="zipCode"
|
|
:currentProviderNumber="selectedProvider?.providerNumber"
|
|
v-show="appointmentType"
|
|
customComponentId="dateQuestion"
|
|
selectableDatesSetting="custom"
|
|
ref="datePicker"
|
|
v-model="selectedDate"
|
|
:isMobileSelected="isMobileSelected"
|
|
class="text-link-small"
|
|
:getMoreDatesCallback="getMoreScheduleData"
|
|
validationRules="date-required"
|
|
@date-clicked="handleDateClicked"
|
|
:pricingByDayBasePrice="pricingByDayBasePrice"
|
|
:pricingByDayUpcharge="pricingByDayUpcharge"
|
|
:showPricingByDay="showPricingByDay"
|
|
:isPricingByDayExperiment="isPricingByDayExperiment"
|
|
:timeSlotsForSelectedDate="timeSlotsForSelectedDate"
|
|
:appointmentType="appointmentType"
|
|
:premiumAppointmentFee="mobilePremiumAppointmentFee"
|
|
:estimatedServiceMinutesMinimum="getServiceMinutesMin"
|
|
:estimatedServiceMinutesMaximum="getServiceMinutesMax"
|
|
@timeSlotSelected="updateTimeSlot"
|
|
:displayWaitList="displayWaitList"
|
|
@waitListRequested="handleWaitListRequested" />
|
|
<navbar
|
|
cmsWidgetName="FunnelFooterWidget"
|
|
ref="navbar"
|
|
:isForwardActionDisabled="!meta.valid || isForwardActionDisabled"
|
|
@back-clicked="backButtonAction"
|
|
@ForwardClicked="forwardButtonAction" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</Form>
|
|
</template>
|
|
|
|
<script>
|
|
// Components
|
|
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 funnelHeader from "@/fmg-components/funnel-header/funnel-header";
|
|
import navbar from "@/fmg-components/nav-bar/nav-bar";
|
|
import funnelSubHeader from "@/fmg-components/funnel-sub-header/funnel-sub-header";
|
|
import loadingModal from "@/fmg-components/loading-modal/loading-modal.vue";
|
|
import { Form, defineRule } from "vee-validate";
|
|
import { errorMessages } from "@/constants/error-messages";
|
|
import datePicker from "@/digital-components/date-picker/date-picker";
|
|
import locationAlerts from "@/layouts/schedule/location-alerts/location-alerts";
|
|
import contentGroupModal from "@/fmg-components/content-group-modal/content-group-modal";
|
|
import textBlock from "@/digital-components/text-block/text-block";
|
|
|
|
// Supporting files
|
|
import baseMixin from "@/mixins/base-mixin.js";
|
|
import experimentMixin from "@/mixins/experiment-mixin.js";
|
|
import { experimentSettings } from "@/constants/experiments";
|
|
import {
|
|
AppointmentTypeStrings,
|
|
RouteCodeFlags,
|
|
PREMIUM_FEE_PART_TYPE,
|
|
PRICING_BY_DAY_PART_TYPE,
|
|
} from "@/constants/schedule-constants";
|
|
|
|
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
|
import { settleAllPromises } from "@/helpers/layout-helper";
|
|
import {
|
|
getPricedMobileFeePart,
|
|
getServiceabilityDetails,
|
|
getShopProviderData,
|
|
getZipCodeData,
|
|
} from "@/layouts/service-location/helpers/service-location-helper/service-location-helper";
|
|
import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
|
|
import { applicationConfig } from "@/constants/application-config";
|
|
import { Provider } from "@/layouts/service-location/classes/provider";
|
|
import { partNumberStrings } from "@/constants/part-number-strings";
|
|
|
|
import store from "@/store";
|
|
|
|
import { storeActions } from "@/constants/store-actions";
|
|
import { splitCopyOnCMSPlaceHolder } from "@/helpers/cms-content-helper";
|
|
import {
|
|
calcDaysBetweenDates,
|
|
convertDateStringToDate,
|
|
sumDateString,
|
|
isDropOffRouteCode,
|
|
} from "@/layouts/schedule/helpers/schedule-helper";
|
|
|
|
import { DAYS_OF_WEEK } from "@/digital-components/date-picker/mixins/constants";
|
|
import { required } from "@/helpers/validation-rules";
|
|
import { getAmountDue, getPricingByDayPartWithPrice } from "@/helpers/pricing-helper.js";
|
|
import { getItemsWithoutRecalParts } from "@/helpers/recal-helper";
|
|
import { deepClone } from "@/helpers/object-helper";
|
|
import { debugLog } from "@/helpers/debug-log-helper";
|
|
|
|
// DEFINE VALIDATION RULES
|
|
defineRule("mobile-location-required", (value) => {
|
|
const addressQuestionsValues = Object.values(
|
|
[
|
|
value.addressQuestions.streetAddress,
|
|
value.addressQuestions.city,
|
|
value.isVehicleProtected,
|
|
] || {}
|
|
);
|
|
const filledFields = addressQuestionsValues.filter(
|
|
(val) => val !== null && val !== undefined && val !== ""
|
|
);
|
|
if (
|
|
value.isMobileSelected &&
|
|
filledFields.length > 0 &&
|
|
filledFields.length < addressQuestionsValues.length
|
|
) {
|
|
return errorMessages.MOBILE_LOCATION_REQUIRED;
|
|
}
|
|
return true;
|
|
});
|
|
defineRule("date-required", required(errorMessages.DATE_REQUIRED));
|
|
|
|
// Define constants
|
|
const TIME_SLOTS_CALL_DAYS_LIMIT = 34; // needs to be 34 for API limits (35 does not consistently work)
|
|
const NUMBER_OF_CALENDAR_ROWS_TO_SHOW_FOR_INITIAL_VIEW = 2;
|
|
const MOBILE_FEE_PART_TYPE = "MOBILE FEE";
|
|
|
|
const getScheduleApiResponse = async ({
|
|
startDateString,
|
|
endDateString,
|
|
providerNumber,
|
|
zipCode,
|
|
includeMobileTimeSlots = true,
|
|
includeInshopTimeSlots = true,
|
|
}) => {
|
|
const apiEndDateLimit = sumDateString(startDateString, TIME_SLOTS_CALL_DAYS_LIMIT);
|
|
const difference = calcDaysBetweenDates(startDateString, endDateString);
|
|
const apiCallsCount = Math.ceil(difference / TIME_SLOTS_CALL_DAYS_LIMIT);
|
|
const storeActionConfigs = [];
|
|
let apiStartDate = startDateString;
|
|
let apiEndDate = endDateString;
|
|
|
|
for (let i = 1; i <= apiCallsCount; i++) {
|
|
let storeActionConfigInshop;
|
|
let storeActionConfigMobile;
|
|
|
|
if (i > 1) {
|
|
apiStartDate = sumDateString(apiEndDate, 1);
|
|
apiEndDate = sumDateString(apiStartDate, TIME_SLOTS_CALL_DAYS_LIMIT);
|
|
|
|
if (i === apiCallsCount) {
|
|
apiEndDate = endDateString;
|
|
}
|
|
} else {
|
|
if (apiEndDate > apiEndDateLimit) {
|
|
apiEndDate = apiEndDateLimit;
|
|
}
|
|
}
|
|
|
|
storeActionConfigMobile = {
|
|
storeAction: storeActions.GET_MOBILE_TIME_SLOTS,
|
|
payload: {
|
|
startDate: apiStartDate,
|
|
endDate: apiEndDate,
|
|
zipCode: zipCode,
|
|
},
|
|
};
|
|
storeActionConfigInshop = {
|
|
storeAction: storeActions.GET_SHOP_TIME_SLOTS,
|
|
payload: {
|
|
startDate: apiStartDate,
|
|
endDate: apiEndDate,
|
|
shopAppointmentType: "InshopOrDropoff",
|
|
providerNumber: providerNumber,
|
|
},
|
|
};
|
|
if (apiStartDate < apiEndDate) {
|
|
if (includeInshopTimeSlots) storeActionConfigs.push(storeActionConfigInshop);
|
|
if (includeMobileTimeSlots) storeActionConfigs.push(storeActionConfigMobile);
|
|
}
|
|
}
|
|
|
|
const inshopTimeSlotsData = {
|
|
days: [],
|
|
};
|
|
const mobileTimeSlotsData = {
|
|
days: [],
|
|
};
|
|
|
|
function compareDayStrings(a, b) {
|
|
if (a.date < b.date) return -1;
|
|
if (a.date > b.date) return 1;
|
|
return 0;
|
|
}
|
|
|
|
const makeParallelCalls = async () => {
|
|
await Promise.all(
|
|
storeActionConfigs.map(async (storeActionConfig) => {
|
|
if (storeActionConfig.storeAction.indexOf("Mobile") > -1) {
|
|
const mobileTimeSlotsResponse =
|
|
await baseMixin.methods.dispatchStoreActionWithLogging(
|
|
storeActionConfig.storeAction,
|
|
storeActionConfig.payload,
|
|
"schedule",
|
|
false
|
|
);
|
|
mobileTimeSlotsData.estimatedServiceMinutesMinimum =
|
|
mobileTimeSlotsResponse.data.estimatedServiceMinutesMinimum;
|
|
mobileTimeSlotsData.estimatedServiceMinutesMaximum =
|
|
mobileTimeSlotsResponse.data.estimatedServiceMinutesMaximum;
|
|
mobileTimeSlotsData.days = [
|
|
...mobileTimeSlotsData.days,
|
|
...mobileTimeSlotsResponse.data.days,
|
|
];
|
|
mobileTimeSlotsData.type = mobileTimeSlotsResponse.data.type;
|
|
mobileTimeSlotsData.zipCode = mobileTimeSlotsResponse.data.zipCode;
|
|
} else {
|
|
const inshopTimeSlotsResponse =
|
|
await baseMixin.methods.dispatchStoreActionWithLogging(
|
|
storeActionConfig.storeAction,
|
|
storeActionConfig.payload,
|
|
"schedule",
|
|
false
|
|
);
|
|
|
|
inshopTimeSlotsData.estimatedServiceMinutesMinimum =
|
|
inshopTimeSlotsResponse.data.estimatedServiceMinutesMinimum;
|
|
inshopTimeSlotsData.estimatedServiceMinutesMaximum =
|
|
inshopTimeSlotsResponse.data.estimatedServiceMinutesMaximum;
|
|
inshopTimeSlotsData.days = [
|
|
...inshopTimeSlotsData.days,
|
|
...inshopTimeSlotsResponse.data.days,
|
|
];
|
|
inshopTimeSlotsData.type = inshopTimeSlotsResponse.data.type;
|
|
inshopTimeSlotsData.providerNumber =
|
|
inshopTimeSlotsResponse.data.providerNumber;
|
|
}
|
|
})
|
|
);
|
|
};
|
|
|
|
return makeParallelCalls().then(() => {
|
|
// sort days chronologically
|
|
inshopTimeSlotsData.days.sort(compareDayStrings);
|
|
mobileTimeSlotsData.days.sort(compareDayStrings);
|
|
|
|
return {
|
|
inshopTimeSlotsData: inshopTimeSlotsData,
|
|
mobileTimeSlotsData: mobileTimeSlotsData,
|
|
};
|
|
});
|
|
};
|
|
|
|
const returnFirstDate = (dates) => {
|
|
if (dates.days?.length > 0) {
|
|
return dates.days[0].date;
|
|
} else {
|
|
return null;
|
|
}
|
|
};
|
|
|
|
export default {
|
|
name: "schedule",
|
|
data() {
|
|
return {
|
|
selectedDate: this.getSelectedDateFromStore(),
|
|
selectedTimeSlotInfo: this.getSelectedTimeSlotInfo(),
|
|
mobilePremiumAppointmentFee: null,
|
|
waitListRequested: null,
|
|
displayWaitList: null,
|
|
pricingByDayUpchargeLineItem: null,
|
|
includePricingByDayUpcharge: null,
|
|
isPricingByDayExperiment: null,
|
|
pricingByDayBasePrice: null,
|
|
pricingByDayUpcharge: null,
|
|
showPricingByDay: null,
|
|
selectableDatesInshop: [],
|
|
selectableDatesMobile: [],
|
|
preSelectedDate: null,
|
|
|
|
streetAddress: this.getServiceAddressFromStore(),
|
|
apartmentNumberOrBusinessName: this.getServiceAddress2FromStore(),
|
|
carId: this.getCarIdfromStore(),
|
|
city: this.getServiceCityFromStore(),
|
|
state: this.getServiceStateFromStore(),
|
|
zipCode: this.getServiceZipCodeFromStore(),
|
|
isVehicleHeavyTruck: this.getIsVehicleHeavyTruckFromStore(),
|
|
isVehicleProtected: this.getIsVehicleProtectedFromStore(),
|
|
isGlassServiceableInshop: null,
|
|
isRecalibrationServiceableInshop: null,
|
|
isGlassServiceableDropoff: null,
|
|
isRecalibrationServiceableDropoff: null,
|
|
isGlassServiceableMobile: null,
|
|
isRecalibrationServiceableMobile: null,
|
|
appointmentType: this.getAppointmentTypeFromStore(),
|
|
appointmentTypeFromAppointmentTypeQuestion: this.getAppointmentTypeFromStore(),
|
|
selectedProvider: this.getSelectedProviderFromStore(),
|
|
mobileFeePart: null,
|
|
recycleFeePart: null,
|
|
zipContainsMilitaryBase: false,
|
|
zipCodeCtu: null,
|
|
billToAccountNumber: null,
|
|
shopProviderData: null,
|
|
navigatingForward: false,
|
|
shopListButton: shopListButton,
|
|
};
|
|
},
|
|
async beforeRouteEnter(to, from, next) {
|
|
const isPricingByDayExperiment = experimentMixin.methods.hasSettingEqualTo(
|
|
experimentSettings.PRICING_BY_DAY,
|
|
"true"
|
|
);
|
|
const showPricingByDay = !store.getters.payment.isInsurance && isPricingByDayExperiment;
|
|
|
|
// Get pricingByDayBasePrice needed for Pricing By Day
|
|
const lineItems = deepClone(store.getters.order.lineItems);
|
|
const isRecalibrationOnOrder = store.getters.isRecalibrationOnOrder;
|
|
const shouldHideRecalibration =
|
|
experimentMixin.methods.hasSettingEqualTo(
|
|
experimentSettings.RECAL_PRICE_REMOVE,
|
|
"true"
|
|
) && isRecalibrationOnOrder;
|
|
const glassParts =
|
|
isRecalibrationOnOrder && shouldHideRecalibration
|
|
? getItemsWithoutRecalParts(lineItems.glassParts)
|
|
: (lineItems.glassParts ?? []);
|
|
const supportingItemsWithoutFees = baseMixin.methods.filterOutCertainPartTypesOrNumbers(
|
|
lineItems.supportingItems,
|
|
{
|
|
partNumbersToRemove: [
|
|
partNumberStrings.RECYCLE_FEE,
|
|
partNumberStrings.PRICING_BY_DAY_UPCHARGE,
|
|
],
|
|
}
|
|
);
|
|
const lineItemsToBePriced = {
|
|
glassParts: glassParts,
|
|
supportingItems: supportingItemsWithoutFees,
|
|
vaps: lineItems.vaps ?? [],
|
|
promos: lineItems.promos ?? [],
|
|
};
|
|
const priceString = getAmountDue(lineItemsToBePriced, false); // pass the IncludeTax param as false
|
|
const priceStringIntegerRoundedDown = priceString?.split(".")[0]; // same method used as getDisplayPrice() in service-package-radio used on /quote
|
|
const pricingByDayBasePrice = parseInt(priceStringIntegerRoundedDown);
|
|
let includePricingByDayUpcharge = false;
|
|
let appointmentType = store.getters.order.serviceLocation.appointmentType;
|
|
|
|
// Check to see if date should be pre-selected
|
|
let scheduleFromStore = await store.getters.order.schedule;
|
|
let preSelectedDate;
|
|
|
|
if (scheduleFromStore.date && scheduleFromStore.date.length > 0) {
|
|
preSelectedDate = scheduleFromStore.date;
|
|
|
|
if (appointmentType === AppointmentTypeStrings.MOBILE) {
|
|
preSelectedDate += "-mobile";
|
|
}
|
|
|
|
// Check to see if pre-selected date should have pricing by day upcharge
|
|
if (showPricingByDay) {
|
|
// is this preSelectedDate a higher priced pricingByDay day?
|
|
const dayIndex = convertDateStringToDate(scheduleFromStore?.date).getDay();
|
|
const dayObject = DAYS_OF_WEEK[dayIndex];
|
|
if (dayObject.isPricingByDayUpchargeDay) {
|
|
includePricingByDayUpcharge = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set up promises
|
|
const cmsContentPromise = fetchCmsContentForPage(to.name);
|
|
|
|
const alertReasonsPromise = locationAlerts.methods.loadInitialData(
|
|
store.getters.order.serviceLocation.zipCodeCtu,
|
|
store.getters.order.serviceLocation.provider?.address?.zipCodeCtu
|
|
);
|
|
|
|
const serviceZipCode = store.getters.order.serviceLocation.zipCode;
|
|
const zipCodeDataPromise = getZipCodeData(serviceZipCode);
|
|
const serviceabilityDetailsPromise = getServiceabilityDetails(
|
|
serviceZipCode,
|
|
null,
|
|
"service-location"
|
|
);
|
|
const mobileFeePartPromise = getPricedMobileFeePart(serviceZipCode, "service-location");
|
|
const shopProviderData = await getShopProviderData(serviceZipCode);
|
|
const providerNumber = shopProviderData?.data?.shopProviders[0]?.providerNumber;
|
|
|
|
// Get pricingByDayUpcharge needed for Pricing By Day
|
|
const pricingByDayUpchargePartPromise = showPricingByDay
|
|
? getPricingByDayPartWithPrice()
|
|
: null;
|
|
|
|
const premiumFeePromise = baseMixin.methods.dispatchStoreActionWithLogging(
|
|
storeActions.GET_MOBILE_PREMIUM_FEE,
|
|
null,
|
|
"schedule"
|
|
);
|
|
const premiumFeeWithPricePromise = premiumFeePromise.then((result) => {
|
|
if (result.data) {
|
|
return baseMixin.methods.dispatchStoreActionWithLogging(
|
|
storeActions.PRICE_ORDER_ITEMS_AND_SAVE_SERVER_DATA,
|
|
{
|
|
availableLineItems: [result.data],
|
|
},
|
|
"schedule",
|
|
false
|
|
);
|
|
} else {
|
|
return result.data;
|
|
}
|
|
});
|
|
|
|
// Settle promises and get results
|
|
const promiseResultMap = [
|
|
{
|
|
resultKey: "cmsContent",
|
|
promise: cmsContentPromise,
|
|
},
|
|
{
|
|
resultKey: "alertReasons",
|
|
promise: alertReasonsPromise,
|
|
},
|
|
{
|
|
resultKey: "pricingByDayUpchargePart",
|
|
promise: pricingByDayUpchargePartPromise,
|
|
},
|
|
{
|
|
resultKey: "premiumFeeWithPrice",
|
|
promise: premiumFeeWithPricePromise,
|
|
},
|
|
{
|
|
resultKey: "zipCodeData",
|
|
promise: zipCodeDataPromise,
|
|
},
|
|
{
|
|
resultKey: "mobileFeePart",
|
|
promise: mobileFeePartPromise,
|
|
},
|
|
{
|
|
resultKey: "serviceabilityDetails",
|
|
promise: serviceabilityDetailsPromise,
|
|
},
|
|
];
|
|
|
|
const resultMap = await settleAllPromises(promiseResultMap);
|
|
|
|
// add pricing by day data
|
|
const pricingByDayUpcharge =
|
|
showPricingByDay && resultMap.pricingByDayUpchargePart
|
|
? await baseMixin.methods.getTotalLineItemPrice(
|
|
resultMap.pricingByDayUpchargePart,
|
|
false
|
|
)
|
|
: null;
|
|
|
|
// Call the "next" function to complete the transition to this page.
|
|
next((vm) => {
|
|
vm.setCmsContent(resultMap.cmsContent);
|
|
vm.$refs.locationAlerts.initializeComponent(resultMap.alertReasons);
|
|
vm.mobilePremiumAppointmentFee = resultMap.premiumFeeWithPrice
|
|
? resultMap.premiumFeeWithPrice[0]
|
|
: null;
|
|
vm.updateFooterButtonText(vm.selectedTimeSlotInfo);
|
|
vm.pricingByDayUpchargeLineItem = resultMap.pricingByDayUpchargePart;
|
|
vm.includePricingByDayUpcharge = includePricingByDayUpcharge;
|
|
vm.isPricingByDayExperiment = isPricingByDayExperiment;
|
|
vm.pricingByDayBasePrice = pricingByDayBasePrice;
|
|
vm.pricingByDayUpcharge = pricingByDayUpcharge;
|
|
vm.showPricingByDay = showPricingByDay;
|
|
vm.preSelectedDate = preSelectedDate;
|
|
vm.appointmentType = appointmentType;
|
|
vm.setData(
|
|
resultMap.zipCodeData,
|
|
resultMap.serviceabilityDetails,
|
|
resultMap.mobileFeePart,
|
|
shopProviderData.data
|
|
);
|
|
vm.initializeDatePicker();
|
|
});
|
|
},
|
|
computed: {
|
|
serviceZipCodeQuestion: {
|
|
get: function () {
|
|
return {
|
|
state: this.state,
|
|
zipCode: this.zipCode,
|
|
zipCodeCtu: this.zipCodeCtu,
|
|
};
|
|
},
|
|
set: function (newValue) {
|
|
this.handleZipCodeChange(newValue);
|
|
},
|
|
},
|
|
isMobileSelected() {
|
|
return this.appointmentType == AppointmentTypeStrings.MOBILE;
|
|
},
|
|
isServiceableMobile() {
|
|
if (this.isRecalibrationServiceableMobile !== null) {
|
|
return (
|
|
(this.isGlassServiceableMobile && this.isRecalibrationServiceableMobile) ||
|
|
this.isMobileStaticRecalibrationApplicable
|
|
);
|
|
} else {
|
|
return this.isGlassServiceableMobile;
|
|
}
|
|
},
|
|
isMobileStaticRecalibrationApplicable() {
|
|
return (
|
|
this.displayMSR &&
|
|
this.mobileFeePart?.partNumber == partNumberStrings.MOBILE_STATIC_RECAL_FEE &&
|
|
(this.isCashItacNoComp || this.mobileFeePart?.isInsurable)
|
|
);
|
|
},
|
|
displayMSR() {
|
|
return (
|
|
experimentMixin.methods
|
|
.getSettingValue(experimentSettings.DISPLAY_MSR)
|
|
?.toLowerCase() === "true"
|
|
);
|
|
},
|
|
isCashItacNoComp() {
|
|
return !this.isInsurance || this.isITAC || this.isNoComp;
|
|
},
|
|
mobileFeeHasPrice() {
|
|
return (
|
|
this.mobileFeePart?.laborAmount > 0 ||
|
|
this.mobileFeePart?.sellingPrice > 0 ||
|
|
this.mobileFeePart?.kitPrice > 0
|
|
);
|
|
},
|
|
mobileFeeApplies() {
|
|
if (this.isMobileStaticRecalibrationApplicable && this.mobileFeeHasPrice) {
|
|
return this.isCashItacNoComp;
|
|
} else {
|
|
return this.mobileFeeHasPrice;
|
|
}
|
|
},
|
|
mobileFeeText() {
|
|
const cmsContentText = this.getCmsContent("MobileFeeDisclaimerWidget", "Text");
|
|
return cmsContentText.replaceAll("{custom:mobileFee}", this.mobileFee);
|
|
},
|
|
mobileFee() {
|
|
if (!this.mobileFeePart) {
|
|
return 0;
|
|
}
|
|
return (
|
|
this.mobileFeePart.laborAmount +
|
|
this.mobileFeePart.sellingPrice +
|
|
this.mobileFeePart.kitPrice
|
|
);
|
|
},
|
|
showMobileFreeAlert() {
|
|
if (this.isServiceableMobile && !this.isInsurance && !this.mobileFeeHasPrice) {
|
|
return true;
|
|
}
|
|
return false;
|
|
},
|
|
isServiceableInshop() {
|
|
if (this.isRecalibrationServiceableInshop !== null) {
|
|
return this.isGlassServiceableInshop && this.isRecalibrationServiceableInshop;
|
|
} else {
|
|
return this.isGlassServiceableInshop;
|
|
}
|
|
},
|
|
isServiceableDropoff() {
|
|
if (this.isRecalibrationServiceableDropoff !== null) {
|
|
return this.isGlassServiceableDropoff && this.isRecalibrationServiceableDropoff;
|
|
} else {
|
|
return this.isGlassServiceableDropoff;
|
|
}
|
|
},
|
|
isShopQuestionDisplayed() {
|
|
return (
|
|
this.appointmentType === AppointmentTypeStrings.IN_SHOP ||
|
|
this.appointmentType === AppointmentTypeStrings.DROP_OFF ||
|
|
AppointmentTypeStrings.IN_SHOP_OR_DROP_OFF
|
|
);
|
|
},
|
|
isAppointmentTypeDisplayed() {
|
|
return this.zipCode && !this.displayNoShopsAlert;
|
|
},
|
|
requiresInshopRecalibration() {
|
|
// Specifically check for isRecalibrationServiceableMobile === false, not null or true.
|
|
return (
|
|
this.isServiceableInshop &&
|
|
this.isGlassServiceableMobile &&
|
|
this.isRecalibrationServiceableMobile === false &&
|
|
!this.isMobileStaticRecalibrationApplicable
|
|
);
|
|
},
|
|
displayRecalibrationWarning() {
|
|
return this.requiresInshopRecalibration;
|
|
},
|
|
displayServiceableInshopOnly() {
|
|
return (
|
|
!this.displayRecalibrationWarning &&
|
|
this.isServiceableInshop &&
|
|
!this.isServiceableMobile
|
|
);
|
|
},
|
|
displayMilitaryZipAlert() {
|
|
return this.zipContainsMilitaryBase && this.isServiceableMobile;
|
|
},
|
|
displayServiceableMobileOnly() {
|
|
return this.isServiceableMobile && !this.isServiceableInshop;
|
|
},
|
|
displayNoShopsAlert() {
|
|
return !this.isServiceableInshop && !this.isServiceableMobile;
|
|
},
|
|
recalibrationInformationModal() {
|
|
return this.$refs.recalibrationInformationModal;
|
|
},
|
|
isInsurance() {
|
|
return store.getters.payment.isInsurance;
|
|
},
|
|
isITAC() {
|
|
return store.getters.order.policy.isItac;
|
|
},
|
|
isNoComp() {
|
|
return store.getters.order.policy.isNoComp;
|
|
},
|
|
isForwardActionDisabled() {
|
|
if (
|
|
(this.appointmentType === AppointmentTypeStrings.IN_SHOP ||
|
|
this.appointmentType === AppointmentTypeStrings.DROP_OFF) &&
|
|
!this.selectedTimeSlotInfo?.timeSlot?.date
|
|
) {
|
|
return true;
|
|
}
|
|
return (
|
|
this.displayNoShopsAlert ||
|
|
this.appointmentType === AppointmentTypeStrings.IN_SHOP_OR_DROP_OFF
|
|
);
|
|
},
|
|
additionalButtonData() {
|
|
return {
|
|
shopAppointmentType: this.appointmentType,
|
|
};
|
|
},
|
|
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 &&
|
|
this.selectedProvider?.providerNumber
|
|
) {
|
|
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;
|
|
},
|
|
ChangeShopLinkText() {
|
|
return this.getCmsContent("ChangeShopLink", "Text");
|
|
},
|
|
ChangeShopLink() {
|
|
// Splits content when brackets are found in text so that text can be looped through and router-link can be injected when needed
|
|
return this.splitCopyOnCMSPlaceHolder(this.ChangeShopLinkText);
|
|
},
|
|
getServiceMinutesMin() {
|
|
return this.isMobileSelected
|
|
? this.selectableDatesMobile.estimatedServiceMinutesMinimum
|
|
: this.selectableDatesInshop.estimatedServiceMinutesMinimum;
|
|
},
|
|
getServiceMinutesMax() {
|
|
return this.isMobileSelected
|
|
? this.selectableDatesMobile.estimatedServiceMinutesMaximum
|
|
: this.selectableDatesInshop.estimatedServiceMinutesMaximum;
|
|
},
|
|
timeSlotsForSelectedDate() {
|
|
if (!this.selectedDate) return null;
|
|
const selectedDate = this.selectedDate;
|
|
|
|
if (this.isMobileSelected) {
|
|
const selectedDateString = selectedDate.replace("-mobile", "");
|
|
return this.selectableDatesMobile.days?.find(
|
|
(selectableDate) => selectableDate.date === selectedDateString
|
|
);
|
|
} else {
|
|
return this.selectableDatesInshop.days?.find(
|
|
(selectableDate) => selectableDate.date === this.selectedDate
|
|
);
|
|
}
|
|
},
|
|
},
|
|
methods: {
|
|
splitCopyOnCMSPlaceHolder,
|
|
arePagePrerequisitesValid() {
|
|
const paymentInfo = store.getters.payment.isInsurance !== null;
|
|
const damageInfo =
|
|
store.getters.order.damage.isRepair ||
|
|
(store.getters.order.lineItems?.glassParts != null &&
|
|
store.getters.order.lineItems.glassParts.length > 0);
|
|
const parentAccountNumberInfo =
|
|
store.getters.payment.parentAccountNumber !==
|
|
applicationConfig.CASH_PARENT_ACCOUNT_NUMBER;
|
|
const policyNumberInfo = store.getters.order.policy.policyNumber;
|
|
const zipCodeInfo = store.getters.order.serviceLocation.zipCode;
|
|
const supportingItemsInfo = store.getters.lineItems.supportingItems;
|
|
|
|
// insurance drops the recycle fee on replace orders so it won't be in supportingItems
|
|
const insurancePreReqResult =
|
|
parentAccountNumberInfo &&
|
|
policyNumberInfo &&
|
|
zipCodeInfo &&
|
|
paymentInfo &&
|
|
damageInfo;
|
|
const cashPreReqResult =
|
|
supportingItemsInfo && zipCodeInfo && paymentInfo && damageInfo;
|
|
|
|
const outputDebugLog = (preReqResult) => {
|
|
// prettier-ignore
|
|
debugLog("--- schedule.vue pagePrereqs start ---", null, !preReqResult);
|
|
debugLog(
|
|
"store.getters.payment.isInsurance:",
|
|
store.getters.payment?.isInsurance,
|
|
!preReqResult
|
|
);
|
|
debugLog(
|
|
"store.getters.order.damage.isRepair:",
|
|
store.getters.order.damage?.isRepair,
|
|
!preReqResult
|
|
);
|
|
debugLog(
|
|
"store.getters.order.lineItems.glassParts:",
|
|
store.getters.order.lineItems?.glassParts,
|
|
!preReqResult
|
|
);
|
|
debugLog(
|
|
"store.getters.payment.parentAccountNumber:",
|
|
store.getters.payment.parentAccountNumber,
|
|
!preReqResult
|
|
);
|
|
debugLog(
|
|
"store.getters.order.policy.policyNumber:",
|
|
store.getters.order.policy.policyNumber,
|
|
!preReqResult
|
|
);
|
|
debugLog(
|
|
"store.getters.order.serviceLocation.zipCode:",
|
|
store.getters.order.serviceLocation.zipCode,
|
|
!preReqResult
|
|
);
|
|
debugLog(
|
|
"store.getters.lineItems.supportingItems:",
|
|
store.getters.lineItems.supportingItems,
|
|
!preReqResult
|
|
);
|
|
debugLog("--- schedule.vue pagePrereqs end ---", null, !preReqResult);
|
|
};
|
|
|
|
if (store.getters.payment.isInsurance) {
|
|
outputDebugLog(insurancePreReqResult);
|
|
return insurancePreReqResult;
|
|
} else {
|
|
outputDebugLog(cashPreReqResult);
|
|
return cashPreReqResult;
|
|
}
|
|
},
|
|
|
|
setData(zipCodeData, serviceabilityDetails, mobileFeePart, shopProviderData) {
|
|
if (zipCodeData) {
|
|
this.zipContainsMilitaryBase = zipCodeData.containsMilitaryBase;
|
|
this.zipCodeCtu = zipCodeData.zipCodeCtu;
|
|
}
|
|
|
|
if (serviceabilityDetails) {
|
|
this.setServiceabilityDetails(serviceabilityDetails);
|
|
}
|
|
|
|
if (mobileFeePart) {
|
|
this.mobileFeePart = mobileFeePart;
|
|
}
|
|
|
|
if (shopProviderData) {
|
|
this.shopProviderData = shopProviderData;
|
|
this.updateSelectedProvider(); // ensure that page loads with non-null selectedProvider
|
|
}
|
|
|
|
var gaLabel = this.GaLabels.NO;
|
|
if (this.isServiceableMobile) {
|
|
gaLabel = this.GaLabels.YES;
|
|
}
|
|
this.pushEventToGA(
|
|
this.GaCategories.APPOINTMENT,
|
|
this.GaActions.MOBILE_AVAILABLE,
|
|
gaLabel,
|
|
true
|
|
);
|
|
},
|
|
setContainsMilitaryBase(val) {
|
|
if (this.zipContainsMilitaryBase !== val) {
|
|
this.zipContainsMilitaryBase = val;
|
|
}
|
|
},
|
|
setBillToAccountNumber(val) {
|
|
this.billToAccountNumber = val;
|
|
},
|
|
setCtuForMobile(val) {
|
|
this.zipCodeCtu = val;
|
|
},
|
|
setMobileFeePart(mobileFeePart) {
|
|
this.mobileFeePart = mobileFeePart;
|
|
},
|
|
setRecycleFeePart(recycleFeePart) {
|
|
this.recycleFeePart = recycleFeePart;
|
|
},
|
|
getCarIdfromStore() {
|
|
return store.getters.vehicle.carId;
|
|
},
|
|
getServiceAddressFromStore() {
|
|
return store.getters.order.serviceLocation.address;
|
|
},
|
|
getServiceAddress2FromStore() {
|
|
return store.getters.order.serviceLocation.address2;
|
|
},
|
|
getServiceCityFromStore() {
|
|
return store.getters.order.serviceLocation.city;
|
|
},
|
|
getServiceStateFromStore() {
|
|
return store.getters.order.serviceLocation.state;
|
|
},
|
|
getServiceZipCodeFromStore() {
|
|
return store.getters.order.serviceLocation.zipCode;
|
|
},
|
|
getIsVehicleHeavyTruckFromStore() {
|
|
return store.getters.order.vehicle?.isBigTruck ?? false;
|
|
},
|
|
getIsVehicleProtectedFromStore() {
|
|
return store.getters.order.serviceLocation.isVehicleProtected;
|
|
},
|
|
getAppointmentTypeFromStore() {
|
|
return store.getters.order.serviceLocation.appointmentType;
|
|
},
|
|
getSelectedProviderFromStore() {
|
|
return store.getters.order.serviceLocation.provider;
|
|
},
|
|
resetMobileLocation() {
|
|
this.streetAddress = "";
|
|
this.apartmentNumberOrBusinessName = "";
|
|
this.city = "";
|
|
|
|
this.isVehicleProtected = null;
|
|
},
|
|
resetWaitlist() {
|
|
this.dispatchStoreAction(this.storeActions.SAVE_WAITLIST_REQUESTED, false, false);
|
|
this.waitListRequested = false;
|
|
},
|
|
setServiceabilityDetails(serviceabilityDetails) {
|
|
this.isGlassServiceableInshop = serviceabilityDetails.isGlassServiceableInshop;
|
|
this.isRecalibrationServiceableInshop =
|
|
serviceabilityDetails.isRecalibrationServiceableInshop;
|
|
this.isGlassServiceableDropoff = serviceabilityDetails.isGlassServiceableDropoff;
|
|
this.isRecalibrationServiceableDropoff =
|
|
serviceabilityDetails.isRecalibrationServiceableDropoff;
|
|
this.isGlassServiceableMobile = serviceabilityDetails.isGlassServiceableMobile;
|
|
this.isRecalibrationServiceableMobile =
|
|
serviceabilityDetails.isRecalibrationServiceableMobile;
|
|
},
|
|
async reloadShopData(zipCode) {
|
|
await this.$refs.shopQuestion.reloadShopData(zipCode);
|
|
},
|
|
openRecalibrationInformationModal() {
|
|
this.recalibrationInformationModal.openModal();
|
|
},
|
|
updateAndSaveIsMSRFeeApplicable() {
|
|
const isMSRFeeApplicable =
|
|
this.isMobileStaticRecalibrationApplicable && this.appointmentType == "Mobile";
|
|
this.dispatchStoreAction(
|
|
this.storeActions.SAVE_IS_MSR_FEE_APPLICABLE,
|
|
isMSRFeeApplicable
|
|
);
|
|
},
|
|
updateAndSaveSupportingItems() {
|
|
let supportingItems = store.getters.lineItems.supportingItems;
|
|
let shouldSaveSupportingItems = false;
|
|
|
|
supportingItems =
|
|
!supportingItems && this.isMobileStaticRecalibrationApplicable
|
|
? []
|
|
: supportingItems;
|
|
|
|
// for insurance orders, fees can get removed causing supportingitems to be null or empty
|
|
if (!supportingItems) {
|
|
return;
|
|
}
|
|
|
|
// update recyle fee price
|
|
if (this.recycleFeePart) {
|
|
const recycleFeeIndex = supportingItems.findIndex(
|
|
(item) => item.partNumber == partNumberStrings.RECYCLE_FEE
|
|
);
|
|
if (recycleFeeIndex >= 0) {
|
|
supportingItems[recycleFeeIndex].laborAmount = this.recycleFeePart.laborAmount;
|
|
supportingItems[recycleFeeIndex].sellingPrice =
|
|
this.recycleFeePart.sellingPrice;
|
|
supportingItems[recycleFeeIndex].kitPrice = this.recycleFeePart.kitPrice;
|
|
|
|
shouldSaveSupportingItems = true;
|
|
}
|
|
}
|
|
|
|
// if we have a mobile fee, then save/update supporting items
|
|
if (this.appointmentType == "Mobile") {
|
|
const mobileFeeIndex = supportingItems.findIndex(
|
|
(item) => item.partType == MOBILE_FEE_PART_TYPE
|
|
);
|
|
// If it already exists, update the price with latest data
|
|
if (mobileFeeIndex >= 0) {
|
|
supportingItems[mobileFeeIndex].laborAmount = this.mobileFeePart.laborAmount;
|
|
supportingItems[mobileFeeIndex].sellingPrice = this.mobileFeePart.sellingPrice;
|
|
supportingItems[mobileFeeIndex].kitPrice = this.mobileFeePart.kitPrice;
|
|
} else {
|
|
if (this.mobileFeePart !== null) supportingItems.push(this.mobileFeePart);
|
|
}
|
|
|
|
shouldSaveSupportingItems = true;
|
|
} else {
|
|
// if it's not a mobile, then make sure we remove any that may have been added
|
|
const removeMobileFeeIndex = supportingItems?.findIndex(
|
|
(item) => item.partType == MOBILE_FEE_PART_TYPE
|
|
);
|
|
|
|
if (removeMobileFeeIndex >= 0) {
|
|
supportingItems.splice(removeMobileFeeIndex, 1);
|
|
shouldSaveSupportingItems = true;
|
|
}
|
|
}
|
|
|
|
// Use shouldSaveSupportingItems flag to determine if we need to save supporting items. Prevents unnecessary/multiple saves
|
|
if (shouldSaveSupportingItems) {
|
|
this.dispatchStoreAction(
|
|
this.storeActions.SAVE_SUPPORTING_ITEMS_SUPPRESSING_STATE_RESETTING,
|
|
supportingItems,
|
|
false
|
|
);
|
|
}
|
|
},
|
|
getTimeSlotInfo() {
|
|
// Get the current date
|
|
const currentDate = new Date();
|
|
|
|
// Add 10 days to the current date
|
|
currentDate.setDate(currentDate.getDate() + 10);
|
|
|
|
// Format the date as yyyy-mm-dd
|
|
const year = currentDate.getFullYear();
|
|
const month = String(currentDate.getMonth() + 1).padStart(2, "0");
|
|
const day = String(currentDate.getDate()).padStart(2, "0");
|
|
|
|
const formattedDate = `${year}-${month}-${day}`;
|
|
|
|
return {
|
|
date: formattedDate,
|
|
endTime: "12:00",
|
|
jobMaxMinutes: "180",
|
|
jobMinMinutes: "120",
|
|
routeCode: "3357I-03357-M-I*20988*AM",
|
|
startTime: "08:00",
|
|
};
|
|
},
|
|
onShopSelected(shopQuestionPopUpData) {
|
|
this.resetWaitlist();
|
|
this.preSelectedDate = null;
|
|
this.shopProviderData = shopQuestionPopUpData.shopProviderData;
|
|
this.setServiceabilityDetails(shopQuestionPopUpData.serviceabilityDetails);
|
|
const selectedProvider = this.shopProviderData.shopProviders.find((shopProvider) => {
|
|
return shopProvider.providerNumber === shopQuestionPopUpData.selectedProviderNumber;
|
|
});
|
|
|
|
// No zipCodeData comes back if zip was not changed
|
|
if (shopQuestionPopUpData.zipCodeData) {
|
|
this.updateSelectedProviderAndZipCode(
|
|
shopQuestionPopUpData.zipCodeData,
|
|
selectedProvider
|
|
);
|
|
} else {
|
|
this.updateSelectedProvider(selectedProvider);
|
|
}
|
|
},
|
|
async getMoreScheduleData(startDate, endDate) {
|
|
// called only when "View more dates" is clicked; not on initial page load
|
|
const moreShopTimeSlots = await getScheduleApiResponse({
|
|
startDateString: startDate,
|
|
endDateString: endDate,
|
|
providerNumber: this.selectedProvider?.providerNumber,
|
|
zipCode: this.zipCode,
|
|
includeMobileTimeSlots: this.isServiceableMobile,
|
|
includeInshopTimeSlots: this.isServiceableInshop || this.isServiceableDropoff,
|
|
});
|
|
|
|
// ADD API CALL RESULTS TO EXISTING DATE DATA
|
|
this.selectableDatesInshop.days = this.selectableDatesInshop.days.concat(
|
|
moreShopTimeSlots.inshopTimeSlotsData.days
|
|
);
|
|
this.selectableDatesMobile.days = this.selectableDatesMobile.days.concat(
|
|
moreShopTimeSlots.mobileTimeSlotsData.days
|
|
);
|
|
|
|
return moreShopTimeSlots;
|
|
},
|
|
|
|
async initializeDatePicker() {
|
|
this.selectedDate = null;
|
|
|
|
const datePickerInitialData = await this.$refs.datePicker.loadInitialData({
|
|
// setup config options for date-picker
|
|
selectableDatesSetting: "custom",
|
|
initialViewRowsToShow: NUMBER_OF_CALENDAR_ROWS_TO_SHOW_FOR_INITIAL_VIEW,
|
|
getSelectableDatesCallback: getScheduleApiResponse,
|
|
preSelectedDate: this.preSelectedDate,
|
|
providerNumber: this.selectedProvider?.providerNumber,
|
|
zipCode: this.zipCode,
|
|
includeMobileTimeSlots: this.isServiceableMobile,
|
|
includeInshopTimeSlots: this.isServiceableInshop || this.isServiceableDropoff,
|
|
});
|
|
datePickerInitialData.pricingByDayBasePrice = this.pricingByDayBasePrice;
|
|
datePickerInitialData.pricingByDayUpcharge = this.pricingByDayUpcharge;
|
|
await this.$refs.datePicker.initializeComponent(datePickerInitialData);
|
|
this.selectableDatesInshop =
|
|
datePickerInitialData.initialShopTimeSlotsResponse.inshopTimeSlotsData;
|
|
this.selectableDatesMobile =
|
|
datePickerInitialData.initialShopTimeSlotsResponse.mobileTimeSlotsData;
|
|
this.setDisplayWaitList();
|
|
|
|
if (this.preSelectedDate) this.selectedDate = this.preSelectedDate;
|
|
|
|
if (!this.selectedDate) {
|
|
// if no date is preselected on load, then select the first available
|
|
let selectedDateMobile = this.getSelectedDateForMobile();
|
|
let selectedDateInshop = this.getSelectedDateForInshop();
|
|
|
|
// if there is still no selected date, then load more and try again
|
|
if (
|
|
(this.isServiceableMobile && !selectedDateMobile) ||
|
|
(this.isServiceableInshop && !selectedDateInshop) ||
|
|
(this.isServiceableDropoff && !selectedDateInshop)
|
|
) {
|
|
await this.$nextTick();
|
|
await this.$refs.datePicker.showAnotherMonth();
|
|
|
|
// update all dates
|
|
|
|
// > CHLOE HERD 7/22 -- CASH-1207
|
|
// > Do not update the available dates again here;
|
|
// > they have already been updated by `showAnotherMonth`.
|
|
// > Doing so will likely add or remove dates,
|
|
// > desyncing the schedule page and the date-picker.
|
|
|
|
this.setDisplayWaitList();
|
|
}
|
|
|
|
await this.$nextTick();
|
|
|
|
if (this.isMobileSelected) {
|
|
this.selectedDate = this.getSelectedDateForMobile();
|
|
} else {
|
|
this.selectedDate = this.getSelectedDateForInshop();
|
|
}
|
|
}
|
|
},
|
|
getScheduleApiResponse,
|
|
getServiceZipCtuCodeFromStore() {
|
|
return store.getters.order.serviceLocation.zipCodeCtu;
|
|
},
|
|
getSelectedDateFromStore() {
|
|
let isMobileSelected = this.isMobileSelected;
|
|
if (isMobileSelected === undefined) {
|
|
isMobileSelected = this.appointmentType === AppointmentTypeStrings.MOBILE;
|
|
}
|
|
let storedDate = store.getters.order.schedule.date;
|
|
if (isMobileSelected) storedDate += "-mobile";
|
|
return storedDate;
|
|
},
|
|
getSelectedTimeSlotInfo() {
|
|
const supportingItems = this.getSupportingItems();
|
|
|
|
var isPremiumAppointment = false;
|
|
if (supportingItems) {
|
|
isPremiumAppointment =
|
|
!!supportingItems.filter(
|
|
(lineItem) => lineItem.partType === PREMIUM_FEE_PART_TYPE
|
|
).length > 0;
|
|
}
|
|
|
|
const selectedTimeSlotInfo = {
|
|
timeSlot: store.getters.order.schedule,
|
|
isPremiumAppointment: isPremiumAppointment,
|
|
};
|
|
|
|
return selectedTimeSlotInfo;
|
|
},
|
|
getSupportingItems() {
|
|
return store.getters.lineItems.supportingItems;
|
|
},
|
|
updateFooterButtonText(timeSlotInfo) {
|
|
let navbarButtonText;
|
|
if (!timeSlotInfo || !timeSlotInfo.timeSlot.date) {
|
|
navbarButtonText = "Continue";
|
|
} else {
|
|
navbarButtonText = `Select ${this.convertSelectedDateToShortMonthAndDay(
|
|
timeSlotInfo.timeSlot.date
|
|
)}`;
|
|
|
|
if (
|
|
this.appointmentType === AppointmentTypeStrings.IN_SHOP ||
|
|
this.appointmentType === AppointmentTypeStrings.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
|
|
) {
|
|
navbarButtonText += ` at ${this.getDisplayTextForMilitaryTime(
|
|
timeSlotInfo.timeSlot.startTime,
|
|
true
|
|
)} - ${this.getDisplayTextForMilitaryTime(
|
|
timeSlotInfo.timeSlot.endTime,
|
|
true
|
|
)}`;
|
|
}
|
|
}
|
|
this.$refs.navbar.updateButtonText(navbarButtonText);
|
|
},
|
|
convertSelectedDateToShortMonthAndDay(selectedDate) {
|
|
// This conversion ensures we don't get get GMT induced date changes
|
|
const dateObject = convertDateStringToDate(selectedDate.replace("-mobile", ""));
|
|
return dateObject.toLocaleDateString("en-us", { month: "short", day: "numeric" });
|
|
},
|
|
getDisplayTextForMilitaryTime(militaryTimeInput, shouldTrimMinutesIfEmpty = false) {
|
|
// Expected input: "HH:MM"
|
|
let hours = parseInt(militaryTimeInput.split(":")[0]);
|
|
const minutes = militaryTimeInput.split(":")[1];
|
|
const meridianNotation = hours > 11 ? "PM" : "AM";
|
|
|
|
if (hours > 12) hours -= 12;
|
|
if (shouldTrimMinutesIfEmpty && minutes === "00") {
|
|
return `${hours} ${meridianNotation}`;
|
|
} else {
|
|
return `${hours}:${minutes} ${meridianNotation}`;
|
|
}
|
|
},
|
|
backButtonAction() {
|
|
const payment = store.getters.order.payment;
|
|
if (
|
|
payment?.isInsurance &&
|
|
(payment?.insuranceCoverage?.isVerified ||
|
|
store.getters.order.referralNumber.length === 6)
|
|
) {
|
|
navigateToHeritageFunnel({
|
|
shouldSaveSession: false,
|
|
pageNameToLog: "service-location",
|
|
navType: "back",
|
|
});
|
|
} else {
|
|
this.$router.navigateWithoutSaving(
|
|
this.navigationScenarios.CLICKED_BACK,
|
|
this.pageName
|
|
);
|
|
}
|
|
},
|
|
async forwardButtonAction() {
|
|
this.navigatingForward = true;
|
|
var ctu = this.zipCodeCtu;
|
|
if (this.appointmentType === AppointmentTypeStrings.MOBILE) {
|
|
if (this.selectedProvider && this.selectedProvider.address) {
|
|
this.selectedProvider.address.streetAddress = null;
|
|
this.selectedProvider.address.city = null;
|
|
this.selectedProvider.address.state = null;
|
|
this.selectedProvider.address.zipCode = null;
|
|
this.selectedProvider.address.zipCodeCtu = null;
|
|
}
|
|
} else {
|
|
if (this.zipCodeCtu != this.selectedProvider.address.zipCodeCtu) {
|
|
ctu = this.selectedProvider.address.zipCodeCtu;
|
|
}
|
|
this.resetMobileLocation();
|
|
}
|
|
|
|
await this.dispatchStoreAction(
|
|
this.storeActions.SAVE_SERVICE_LOCATION,
|
|
{
|
|
address: this.streetAddress,
|
|
address2: this.apartmentNumberOrBusinessName,
|
|
city: this.city,
|
|
state: this.state,
|
|
zipCode: this.zipCode,
|
|
zipCodeCtu: ctu,
|
|
appointmentType: this.appointmentType,
|
|
isVehicleProtected: this.isVehicleProtected,
|
|
provider: {
|
|
providerNumber: this.selectedProvider?.providerNumber,
|
|
address: {
|
|
streetAddress: this.selectedProvider?.address?.streetAddress,
|
|
city: this.selectedProvider?.address?.city,
|
|
state: this.selectedProvider?.address?.state,
|
|
zipCode: this.selectedProvider?.address?.zipCode,
|
|
zipCodeCtu: this.selectedProvider?.address?.zipCodeCtu,
|
|
},
|
|
},
|
|
},
|
|
false
|
|
);
|
|
|
|
if (this.billToAccountNumber) {
|
|
this.dispatchStoreAction(
|
|
this.storeActions.SAVE_BILL_TO_ACCOUNT_NUMBER,
|
|
this.billToAccountNumber,
|
|
false
|
|
);
|
|
}
|
|
|
|
this.updateAndSaveSupportingItems();
|
|
this.updateAndSaveIsMSRFeeApplicable();
|
|
|
|
this.updateSupportingItems();
|
|
|
|
if (this.displayWaitList) {
|
|
let gaLabel = "";
|
|
let status = this.waitListRequested ? "checked" : "unchecked";
|
|
if (
|
|
this.waitListRequested === null &&
|
|
this.$store.state.order.customer.waitListRequested === true
|
|
) {
|
|
status = "checked";
|
|
}
|
|
const type = store.getters.isMobileAppointment ? "mobile" : "inshop";
|
|
gaLabel = `${status}_${type}`;
|
|
|
|
const currentDate = new Date();
|
|
const dateString = this.isMobileSelected
|
|
? this.selectableDatesMobile.days[0].date
|
|
: this.selectableDatesInshop.days[0].date;
|
|
const [year, month, day] = dateString.split("-").map(Number);
|
|
const appointmentDate = new Date(year, month - 1, day);
|
|
const timeDifference = appointmentDate - currentDate;
|
|
|
|
// Convert the time difference from milliseconds to days
|
|
const daysUntilAppointment = Math.ceil(timeDifference / (1000 * 60 * 60 * 24));
|
|
|
|
gaLabel +=
|
|
"_" +
|
|
daysUntilAppointment.toString() +
|
|
"_" +
|
|
store.getters.order.serviceLocation.zipCode +
|
|
"_" +
|
|
store.getters.order.serviceLocation.zipCodeCtu;
|
|
this.pushEventToGA("waitlist", "add_to_waitlist_check_box_status", gaLabel, true);
|
|
}
|
|
|
|
if (this.selectedTimeSlotInfo.timeSlot.jobMinMinutes == null) {
|
|
this.selectedTimeSlotInfo.timeSlot.jobMinMinutes = this.isMobileSelected
|
|
? this.selectableDatesMobile.estimatedServiceMinutesMinimum?.toString()
|
|
: this.selectableDatesInshop.estimatedServiceMinutesMinimum?.toString();
|
|
this.selectedTimeSlotInfo.timeSlot.jobMaxMinutes = this.isMobileSelected
|
|
? this.selectableDatesMobile.estimatedServiceMinutesMaximum?.toString()
|
|
: this.selectableDatesInshop.estimatedServiceMinutesMaximum?.toString();
|
|
}
|
|
|
|
this.dispatchStoreAction(
|
|
this.storeActions.SAVE_SCHEDULE,
|
|
this.selectedTimeSlotInfo.timeSlot,
|
|
false
|
|
);
|
|
|
|
if (this.waitListRequested !== null && this.waitListRequested !== undefined) {
|
|
this.dispatchStoreAction(
|
|
this.storeActions.SAVE_WAITLIST_REQUESTED,
|
|
this.waitListRequested,
|
|
false
|
|
);
|
|
}
|
|
|
|
if (this.appointmentType == AppointmentTypeStrings.MOBILE) {
|
|
//navigate to mobile details page
|
|
this.$router.navigateWithSaving(
|
|
this.navigationScenarios.CLICKED_FORWARD_WITH_MOBILE_SERVICE,
|
|
this.pageName
|
|
);
|
|
} else {
|
|
this.$router.navigateWithSaving(
|
|
this.navigationScenarios.CLICKED_FORWARD,
|
|
this.pageName
|
|
);
|
|
}
|
|
},
|
|
setDisplayWaitList() {
|
|
const dateString = this.isMobileSelected
|
|
? this.selectableDatesMobile?.days[0]?.date
|
|
: this.selectableDatesInshop?.days[0]?.date;
|
|
if (
|
|
experimentMixin.methods.hasSettingEqualTo(
|
|
experimentSettings.DISPLAY_WAITLIST,
|
|
"true"
|
|
) &&
|
|
dateString
|
|
) {
|
|
const [year, month, day] = dateString.split("-").map(Number);
|
|
const targetDate = new Date(year, month - 1, day);
|
|
const currentDate = new Date();
|
|
const futureDate = new Date(currentDate);
|
|
const experimentThresholdDays = experimentMixin.methods.hasSetting(
|
|
experimentSettings.WAITLIST_THRESHOLD_DAYS
|
|
)
|
|
? parseInt(
|
|
experimentMixin.methods.getSettingValue(
|
|
experimentSettings.WAITLIST_THRESHOLD_DAYS
|
|
)
|
|
)
|
|
: 0;
|
|
futureDate.setDate(currentDate.getDate() + experimentThresholdDays);
|
|
|
|
if (targetDate >= futureDate) {
|
|
this.displayWaitList = true;
|
|
} else {
|
|
this.displayWaitList = false;
|
|
}
|
|
}
|
|
},
|
|
updateSupportingItems() {
|
|
const supportingItems = this.getSupportingItems();
|
|
|
|
// if we have a pricing by day upcharge, then save/update supporting items with it
|
|
const pricingByDayUpchargeFeeIndex = supportingItems?.findIndex(
|
|
(item) => item.partType == PRICING_BY_DAY_PART_TYPE
|
|
);
|
|
|
|
if (this.includePricingByDayUpcharge && this.showPricingByDay) {
|
|
if (pricingByDayUpchargeFeeIndex && pricingByDayUpchargeFeeIndex > -1) {
|
|
supportingItems[pricingByDayUpchargeFeeIndex].laborAmount =
|
|
this.pricingByDayUpchargeLineItem.laborAmount;
|
|
supportingItems[pricingByDayUpchargeFeeIndex].sellingPrice =
|
|
this.pricingByDayUpchargeLineItem.sellingPrice;
|
|
supportingItems[pricingByDayUpchargeFeeIndex].kitPrice =
|
|
this.pricingByDayUpchargeLineItem.kitPrice;
|
|
} else {
|
|
supportingItems.push(this.pricingByDayUpchargeLineItem);
|
|
}
|
|
} else {
|
|
if (pricingByDayUpchargeFeeIndex >= 0) {
|
|
// remove pricing by day upcharge if it already was in store
|
|
supportingItems.splice(pricingByDayUpchargeFeeIndex, 1);
|
|
}
|
|
}
|
|
|
|
// if we have a premium fee(early bird), then save/update supporting items
|
|
if (
|
|
this.appointmentType === AppointmentTypeStrings.MOBILE &&
|
|
this.selectedTimeSlotInfo?.isPremiumAppointment
|
|
) {
|
|
const premiumFeeIndex = supportingItems.findIndex(
|
|
(item) => item.partType == PREMIUM_FEE_PART_TYPE
|
|
);
|
|
|
|
if (premiumFeeIndex > -1) {
|
|
supportingItems[premiumFeeIndex].laborAmount =
|
|
this.mobilePremiumAppointmentFee.laborAmount;
|
|
supportingItems[premiumFeeIndex].sellingPrice =
|
|
this.mobilePremiumAppointmentFee.sellingPrice;
|
|
supportingItems[premiumFeeIndex].kitPrice =
|
|
this.mobilePremiumAppointmentFee.kitPrice;
|
|
} else {
|
|
supportingItems.push(this.mobilePremiumAppointmentFee);
|
|
}
|
|
} else {
|
|
if (!supportingItems) {
|
|
return;
|
|
}
|
|
|
|
// if it's not a mobile and/or premium early bird, then make sure we remove any that may have been added
|
|
const removePremiumFeeIndex = supportingItems.findIndex(
|
|
(item) => item.partType == PREMIUM_FEE_PART_TYPE
|
|
);
|
|
|
|
if (removePremiumFeeIndex >= 0) {
|
|
supportingItems.splice(removePremiumFeeIndex, 1);
|
|
}
|
|
}
|
|
|
|
this.dispatchStoreAction(
|
|
this.storeActions.SAVE_SUPPORTING_ITEMS_SUPPRESSING_STATE_RESETTING,
|
|
supportingItems,
|
|
false
|
|
);
|
|
},
|
|
handleWaitListRequested(value) {
|
|
this.waitListRequested = value;
|
|
},
|
|
handleDateClicked(date) {
|
|
// do something to mark this as upcharge day or not...
|
|
if (date.isPricingByDayUpchargeDay) {
|
|
this.includePricingByDayUpcharge = true;
|
|
} else {
|
|
this.includePricingByDayUpcharge = false;
|
|
}
|
|
},
|
|
updateTimeSlot(timeSlotObj) {
|
|
this.selectedTimeSlotInfo = timeSlotObj;
|
|
if (
|
|
this.appointmentType !== AppointmentTypeStrings.MOBILE &&
|
|
this.appointmentType !== null
|
|
) {
|
|
// update apptType based on routeCode to determine if it should be dropoff or inshop
|
|
debugLog(`Updating appointment type based on route code.`);
|
|
debugLog(`Route code =`, timeSlotObj.timeSlot.routeCode);
|
|
debugLog(
|
|
`Parsed type = `,
|
|
this.getInShopOrDropOffApptType(timeSlotObj.timeSlot.routeCode)
|
|
);
|
|
this.appointmentType = this.getInShopOrDropOffApptType(
|
|
timeSlotObj.timeSlot.routeCode
|
|
);
|
|
}
|
|
},
|
|
getInShopOrDropOffApptType(routeCode) {
|
|
return isDropOffRouteCode(routeCode)
|
|
? AppointmentTypeStrings.DROP_OFF
|
|
: AppointmentTypeStrings.IN_SHOP;
|
|
},
|
|
getSelectedDate() {
|
|
let dateToSelect;
|
|
if (this.isMobileSelected) {
|
|
dateToSelect = returnFirstDate(this.selectableDatesMobile);
|
|
} else {
|
|
dateToSelect = returnFirstDate(this.selectableDatesInshop);
|
|
}
|
|
if (!dateToSelect) return null;
|
|
return this.isMobileSelected ? dateToSelect + "-mobile" : dateToSelect;
|
|
},
|
|
getSelectedDateForMobile() {
|
|
let dateToSelect = returnFirstDate(this.selectableDatesMobile);
|
|
if (!dateToSelect) return null;
|
|
return dateToSelect + "-mobile";
|
|
},
|
|
getSelectedDateForInshop() {
|
|
let dateToSelect = returnFirstDate(this.selectableDatesInshop);
|
|
if (!dateToSelect) return null;
|
|
return dateToSelect;
|
|
},
|
|
resetSelectedProvider() {
|
|
this.selectedProvider = new Provider();
|
|
this.updateSelectedProvider();
|
|
},
|
|
updateSelectedProvider(newProvider) {
|
|
if (newProvider) {
|
|
this.selectedProvider = newProvider;
|
|
this.initializeDatePicker();
|
|
} else if (this.appointmentType === this.appointmentTypeStrings.MOBILE) {
|
|
this.selectedProvider = {
|
|
providerNumber: this.shopProviderData.mobileProviderNumber.toString(),
|
|
address: {
|
|
streetAddress: null,
|
|
city: null,
|
|
state: null,
|
|
zipCode: null,
|
|
zipCodeCtu: null,
|
|
},
|
|
};
|
|
} else if (
|
|
!this.selectedProvider?.address?.streetAddress &&
|
|
this.shopProviderData?.shopProviders?.length
|
|
) {
|
|
this.selectedProvider = this.shopProviderData.shopProviders[0];
|
|
}
|
|
},
|
|
handleZipCodeChange(newZipCode) {
|
|
this.resetMobileLocation();
|
|
|
|
getShopProviderData(newZipCode.zipCode).then((result) => {
|
|
this.shopProviderData = result.data;
|
|
this.resetSelectedProvider();
|
|
this.state = newZipCode.state;
|
|
this.zipCode = newZipCode.zipCode;
|
|
this.zipCodeCtu = newZipCode.zipCodeCtu;
|
|
this.selectedDate = null;
|
|
this.initializeDatePicker();
|
|
});
|
|
},
|
|
updateSelectedProviderAndZipCode(newZipCode, newProvider) {
|
|
this.resetMobileLocation();
|
|
this.state = newZipCode.state;
|
|
this.zipCode = newZipCode.zipCode;
|
|
this.zipCodeCtu = newZipCode.zipCodeCtu;
|
|
this.zipContainsMilitaryBase = newZipCode.containsMilitaryBase;
|
|
this.selectedDate = null;
|
|
this.selectedProvider = newProvider;
|
|
this.initializeDatePicker();
|
|
},
|
|
handleAppointmentTypeChange(newAppointmentType) {
|
|
this.updateFooterButtonText();
|
|
if (newAppointmentType === AppointmentTypeStrings.MOBILE) {
|
|
this.appointmentType = AppointmentTypeStrings.MOBILE;
|
|
this.updateSelectedProvider();
|
|
} else if (newAppointmentType) {
|
|
if (this.selectedTimeSlotInfo?.timeSlot?.routeCode) {
|
|
// update appointmentType based on routeCode to determine if it should be dropoff or inshop
|
|
this.appointmentType = this.getInShopOrDropOffApptType(
|
|
this.selectedTimeSlotInfo.timeSlot.routeCode
|
|
);
|
|
} else {
|
|
this.appointmentType = AppointmentTypeStrings.IN_SHOP_OR_DROP_OFF;
|
|
}
|
|
|
|
// make sure a selectedProvider exists
|
|
this.updateSelectedProvider();
|
|
} else {
|
|
this.appointmentType = null;
|
|
}
|
|
this.selectedDate = this.getSelectedDate();
|
|
this.setDisplayWaitList();
|
|
},
|
|
},
|
|
watch: {
|
|
appointmentTypeFromAppointmentTypeQuestion: {
|
|
handler(newValue, oldValue) {
|
|
this.handleAppointmentTypeChange(newValue);
|
|
},
|
|
},
|
|
selectedDate(newValue, oldValue) {
|
|
// Clear time slot selection if date selected changes
|
|
if (newValue !== oldValue) {
|
|
this.selectedTimeSlotInfo = {
|
|
timeSlot: {
|
|
date: null,
|
|
routeCode: null,
|
|
startTime: null,
|
|
endTime: null,
|
|
jobMaxMinutes: null,
|
|
jobMinMinutes: null,
|
|
},
|
|
isPremiumAppointment: null,
|
|
};
|
|
}
|
|
},
|
|
selectedTimeSlotInfo(newValue) {
|
|
this.updateFooterButtonText(newValue);
|
|
},
|
|
},
|
|
components: {
|
|
funnelHeader,
|
|
navbar,
|
|
funnelSubHeader,
|
|
Form,
|
|
loadingModal,
|
|
datePicker,
|
|
locationAlerts,
|
|
textBlock,
|
|
|
|
alert,
|
|
serviceZipModalQuestion,
|
|
appointmentTypeQuestion,
|
|
contentGroupModal,
|
|
shopQuestionPopup,
|
|
buttonQuestion,
|
|
},
|
|
};
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
.container-fluid {
|
|
&.page-schedule {
|
|
padding: 0 1rem;
|
|
.text-link-small {
|
|
a,
|
|
.btn-link {
|
|
width: auto;
|
|
margin: 0 auto;
|
|
height: auto;
|
|
font-size: 0.875rem;
|
|
line-height: 1.75;
|
|
padding: 0;
|
|
border-radius: 0;
|
|
&:focus {
|
|
outline: 1px solid $blue;
|
|
}
|
|
@include media-breakpoint-up(md) {
|
|
font-size: 1rem;
|
|
}
|
|
}
|
|
}
|
|
.funnel-sub-header {
|
|
h5.dark-header {
|
|
margin-bottom: 0.25rem;
|
|
}
|
|
}
|
|
.change-location a {
|
|
font-family: UrbanistSemibold;
|
|
}
|
|
.date-picker {
|
|
border-top: 1px solid $gray-500;
|
|
margin-top: 1.5rem;
|
|
}
|
|
.time-slots-question {
|
|
padding: 0 0.75rem;
|
|
}
|
|
}
|
|
}
|
|
.shop-question-button .list-button-content {
|
|
background: $blue-100;
|
|
box-shadow: 0 0 0 1px $primary;
|
|
}
|
|
</style>
|