Merge branch 'release/2026.01.15' into rlsmerge/2026.01.15-to-develop
This commit is contained in:
commit
f02f9d7335
15 changed files with 419 additions and 73 deletions
|
|
@ -12,6 +12,7 @@ const partTypeStrings = {
|
|||
SERVICE_PACKAGE_DISCOUNT: "SERVICE PACKAGE DISCOUNT",
|
||||
QUOTE_PAGE_DISCOUNT: "QUOTE PAGE DISCOUNT",
|
||||
DONATION: "DONATION",
|
||||
WINDSHIELD: "WINDSHIELD",
|
||||
};
|
||||
|
||||
export { partTypeStrings };
|
||||
|
|
|
|||
|
|
@ -111,6 +111,7 @@ const storeActions = {
|
|||
SAVE_PAYPAL_TOKEN: "savePaypalToken",
|
||||
SAVE_NEXTGEN_SETTLED_AMOUNT: "saveNextGenSettledAmount",
|
||||
SAVE_IS_RECAL_ACK_OPT_IN: "saveIsRecalAckOptIn",
|
||||
SAVE_IS_OEM_GLASS_SELECTED: "saveIsOemGlassSelected",
|
||||
SAVE_IS_MSR_FEE_APPLICABLE: "saveIsMSRFeeApplicable",
|
||||
|
||||
CREATE_SUBMITTED_STATE: "createSubmittedState",
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@ const storeMutations = {
|
|||
LOCK_TOKEN: "updateLockToken",
|
||||
UPDATE_SETTLED_TENDER_AMOUNT: "updateSettledTenderAmount",
|
||||
UPDATE_IS_RECAL_ACK_OPT_IN: "updateIsRecalAckOptIn",
|
||||
UPDATE_IS_OEM_GLASS_SELECTED: "updateIsOemGlassSelected",
|
||||
UPDATE_IS_MSR_FEE_APPLICABLE: "updateIsMSRFeeApplicable",
|
||||
UPDATE_CASH_PRICE_SUBTOTAL: "updateCashPriceSubTotal",
|
||||
|
||||
|
|
|
|||
|
|
@ -281,7 +281,7 @@ export default {
|
|||
if (event.screenX === 0 && event.screenY === 0) {
|
||||
return;
|
||||
}
|
||||
this.$emit("date-selected", date);
|
||||
this.$emit("date-clicked", date);
|
||||
},
|
||||
getWeekStartDate(dateString) {
|
||||
const date = convertDateStringToDate(dateString);
|
||||
|
|
@ -846,6 +846,12 @@ export default {
|
|||
},
|
||||
},
|
||||
watch: {
|
||||
isLoading(newValue) {
|
||||
// if done loading dates, then set to first available date
|
||||
if (newValue === false && this.firstAvailableSelectableDate) {
|
||||
this.selectedDate = this.firstAvailableSelectableDate;
|
||||
}
|
||||
},
|
||||
modelValue(newValue) {
|
||||
this.resetField({
|
||||
value: newValue,
|
||||
|
|
|
|||
|
|
@ -306,6 +306,12 @@ describe("partQuestions.vue...", () => {
|
|||
//Assert
|
||||
expect(spy).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
storeActions.SAVE_IS_OEM_GLASS_SELECTED,
|
||||
false,
|
||||
false
|
||||
);
|
||||
expect(spy).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
"savePartQuestionAnswers",
|
||||
[
|
||||
{
|
||||
|
|
|
|||
|
|
@ -188,6 +188,8 @@ export default {
|
|||
}
|
||||
});
|
||||
|
||||
this.dispatchStoreAction(this.storeActions.SAVE_IS_OEM_GLASS_SELECTED, false, false);
|
||||
|
||||
// save to vuex store as order.damage.partQuestionAnswers (array)
|
||||
// used in GET_PARTS call following this one
|
||||
await this.dispatchStoreAction(
|
||||
|
|
|
|||
167
src/layouts/quote/oem-glass-question/oem-glass-question.vue
Normal file
167
src/layouts/quote/oem-glass-question/oem-glass-question.vue
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
<template>
|
||||
<div
|
||||
class="oem-glass-question-container py-5 mt-3 mb-6"
|
||||
:class="[isExpanded ? 'expanded' : '']">
|
||||
<div class="oem-glass-question-header" @click="toggleIsExpanded">
|
||||
{{ headerText }}
|
||||
</div>
|
||||
<div class="oem-glass-question-body">
|
||||
<div class="oem-glass-question-copy" v-html="bodyText"></div>
|
||||
<div class="oem-glass-question-option">
|
||||
<checkboxQuestion
|
||||
class="mt-3 ms-1"
|
||||
cmsWidgetName="OemGlassInputQuestionWidget"
|
||||
v-model="installOemGlass" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import checkboxQuestion from "@/digital-components/checkbox-question/checkbox-question";
|
||||
|
||||
export default {
|
||||
name: "oem-part-question",
|
||||
props: {
|
||||
cmsWidgetName: String,
|
||||
modelValue: Boolean,
|
||||
oemGlassPartPrice: Number,
|
||||
aftermarketGlassPartPrice: Number,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isExpanded: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
headerText() {
|
||||
return this.getCmsContent(this.cmsWidgetName, "HeaderText");
|
||||
},
|
||||
bodyText() {
|
||||
let priceDifference = this.oemGlassPartPrice - this.aftermarketGlassPartPrice;
|
||||
let bodyText = this.getCmsContent(this.cmsWidgetName, "BodyText");
|
||||
bodyText = bodyText.replace(
|
||||
"{custom:OEMGLASSPRICE}",
|
||||
"<span id='price-dif'>$" + priceDifference.toFixed(2) + "</span>"
|
||||
);
|
||||
return bodyText;
|
||||
},
|
||||
footerText() {
|
||||
return this.getCmsContent(this.cmsWidgetName, "FooterText");
|
||||
},
|
||||
installOemGlass: {
|
||||
get() {
|
||||
return this.modelValue;
|
||||
},
|
||||
set(newValue) {
|
||||
this.$emit("update:modelValue", newValue);
|
||||
},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
toggleIsExpanded() {
|
||||
this.setIsExpanded(!this.isExpanded);
|
||||
},
|
||||
setIsExpanded(newVal) {
|
||||
this.isExpanded = newVal;
|
||||
},
|
||||
},
|
||||
components: {
|
||||
checkboxQuestion,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.oem-glass-question-container {
|
||||
border-top: 1px solid gray;
|
||||
|
||||
@include media-breakpoint-up(lg) {
|
||||
margin: 0 2rem;
|
||||
}
|
||||
|
||||
.oem-glass-question-header {
|
||||
font-family: UrbanistSemibold, Arial, Helvetica, sans-serif;
|
||||
color: $blue;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
cursor: pointer;
|
||||
|
||||
span {
|
||||
flex-grow: 1;
|
||||
color: $black;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
transition: all 0.5s ease;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 9'%3E%3Cpath d='M7.99282 0.117702C7.75716 0.116049 7.53044 0.207797 7.3623 0.37286L0.255553 7.46878C0.0862976 7.63796 -0.00878906 7.86742 -0.00878906 8.10667C-0.00878906 8.34593 0.0862976 8.57539 0.255553 8.74457C0.424808 8.91374 0.654368 9.00879 0.893731 9.00879C1.13309 9.00879 1.36265 8.91374 1.53191 8.74457L7.99282 2.27378L14.4614 8.74457C14.6307 8.91205 14.8595 9.00547 15.0977 9.00428C15.3359 9.00308 15.5638 8.90737 15.7314 8.73819C15.8989 8.56901 15.9924 8.34022 15.9912 8.10216C15.99 7.8641 15.8942 7.63627 15.725 7.46878L8.6259 0.377963C8.45765 0.21088 8.22999 0.117289 7.99282 0.117702Z' fill='%230070d1'/%3E%3C/svg%3E");
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
color: $blue;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
display: inline-flex;
|
||||
position: relative;
|
||||
margin-left: 0.5rem;
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
|
||||
.oem-glass-question-body {
|
||||
max-height: 0;
|
||||
transition: all 350ms ease-in;
|
||||
overflow: hidden;
|
||||
visibility: hidden;
|
||||
|
||||
.oem-glass-question-option {
|
||||
margin-top: 0.5em;
|
||||
font-family: UrbanistSemibold, Arial, Helvetica, sans-serif;
|
||||
|
||||
.oem-glass-question-strikethrough-price {
|
||||
font-size: smaller;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.oem-glass-question-free-price {
|
||||
display: inline-block;
|
||||
font-weight: bolder;
|
||||
border-radius: 1em;
|
||||
background-color: $green-300;
|
||||
padding: 0 0.5em;
|
||||
margin-inline-start: 0.5em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.expanded {
|
||||
.oem-glass-question-header {
|
||||
&::after {
|
||||
transform: unset;
|
||||
}
|
||||
}
|
||||
|
||||
.oem-glass-question-body {
|
||||
visibility: visible;
|
||||
max-height: none;
|
||||
padding-top: 0.5em;
|
||||
margin-top: 0.5em;
|
||||
|
||||
.oem-glass-question-copy {
|
||||
#price-dif {
|
||||
font-weight: $font-weight-700;
|
||||
color: $black;
|
||||
}
|
||||
}
|
||||
|
||||
.oem-glass-question-option {
|
||||
.small {
|
||||
font-weight: $font-weight-700;
|
||||
color: $black;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -118,6 +118,9 @@ jest.mock("@/mixins/experiment-mixin.js", () => ({
|
|||
if (settingName === mockExperimentSettings.DISPLAY_AFTERPAY_BREAKOUT_DISPLAY) {
|
||||
return false;
|
||||
}
|
||||
if (settingName === mockExperimentSettings.OFFER_OEM) {
|
||||
return false;
|
||||
}
|
||||
return "test";
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -86,6 +86,13 @@
|
|||
isInsuranceSelected ? isInsuranceContinueButtonText : ''
|
||||
" />
|
||||
|
||||
<oemGlassQuestion
|
||||
cmsWidgetName="OemGlassQuestionWidget"
|
||||
v-if="offerOem && !isInsuranceSelected"
|
||||
v-model="isOemGlassSelected"
|
||||
:oemGlassPartPrice="oemGlassPartPrice"
|
||||
:aftermarketGlassPartPrice="aftermarketGlassPartPrice" />
|
||||
|
||||
<saveProgressModalQuestion
|
||||
modalWidgetName="SaveProgressModalWidget"
|
||||
modalName="SaveProgressModal"
|
||||
|
|
@ -129,6 +136,7 @@ import afterpayModalBanner from "@/layouts/quote/afterpay-modal-banner/afterpay-
|
|||
import recalDisclaimer from "@/layouts/quote/recal-disclaimer/recal-disclaimer.vue";
|
||||
import saveProgressModalQuestion from "@/fmg-components/save-progress-modal-question/save-progress-modal-question";
|
||||
import saveProgressPopupQuestion from "@/fmg-components/save-progress-popup-question/save-progress-popup-question";
|
||||
import oemGlassQuestion from "./oem-glass-question/oem-glass-question.vue";
|
||||
|
||||
// Supporting files
|
||||
import baseMixin from "@/mixins/base-mixin.js";
|
||||
|
|
@ -218,19 +226,15 @@ export default {
|
|||
null,
|
||||
"quote"
|
||||
);
|
||||
|
||||
// call API oem-after-market-parts method
|
||||
const offerOem = experimentMixin.methods.hasSettingEqualTo(
|
||||
const experimentAllowsOfferOem = experimentMixin.methods.hasSettingEqualTo(
|
||||
experimentSettings.OFFER_OEM,
|
||||
"true"
|
||||
);
|
||||
if (offerOem) {
|
||||
const oemAfterMarketParts = baseMixin.methods.dispatchStoreActionWithLogging(
|
||||
storeActions.GET_OEM_AFTERMARKET_PARTS,
|
||||
null,
|
||||
"quote"
|
||||
);
|
||||
}
|
||||
const oemGlassCheckPromise = baseMixin.methods.dispatchStoreActionWithLogging(
|
||||
storeActions.GET_OEM_AFTERMARKET_PARTS,
|
||||
null,
|
||||
"quote"
|
||||
);
|
||||
|
||||
const shouldExposeSkipQuote = store.getters.isRecalibrationOnOrder;
|
||||
const skipQuoteExperiment = store.getters.applicationUser.experiments.find(
|
||||
|
|
@ -304,6 +308,10 @@ export default {
|
|||
resultKey: "supportingItems",
|
||||
promise: supportingItemsPromise,
|
||||
},
|
||||
{
|
||||
resultKey: "oemGlassCheck",
|
||||
promise: oemGlassCheckPromise,
|
||||
},
|
||||
];
|
||||
|
||||
const resultMap = await settleAllPromises(promiseResultMap);
|
||||
|
|
@ -325,6 +333,18 @@ export default {
|
|||
lineItems.vaps = lineItems.vaps ?? [];
|
||||
const nullSafeGlassParts = lineItems.glassParts ?? [];
|
||||
|
||||
const oemGlassCheck = resultMap.oemGlassCheck;
|
||||
const offerOem = experimentAllowsOfferOem ? oemGlassCheck?.offerOem : false;
|
||||
const oemGlassPieceParts = oemGlassCheck?.glassPieceParts;
|
||||
const oemGlassParts =
|
||||
oemGlassPieceParts && oemGlassPieceParts.length > 0
|
||||
? oemGlassPieceParts[0].parts
|
||||
: null;
|
||||
const isOemGlassSelected = store.getters.order.damage?.installOemGlass ?? false;
|
||||
|
||||
let alternateGlassPartsForOem;
|
||||
if (oemGlassParts) alternateGlassPartsForOem = oemGlassParts;
|
||||
|
||||
const quotePageDiscountPartsInfo = getAvailableQuotePageDiscounts();
|
||||
|
||||
const quotePageDiscountParts = quotePageDiscountPartsInfo.map((partInfo) => ({
|
||||
|
|
@ -342,6 +362,18 @@ export default {
|
|||
...quotePageDiscountParts,
|
||||
];
|
||||
|
||||
let pricedAlternateGlassParts = null;
|
||||
if (alternateGlassPartsForOem && alternateGlassPartsForOem.length > 0) {
|
||||
pricedAlternateGlassParts = await baseMixin.methods.dispatchStoreActionWithLogging(
|
||||
storeActions.PRICE_ORDER_ITEMS_AND_SAVE_SERVER_DATA,
|
||||
{
|
||||
availableLineItems: alternateGlassPartsForOem,
|
||||
},
|
||||
"quote",
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
// When calling pricing from the quote page, always use the cash parent account but save the existing one in case it's unverified insurance navigating backwards
|
||||
var holdParentAccountNumber = store.getters.order.payment?.parentAccountNumber;
|
||||
var holdBillToAccountNumber = store.getters.order.payment?.billToAccountNumber;
|
||||
|
|
@ -403,6 +435,41 @@ export default {
|
|||
vm.holdParentAccountNumber = holdParentAccountNumber;
|
||||
vm.holdBillToAccountNumber = holdBillToAccountNumber;
|
||||
vm.skipToInsurance = shouldSkipToInsurance;
|
||||
vm.offerOem = offerOem;
|
||||
vm.alternateGlassPartsForOem = pricedAlternateGlassParts; // oemParts
|
||||
vm.isOemGlassSelected = isOemGlassSelected;
|
||||
|
||||
const getOEMGlassPartPrice = () => {
|
||||
let price = 0;
|
||||
if (vm.alternateGlassPartsForOem) {
|
||||
let oemGlassPart = vm.isOemGlassSelected
|
||||
? vm.lineItems.glassParts
|
||||
: vm.alternateGlassPartsForOem;
|
||||
if (oemGlassPart) {
|
||||
price =
|
||||
oemGlassPart.find(
|
||||
(altPart) => altPart.partType === partTypeStrings.WINDSHIELD
|
||||
)?.sellingPrice || 0;
|
||||
}
|
||||
}
|
||||
return price;
|
||||
};
|
||||
const getAftermarketGlassPartPrice = () => {
|
||||
let price = 0;
|
||||
if (vm.alternateGlassPartsForOem) {
|
||||
let aftermarketGlassPart = vm.isOemGlassSelected
|
||||
? vm.alternateGlassPartsForOem
|
||||
: vm.lineItems.glassParts;
|
||||
if (aftermarketGlassPart) {
|
||||
price = aftermarketGlassPart.find(
|
||||
(altPart) => altPart.partType === partTypeStrings.WINDSHIELD
|
||||
)?.sellingPrice;
|
||||
}
|
||||
}
|
||||
return price;
|
||||
};
|
||||
vm.oemGlassPartPrice = getOEMGlassPartPrice();
|
||||
vm.aftermarketGlassPartPrice = getAftermarketGlassPartPrice();
|
||||
|
||||
if (revalidatePromoResponse) {
|
||||
const revalidateAlerts = buildToastMessagesFromRevalidateOrValidatePromoResponse(
|
||||
|
|
@ -570,6 +637,8 @@ export default {
|
|||
availableLineItems: null,
|
||||
lineItems: [],
|
||||
addableVaps: [],
|
||||
offerOem: null,
|
||||
alternateGlassPartsForOem: null,
|
||||
servicePackage: null,
|
||||
holdParentAccountNumber: null,
|
||||
holdBillToAccountNumber: null,
|
||||
|
|
@ -579,6 +648,7 @@ export default {
|
|||
packageNumber: null,
|
||||
skipToInsurance: null,
|
||||
servicePackageSelected: null,
|
||||
isOemGlassSelected: false,
|
||||
};
|
||||
},
|
||||
async mounted() {
|
||||
|
|
@ -743,6 +813,20 @@ export default {
|
|||
this.lineItems.supportingItems = this.filterOutFees(this.lineItems.supportingItems);
|
||||
}
|
||||
|
||||
if (
|
||||
this.lineItems.glassParts &&
|
||||
this.alternateGlassPartsForOem &&
|
||||
this.alternateGlassPartsForOem.length > 0
|
||||
) {
|
||||
let isOEMGlassOnOrder = this.isOEMGlassOnOrder(this.lineItems.glassParts);
|
||||
if (
|
||||
(this.isOemGlassSelected && !isOEMGlassOnOrder) ||
|
||||
(!this.isOemGlassSelected && isOEMGlassOnOrder)
|
||||
) {
|
||||
this.swapOEMGlassPartsOnOrder();
|
||||
}
|
||||
}
|
||||
|
||||
if (this.lineItems.glassParts?.length > 0) {
|
||||
this.dispatchStoreAction(
|
||||
this.storeActions.SAVE_GLASS_PARTS_SUPPRESSING_STATE_RESETTING,
|
||||
|
|
@ -775,6 +859,11 @@ export default {
|
|||
},
|
||||
false
|
||||
);
|
||||
this.dispatchStoreAction(
|
||||
this.storeActions.SAVE_IS_OEM_GLASS_SELECTED,
|
||||
this.isOemGlassSelected,
|
||||
false
|
||||
);
|
||||
|
||||
await savePageData(
|
||||
this.pageName,
|
||||
|
|
@ -893,6 +982,26 @@ export default {
|
|||
const alert = createPromoSuccessAlert(lineItems.promos[0].promoCode);
|
||||
this.$refs.funnelHeader.pushGlobalAlert(alert, alert.shouldAutoFade);
|
||||
},
|
||||
isOEMGlassOnOrder(glassParts) {
|
||||
if (glassParts && glassParts.length > 0) {
|
||||
return glassParts.some((part) => part.partNumber.endsWith("OEM"));
|
||||
}
|
||||
return false;
|
||||
},
|
||||
swapOEMGlassPartsOnOrder() {
|
||||
this.lineItems.glassParts.forEach((part, index) => {
|
||||
if (part.partType === partTypeStrings.WINDSHIELD) {
|
||||
let altPartToUse = this.alternateGlassPartsForOem.find(
|
||||
(altPart) => altPart.partType === partTypeStrings.WINDSHIELD
|
||||
);
|
||||
if (altPartToUse) {
|
||||
let childParts = this.lineItems.glassParts[index].childParts;
|
||||
altPartToUse.childParts = childParts;
|
||||
this.lineItems.glassParts[index] = altPartToUse;
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
components: {
|
||||
funnelHeader,
|
||||
|
|
@ -909,6 +1018,7 @@ export default {
|
|||
recalDisclaimer,
|
||||
saveProgressModalQuestion,
|
||||
saveProgressPopupQuestion,
|
||||
oemGlassQuestion,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@
|
|||
cmsWidgetName="ScheduleYourServiceWidget"
|
||||
id="schedule-your-service" />
|
||||
<appointmentTypeQuestion
|
||||
v-if="hasSelectableDatesLoaded"
|
||||
v-model="appointmentTypeFromAppointmentTypeQuestion"
|
||||
v-show="isAppointmentTypeDisplayed"
|
||||
:isServiceableMobile="isServiceableMobile"
|
||||
|
|
@ -134,6 +133,8 @@
|
|||
:isOvernightDropoff="isOvernightDropoff" />
|
||||
</div>
|
||||
<datePicker
|
||||
:currentZip="zipCode"
|
||||
:currentProviderNumber="selectedProvider?.providerNumber"
|
||||
v-show="appointmentType"
|
||||
customComponentId="dateQuestion"
|
||||
selectableDatesSetting="custom"
|
||||
|
|
@ -143,7 +144,7 @@
|
|||
class="text-link-small"
|
||||
:getMoreDatesCallback="getMoreScheduleData"
|
||||
validationRules="date-required"
|
||||
@date-selected="handleDateSelected"
|
||||
@date-clicked="handleDateClicked"
|
||||
:pricingByDayBasePrice="pricingByDayBasePrice"
|
||||
:pricingByDayUpcharge="pricingByDayUpcharge"
|
||||
:showPricingByDay="showPricingByDay"
|
||||
|
|
@ -639,12 +640,12 @@ export default {
|
|||
showTimeSlotQuestion() {
|
||||
if (
|
||||
this.selectedDate &&
|
||||
this.appointmentTypeFromAppointmentTypeQuestion &&
|
||||
this.timeSlotsForSelectedDate
|
||||
this.selectedDate.includes("mobile") &&
|
||||
!this.isMobileSelected
|
||||
) {
|
||||
return true;
|
||||
this.setSelectedDateToFirstAvailable();
|
||||
}
|
||||
return false;
|
||||
return this.selectedDate && this.appointmentTypeFromAppointmentTypeQuestion;
|
||||
},
|
||||
isMobileStaticRecalibrationApplicable() {
|
||||
return (
|
||||
|
|
@ -872,13 +873,6 @@ export default {
|
|||
? this.$refs.mobileFirstModal?.getIsModalOpen()
|
||||
: false;
|
||||
},
|
||||
hasSelectableDatesLoaded() {
|
||||
if (this.isMobileSelected) {
|
||||
return this.selectableDatesMobile?.days?.length > 0;
|
||||
} else {
|
||||
return this.selectableDatesInshop?.days?.length > 0;
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
splitCopyOnCMSPlaceHolder,
|
||||
|
|
@ -1140,6 +1134,29 @@ export default {
|
|||
);
|
||||
}
|
||||
},
|
||||
getTimeSlotInfo() {
|
||||
// Get the current date
|
||||
const currentDate = getTodayDate();
|
||||
|
||||
// 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;
|
||||
|
|
@ -1192,6 +1209,8 @@ export default {
|
|||
async initializeDatePicker() {
|
||||
this.isShowMobileFirstAppt && this.showLoadingModal();
|
||||
|
||||
this.selectedDate = null;
|
||||
|
||||
const includeMobileTimeSlots = this.isServiceableMobile;
|
||||
const includeInshopTimeSlots = this.isServiceableInshop || this.isServiceableDropoff;
|
||||
const datePickerInitialData = await this.$refs.datePicker.loadInitialData({
|
||||
|
|
@ -1582,40 +1601,28 @@ export default {
|
|||
handleWaitListRequested(value) {
|
||||
this.waitListRequested = value;
|
||||
},
|
||||
handleDateSelected(date) {
|
||||
const previousDate = this.selectedDate;
|
||||
|
||||
// check if date actually changed
|
||||
if (previousDate !== date.dateString) {
|
||||
this.selectedTimeSlotInfo = this.getEmptyTimeSlot();
|
||||
this.updateFooterButtonText(this.selectedTimeSlotInfo);
|
||||
handleDateClicked(date) {
|
||||
// do something to mark this as upcharge day or not...
|
||||
if (date.isPricingByDayUpchargeDay) {
|
||||
this.includePricingByDayUpcharge = true;
|
||||
} else {
|
||||
this.includePricingByDayUpcharge = false;
|
||||
}
|
||||
|
||||
// TODO: REMOVE AS PART OF CASH-1634
|
||||
// // do something to mark this as upcharge day or not...
|
||||
// if (date.isPricingByDayUpchargeDay) {
|
||||
// this.includePricingByDayUpcharge = true;
|
||||
// } else {
|
||||
// this.includePricingByDayUpcharge = false;
|
||||
// }
|
||||
},
|
||||
getEmptyTimeSlot() {
|
||||
return {
|
||||
timeSlot: {
|
||||
date: null,
|
||||
routeCode: null,
|
||||
startTime: null,
|
||||
endTime: null,
|
||||
jobMaxMinutes: null,
|
||||
jobMinMinutes: null,
|
||||
},
|
||||
isPremiumAppointment: null,
|
||||
};
|
||||
},
|
||||
updateTimeSlot(timeSlotObj) {
|
||||
if (!timeSlotObj?.routeCode) {
|
||||
this.appointmentType = AppointmentTypeStrings.IN_SHOP_OR_DROP_OFF;
|
||||
this.selectedTimeSlotInfo = this.getEmptyTimeSlot();
|
||||
this.selectedTimeSlotInfo = {
|
||||
timeSlot: {
|
||||
date: null,
|
||||
routeCode: null,
|
||||
startTime: null,
|
||||
endTime: null,
|
||||
jobMaxMinutes: null,
|
||||
jobMinMinutes: null,
|
||||
},
|
||||
isPremiumAppointment: null,
|
||||
};
|
||||
return;
|
||||
}
|
||||
const timeSlot = this.timeSlotsForSelectedDate?.timeSlots?.find(
|
||||
|
|
@ -1714,9 +1721,6 @@ export default {
|
|||
},
|
||||
handleAppointmentTypeChange(newAppointmentType) {
|
||||
this.updateFooterButtonText();
|
||||
// if time appointment type changes, clear any selected time slot
|
||||
this.selectedTimeSlotInfo = this.getEmptyTimeSlot();
|
||||
|
||||
if (newAppointmentType === AppointmentTypeStrings.MOBILE) {
|
||||
// Remember last shop selected if previous selection was inshop/dropoff
|
||||
if (
|
||||
|
|
@ -1725,6 +1729,17 @@ export default {
|
|||
AppointmentTypeStrings.IN_SHOP_OR_DROP_OFF) &&
|
||||
this.selectedProvider
|
||||
) {
|
||||
this.selectedTimeSlotInfo = {
|
||||
timeSlot: {
|
||||
date: null,
|
||||
routeCode: null,
|
||||
startTime: null,
|
||||
endTime: null,
|
||||
jobMaxMinutes: null,
|
||||
jobMinMinutes: null,
|
||||
},
|
||||
isPremiumAppointment: null,
|
||||
};
|
||||
this.lastSelectedInshopOrDropoffProvider = this.selectedProvider;
|
||||
}
|
||||
this.appointmentType = AppointmentTypeStrings.MOBILE;
|
||||
|
|
@ -1751,8 +1766,8 @@ export default {
|
|||
} else {
|
||||
this.appointmentType = null;
|
||||
}
|
||||
this.setSelectedDateToFirstAvailable(); // v-if on appointmentTypeQuestion ensures dates have been loaded by now
|
||||
this.setDisplayWaitList(); // v-if on appointmentTypeQuestion ensures dates have been loaded by now
|
||||
this.setSelectedDateToFirstAvailable();
|
||||
this.setDisplayWaitList();
|
||||
},
|
||||
setSelectedDateToFirstAvailable() {
|
||||
this.selectedDate = this.getFirstAvailableDate();
|
||||
|
|
@ -1884,9 +1899,35 @@ export default {
|
|||
watch: {
|
||||
appointmentTypeFromAppointmentTypeQuestion: {
|
||||
handler(newValue, oldValue) {
|
||||
this.handleAppointmentTypeChange(newValue); // v-if on appointmentTypeQuestion ensures dates have been loaded by now
|
||||
this.handleAppointmentTypeChange(newValue);
|
||||
},
|
||||
},
|
||||
selectedDate(newValue, oldValue) {
|
||||
// Clear time slot selection if date selected changes
|
||||
const selectedDate = this.getSelectedDateFromStore();
|
||||
if (oldValue && this.appointmentType === AppointmentTypeStrings.MOBILE) {
|
||||
oldValue = `${oldValue}-mobile`;
|
||||
}
|
||||
const hasValueChanged = newValue !== oldValue;
|
||||
const isDateDifferent = (newValue || oldValue) !== selectedDate;
|
||||
|
||||
if (hasValueChanged && isDateDifferent && !this.selectedMobileFirstAppointment) {
|
||||
this.selectedTimeSlotInfo = {
|
||||
timeSlot: {
|
||||
date: null,
|
||||
routeCode: null,
|
||||
startTime: null,
|
||||
endTime: null,
|
||||
jobMaxMinutes: null,
|
||||
jobMinMinutes: null,
|
||||
},
|
||||
isPremiumAppointment: null,
|
||||
};
|
||||
}
|
||||
},
|
||||
selectedTimeSlotInfo(newValue) {
|
||||
this.updateFooterButtonText(newValue);
|
||||
},
|
||||
},
|
||||
components: {
|
||||
funnelHeader,
|
||||
|
|
@ -1899,6 +1940,7 @@ export default {
|
|||
textBlock,
|
||||
timeSlotQuestion,
|
||||
mobileFirstModal,
|
||||
|
||||
alert,
|
||||
serviceZipModalQuestion,
|
||||
appointmentTypeQuestion,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
<template>
|
||||
<div class="time-slots-question">
|
||||
<buttonQuestion
|
||||
ref="chooseDropOffOrInshop"
|
||||
v-if="isDropOffAppointmentAvailable"
|
||||
v-model="selectedAnswerForDropOffOrInshop"
|
||||
@update:modelValue="dropOffSelectionChanged"
|
||||
|
|
@ -24,9 +23,8 @@
|
|||
</div>
|
||||
</buttonQuestion>
|
||||
<buttonQuestion
|
||||
ref="chooseTimeSlot"
|
||||
v-if="displayTimeSlotQuestion"
|
||||
v-model="selectedAnswerForTimeSlots"
|
||||
ref="buttonQuestion"
|
||||
v-if="shouldDisplayTimeSlotQuestion"
|
||||
@update:modelValue="timeSlotSelectionChanged"
|
||||
buttonTypeString="timeSlotModalListButton"
|
||||
:buttonTypeObject="timeSlotModalListButton"
|
||||
|
|
@ -36,12 +34,10 @@
|
|||
isInshopOrDropOffAppointment ? 'is-inshop-or-drop-off-appointment' : '',
|
||||
]"
|
||||
:answers="availableTimeSlots"
|
||||
:lazyLoad="{
|
||||
isLazyLoad: !isMobileAppointment && !selectedAnswerForTimeSlots,
|
||||
amountToLoad: 10,
|
||||
}"
|
||||
:lazyLoad="{ isLazyLoad: !isMobileAppointment, amountToLoad: 10 }"
|
||||
groupName="chooseTimeSlot"
|
||||
textPosition="text-center"
|
||||
v-model="selectedAnswerForTimeSlots"
|
||||
questionText="Available times:"
|
||||
isRequired
|
||||
validationRules="time-slot-required" />
|
||||
|
|
@ -393,7 +389,7 @@ export default {
|
|||
this.answersForDropOffQuestion[0].value !== PICK_A_TIME_BUTTON_VALUE
|
||||
);
|
||||
},
|
||||
displayTimeSlotQuestion() {
|
||||
shouldDisplayTimeSlotQuestion() {
|
||||
return (
|
||||
this.selectedAnswerForDropOffOrInshop == PICK_A_TIME_BUTTON_VALUE ||
|
||||
!this.isDropOffAppointmentAvailable
|
||||
|
|
|
|||
|
|
@ -80,7 +80,6 @@ export default {
|
|||
},
|
||||
set: function (newValue) {
|
||||
this.$emit("update:modelValue", newValue);
|
||||
this.$emit("handle-appointment-type-change", newValue);
|
||||
},
|
||||
},
|
||||
isMobileOnly() {
|
||||
|
|
|
|||
|
|
@ -198,6 +198,8 @@ export default {
|
|||
false
|
||||
);
|
||||
|
||||
this.dispatchStoreAction(this.storeActions.SAVE_IS_OEM_GLASS_SELECTED, false, false);
|
||||
|
||||
// Navigate to the next page
|
||||
this.navigateForward(matchedParts);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -122,6 +122,7 @@ const getDefaultState = () => {
|
|||
capabilityQuestionAnswers: null,
|
||||
dateOfLoss: null,
|
||||
damageCause: null,
|
||||
installOemGlass: null,
|
||||
},
|
||||
lineItems: {
|
||||
glassParts: null,
|
||||
|
|
@ -342,6 +343,9 @@ export const mutations = {
|
|||
updateIsRecalAckOptIn(state, isRecalAckOptIn) {
|
||||
state.order.isRecalAckOptIn = isRecalAckOptIn;
|
||||
},
|
||||
updateIsOemGlassSelected(state, isOemGlassSelected) {
|
||||
state.order.damage.installOemGlass = isOemGlassSelected;
|
||||
},
|
||||
updateIsMSRFeeApplicable(state, isMSRFeeApplicable) {
|
||||
state.order.isMSRFeeApplicable = isMSRFeeApplicable;
|
||||
},
|
||||
|
|
@ -712,6 +716,8 @@ export const mutations = {
|
|||
state.order.damage.capabilityQuestionAnswers =
|
||||
sessionInformation.order.damage.capabilityQuestionAnswers;
|
||||
|
||||
state.order.damage.installOemGlass = sessionInformation.order.damage.installOemGlass;
|
||||
|
||||
state.order.lineItems.glassParts = sessionInformation.order.lineItems.glassParts;
|
||||
state.order.lineItems.supportingItems = sessionInformation.order.lineItems.supportingItems;
|
||||
state.order.lineItems.vaps = sessionInformation.order.lineItems.vaps ?? [];
|
||||
|
|
@ -1853,8 +1859,7 @@ export const actions = {
|
|||
const serviceType = order.serviceLocation?.appointmentType;
|
||||
const referralSeqNumber = order.referralSequenceNumber;
|
||||
const parentAccountNumber = applicationConfig.CASH_PARENT_ACCOUNT_NUMBER;
|
||||
const oemEndorsementFlag = false; // Need to update from the store when available
|
||||
|
||||
const oemEndorsementFlag = order.damage.installOemGlass || false;
|
||||
// create a new array to avoid mutating state
|
||||
const glassArrayForPayload = convertGlassPieceNamingForApi(glassArray);
|
||||
const resultsArrayForPayload = convertResultsForApi(resultsArray);
|
||||
|
|
@ -2453,6 +2458,7 @@ export const actions = {
|
|||
partQuestionAnswers: order.damage.partQuestionAnswers,
|
||||
moldingQuestionAnswers: order.damage.moldingQuestionAnswers,
|
||||
capabilityQuestionAnswers: order.damage.capabilityQuestionAnswers,
|
||||
installOemGlass: order.damage.installOemGlass,
|
||||
},
|
||||
lineItems: {
|
||||
glassParts: lineItems.glassParts,
|
||||
|
|
@ -2959,6 +2965,10 @@ export const actions = {
|
|||
context.commit(storeMutations.UPDATE_IS_RECAL_ACK_OPT_IN, isRecalAckOptIn);
|
||||
},
|
||||
|
||||
saveIsOemGlassSelected(context, isOemGlassSelected) {
|
||||
context.commit(storeMutations.UPDATE_IS_OEM_GLASS_SELECTED, isOemGlassSelected);
|
||||
},
|
||||
|
||||
saveIsMSRFeeApplicable(context, isMSRFeeApplicable) {
|
||||
context.commit(storeMutations.UPDATE_IS_MSR_FEE_APPLICABLE, isMSRFeeApplicable);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -123,8 +123,8 @@ $body-color: $gray-600;
|
|||
|
||||
//Fonts
|
||||
$font-family-sans-serif: UrbanistRegular, Arial, Helvetica, sans-serif;
|
||||
$font-family-monospace: UrbanistRegular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New",
|
||||
monospace;
|
||||
$font-family-monospace:
|
||||
UrbanistRegular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
// stylelint-enable value-keyword-case
|
||||
$font-family-base: $font-family-sans-serif;
|
||||
$font-family-code: $font-family-monospace;
|
||||
|
|
|
|||
Loading…
Reference in a new issue