Merge remote-tracking branch 'origin/develop' into feature/skiener/CSR-1520
This commit is contained in:
commit
bc40d03925
50 changed files with 2462 additions and 225 deletions
14
src/assets/img/icons/Google.svg
Normal file
14
src/assets/img/icons/Google.svg
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
|
||||||
|
<g fill="none" fill-rule="evenodd">
|
||||||
|
<g fill-rule="nonzero">
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path fill="#FBBB00" d="M4.432 12.086l-.696 2.6-2.544.053C.432 13.329 0 11.715 0 10c0-1.658.403-3.222 1.118-4.599l2.266.415.992 2.252c-.207.606-.32 1.256-.32 1.932 0 .734.132 1.437.376 2.086z" transform="translate(-119 -800) translate(104 753) translate(15 47)"/>
|
||||||
|
<path fill="#518EF8" d="M19.825 8.132C19.94 8.737 20 9.362 20 10c0 .716-.075 1.414-.219 2.088-.486 2.292-1.758 4.294-3.52 5.71h-.001l-2.853-.146-.404-2.52c1.169-.686 2.083-1.76 2.564-3.044H10.22V8.132h9.605z" transform="translate(-119 -800) translate(104 753) translate(15 47)"/>
|
||||||
|
<path fill="#28B446" d="M16.26 17.798C14.547 19.176 12.37 20 10 20c-3.808 0-7.12-2.129-8.808-5.261l3.24-2.653c.845 2.254 3.02 3.859 5.568 3.859 1.096 0 2.122-.297 3.003-.814l3.257 2.667z" transform="translate(-119 -800) translate(104 753) translate(15 47)"/>
|
||||||
|
<path fill="#F14336" d="M16.383 2.302l-3.24 2.652c-.911-.57-1.989-.899-3.143-.899-2.607 0-4.821 1.678-5.624 4.013L1.12 5.401C2.781 2.192 6.134 0 10 0c2.426 0 4.651.864 6.383 2.302z" transform="translate(-119 -800) translate(104 753) translate(15 47)"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.4 KiB |
16
src/assets/img/icons/Outlook.com.svg
Normal file
16
src/assets/img/icons/Outlook.com.svg
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="20" viewBox="0 0 22 20">
|
||||||
|
<g fill="none" fill-rule="evenodd">
|
||||||
|
<g fill="#0072C6" fill-rule="nonzero">
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path d="M12.794 4.525v3.677l1.37.809c.036.01.114.01.15 0l5.899-3.728c0-.441-.44-.758-.687-.758h-6.732z" transform="translate(-119 -870) translate(104 753) translate(15 117)"/>
|
||||||
|
<path d="M12.794 9.574l1.25.805c.176.121.389 0 .389 0-.212.121 5.78-3.61 5.78-3.61v6.757c0 .736-.502 1.044-1.067 1.044h-6.353V9.574z" transform="translate(-119 -870) translate(104 753) translate(15 117)"/>
|
||||||
|
<g>
|
||||||
|
<path d="M6.266 7.736c-.427 0-.766.187-1.017.562-.25.375-.376.871-.376 1.489 0 .627.125 1.122.376 1.487.25.365.58.546.986.546.42 0 .753-.177.998-.532.246-.354.37-.846.37-1.475 0-.656-.12-1.166-.359-1.531-.238-.364-.564-.546-.978-.546z" transform="translate(-119 -870) translate(104 753) translate(15 117) translate(.052 .143)"/>
|
||||||
|
<path d="M.332 2.515v14.383l11.671 2.295V.397L.333 2.515zm7.81 9.623c-.493.609-1.136.914-1.93.914-.773 0-1.403-.295-1.89-.885-.486-.59-.73-1.358-.73-2.305 0-1 .247-1.81.741-2.427.495-.618 1.15-.927 1.966-.927.77 0 1.393.295 1.869.887.476.591.713 1.37.713 2.338.001.995-.246 1.797-.739 2.405z" transform="translate(-119 -870) translate(104 753) translate(15 117) translate(.052 .143)"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.5 KiB |
16
src/assets/img/icons/Outlook.svg
Normal file
16
src/assets/img/icons/Outlook.svg
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="21" height="20" viewBox="0 0 21 20">
|
||||||
|
<g fill="none" fill-rule="evenodd">
|
||||||
|
<g fill="#DBA702" fill-rule="nonzero">
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path d="M12.394 4.525v3.677l1.327.809c.035.01.111.01.146 0l5.714-3.728c0-.441-.425-.758-.665-.758h-6.522z" transform="translate(-119 -835) translate(104 753) translate(15 82)"/>
|
||||||
|
<path d="M12.394 9.574l1.211.805c.171.121.377 0 .377 0-.205.121 5.6-3.61 5.6-3.61v6.757c0 .736-.487 1.044-1.034 1.044h-6.155V9.574z" transform="translate(-119 -835) translate(104 753) translate(15 82)"/>
|
||||||
|
<g>
|
||||||
|
<path d="M6.07 7.736c-.413 0-.742.187-.985.562-.243.375-.365.871-.365 1.489 0 .627.122 1.122.365 1.487s.562.546.956.546c.406 0 .728-.177.966-.532.238-.354.358-.846.358-1.475 0-.656-.116-1.166-.347-1.531-.231-.364-.547-.546-.948-.546z" transform="translate(-119 -835) translate(104 753) translate(15 82) translate(.05 .143)"/>
|
||||||
|
<path d="M.322 2.515v14.383l11.306 2.295V.397L.322 2.515zm7.566 9.623c-.478.609-1.101.914-1.87.914-.75 0-1.359-.295-1.83-.885-.472-.59-.709-1.358-.709-2.305 0-1 .24-1.81.719-2.427.48-.618 1.114-.927 1.904-.927.746 0 1.35.295 1.81.887.461.591.692 1.37.692 2.338 0 .995-.239 1.797-.716 2.405z" transform="translate(-119 -835) translate(104 753) translate(15 82) translate(.05 .143)"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.5 KiB |
13
src/assets/img/icons/Yahoo.svg
Normal file
13
src/assets/img/icons/Yahoo.svg
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="20" viewBox="0 0 22 20">
|
||||||
|
<g fill="none" fill-rule="evenodd">
|
||||||
|
<g fill="#5F01D1" fill-rule="nonzero">
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path d="M0 4.868h4.29l2.498 6.39 2.53-6.39h4.177l-6.29 15.129H3.003l1.722-4.01L0 4.869zm18.358 5.106H13.68L17.832 0h4.661l-4.135 9.974zm-3.452.955c1.435 0 2.599 1.163 2.599 2.599 0 1.435-1.164 2.598-2.6 2.598-1.434 0-2.598-1.163-2.598-2.598 0-1.436 1.164-2.6 2.599-2.6z" transform="translate(-119 -905) translate(104 753) translate(15 152) translate(0 .11)"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 708 B |
11
src/assets/img/icons/iCal.svg
Normal file
11
src/assets/img/icons/iCal.svg
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="17" height="20" viewBox="0 0 17 20">
|
||||||
|
<g fill="none" fill-rule="evenodd">
|
||||||
|
<g fill="#606060" fill-rule="nonzero">
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path d="M5.44 20C2.463 19.983 0 13.907 0 10.812c0-5.055 3.793-6.161 5.254-6.161.659 0 1.362.258 1.982.487.434.16.883.325 1.132.325.15 0 .501-.14.812-.264.663-.263 1.487-.591 2.447-.591h.006c.717 0 2.89.157 4.198 2.12l.306.46-.44.332c-.63.475-1.778 1.341-1.778 3.058 0 2.032 1.3 2.814 1.925 3.19.276.166.561.337.561.71 0 .245-1.95 5.492-4.782 5.492-.692 0-1.182-.209-1.614-.392-.437-.186-.815-.346-1.438-.346-.316 0-.715.149-1.138.307-.578.216-1.232.461-1.974.461h-.02zM11.927 0C12.001 2.66 10.1 4.504 8.2 4.388 7.887 2.266 10.1 0 11.927 0z" transform="translate(-119 -763) translate(104 753) translate(15 10)"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 907 B |
8
src/constants/calendar-options.js
Normal file
8
src/constants/calendar-options.js
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
const calendarOptions = {
|
||||||
|
ICAL: "iCal",
|
||||||
|
GOOGLE: "Google",
|
||||||
|
OUTLOOK: "Outlook",
|
||||||
|
OUTLOOKCOM: "Outlook.com",
|
||||||
|
YAHOO: "Yahoo",
|
||||||
|
};
|
||||||
|
export { calendarOptions };
|
||||||
7
src/constants/calendar-status.js
Normal file
7
src/constants/calendar-status.js
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
const calendarStatus = {
|
||||||
|
FREE: "Free",
|
||||||
|
BUSY: "Busy",
|
||||||
|
TENTATIVE: "Tentative",
|
||||||
|
OUT_OF_THE_OFFICE: "OutOfTheOffice",
|
||||||
|
};
|
||||||
|
export { calendarStatus };
|
||||||
|
|
@ -6,6 +6,8 @@ const experimentSettings = {
|
||||||
GOOGLE_CUSTOM_DIMENSION_INDEX: "Google Custom Dimension Index",
|
GOOGLE_CUSTOM_DIMENSION_INDEX: "Google Custom Dimension Index",
|
||||||
SUPPRESS_VIN_CAPTURE: "SuppressVinCapture",
|
SUPPRESS_VIN_CAPTURE: "SuppressVinCapture",
|
||||||
DISPLAY_AVAILABILITY_INDICATORS: "DisplayAvailabilityIndicators",
|
DISPLAY_AVAILABILITY_INDICATORS: "DisplayAvailabilityIndicators",
|
||||||
|
PIA_EXPERIENCE: "PIA Experience",
|
||||||
|
SUBMIT_ORDER_ENABLE_PIA: "SubmitOrder_Enable_PIA",
|
||||||
};
|
};
|
||||||
|
|
||||||
const experimentTriggers = {
|
const experimentTriggers = {
|
||||||
|
|
|
||||||
13
src/constants/payment-method-constants.js
Normal file
13
src/constants/payment-method-constants.js
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
export const paymentMethods = {
|
||||||
|
NONE: null,
|
||||||
|
LATER: "Later",
|
||||||
|
CREDIT_CARD: "CreditCard",
|
||||||
|
PAYPAL: "Paypal",
|
||||||
|
AFTERPAY: "Afterpay",
|
||||||
|
};
|
||||||
|
|
||||||
|
export const paymentTimes = {
|
||||||
|
NONE: null,
|
||||||
|
ADVANCE: "Advance",
|
||||||
|
LATER: "Later",
|
||||||
|
};
|
||||||
|
|
@ -90,6 +90,7 @@ const storeActions = {
|
||||||
SAVE_SERVICE_LOCATION_TECH_NOTES: "saveServiceLocationTechNotes",
|
SAVE_SERVICE_LOCATION_TECH_NOTES: "saveServiceLocationTechNotes",
|
||||||
SAVE_WORK_ORDER_FLAG: "saveWorkOrderFlag",
|
SAVE_WORK_ORDER_FLAG: "saveWorkOrderFlag",
|
||||||
SAVE_PIA_ERROR_CODE: "savePiaErrorCode",
|
SAVE_PIA_ERROR_CODE: "savePiaErrorCode",
|
||||||
|
SAVE_PIA_WORK_ORDER: "savePiaWorkOrder",
|
||||||
};
|
};
|
||||||
|
|
||||||
export { storeActions };
|
export { storeActions };
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
const storeMutations = {
|
const storeMutations = {
|
||||||
UPDATE_WORK_ORDER_FLAG: "updateWorkOrderFlag",
|
UPDATE_WORK_ORDER_FLAG: "updateWorkOrderFlag",
|
||||||
UPDATE_PIA_ERROR_CODE: "updatePiaErrorCode",
|
UPDATE_PIA_ERROR_CODE: "updatePiaErrorCode",
|
||||||
|
UPDATE_PIA_WORK_ORDER: "updatePiaWorkOrder",
|
||||||
|
|
||||||
// VEHICLE MUTATIONS
|
// VEHICLE MUTATIONS
|
||||||
UPDATE_YEAR: "updateYear",
|
UPDATE_YEAR: "updateYear",
|
||||||
|
|
|
||||||
|
|
@ -223,6 +223,10 @@ export default {
|
||||||
classes = "package-wrapper";
|
classes = "package-wrapper";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.buttonTypeString == "listCard" && this.buttonsInfo.length > 2) {
|
||||||
|
classes += " two-list-card-width";
|
||||||
|
}
|
||||||
|
|
||||||
return classes;
|
return classes;
|
||||||
},
|
},
|
||||||
buttonsInfo() {
|
buttonsInfo() {
|
||||||
|
|
@ -304,18 +308,9 @@ export default {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.vehicle-damage {
|
.two-list-card-width {
|
||||||
.button-question {
|
width: 33.33333333%;
|
||||||
@include media-breakpoint-up(md) {
|
flex: 0 0 auto;
|
||||||
.col-12 {
|
|
||||||
width: 66.66666667%;
|
|
||||||
}
|
|
||||||
.col {
|
|
||||||
width: 33.3333333%;
|
|
||||||
flex: 0 0 auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.button-question-overflow {
|
.button-question-overflow {
|
||||||
height: calc(100vh - 274px);
|
height: calc(100vh - 274px);
|
||||||
|
|
|
||||||
|
|
@ -21,9 +21,8 @@ export default {
|
||||||
name: "add-vaps-modal-buttons",
|
name: "add-vaps-modal-buttons",
|
||||||
props: {
|
props: {
|
||||||
cartItems: Object,
|
cartItems: Object,
|
||||||
rainDefenseItem: Object,
|
|
||||||
wiperItems: Object,
|
|
||||||
vapsTilesCmsName: String,
|
vapsTilesCmsName: String,
|
||||||
|
availableLineItems: Object,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|
@ -55,13 +54,19 @@ export default {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
rainDefensePrice() {
|
rainDefensePrice() {
|
||||||
const lineItem = this.rainDefenseItem;
|
// const lineItem = this.rainDefenseItem;
|
||||||
|
const lineItem = this.availableLineItems.find((item) => {
|
||||||
|
return item.partType === "RAIN DEFENSE";
|
||||||
|
});
|
||||||
|
|
||||||
const price = lineItem.laborAmount + lineItem.sellingPrice + lineItem.kitPrice;
|
const price = lineItem.laborAmount + lineItem.sellingPrice + lineItem.kitPrice;
|
||||||
return parseFloat(price).toFixed(2);
|
return parseFloat(price).toFixed(2);
|
||||||
},
|
},
|
||||||
wipersPrice() {
|
wipersPrice() {
|
||||||
let price = 0;
|
let price = 0;
|
||||||
const lineItems = this.wiperItems;
|
const lineItems = this.availableLineItems.filter((item) => {
|
||||||
|
return item.partType.includes(" WIPER");
|
||||||
|
});
|
||||||
lineItems?.forEach((item) => {
|
lineItems?.forEach((item) => {
|
||||||
price += item.laborAmount + item.sellingPrice + item.kitPrice;
|
price += item.laborAmount + item.sellingPrice + item.kitPrice;
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
@click="toggleClass()">
|
@click="toggleClass()">
|
||||||
<div class="col d-flex justify-content-between">
|
<div class="col d-flex justify-content-between">
|
||||||
<span class="label">Amount Due</span
|
<span class="label">Amount Due</span
|
||||||
><span class="label amount-due">$321.00</span>
|
><span class="label amount-due">{{ amountDue }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="price-table">
|
<div class="price-table">
|
||||||
|
|
@ -54,47 +54,35 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import cart from "@/fmg-components/cart/cart";
|
|
||||||
import baseMixin from "@/mixins/base-mixin.js";
|
import baseMixin from "@/mixins/base-mixin.js";
|
||||||
import funnelSubHeader from "@/fmg-components/funnel-sub-header/funnel-sub-header";
|
|
||||||
import { packageNames } from "@/constants/package-names";
|
|
||||||
import {
|
import {
|
||||||
getHighestFullySatisfiedTier,
|
getHighestFullySatisfiedTier,
|
||||||
containsLineItemWithPartType,
|
containsLineItemWithPartType,
|
||||||
getPackageContents,
|
getPackageContents,
|
||||||
findLineItemsWithPartType,
|
findLineItemsWithPartType,
|
||||||
} from "@/helpers/service-package-helper";
|
} from "@/helpers/service-package-helper";
|
||||||
import reviewBlock from "@/layouts/review/review-block/review-block";
|
|
||||||
import textLink from "@/ux-components/text-link/text-link";
|
import textLink from "@/ux-components/text-link/text-link";
|
||||||
import textBlock from "@/digital-components/text-block/text-block";
|
import textBlock from "@/digital-components/text-block/text-block";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "modal",
|
name: "modal",
|
||||||
props: {
|
props: {
|
||||||
headerCmsWidgetName: String,
|
|
||||||
servicePackageOptionsCmsName: String,
|
|
||||||
damage: Object,
|
damage: Object,
|
||||||
storeLineItems: Object,
|
servicePackageOptionsCmsName: String,
|
||||||
availableLineItems: Object,
|
availableLineItems: Object,
|
||||||
allowableCartItems: Object,
|
lineItems: Object,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
isActive: false,
|
isActive: false,
|
||||||
//availableVaps: [], TO DO Do we need this?
|
|
||||||
packageWithVAPS: false,
|
packageWithVAPS: false,
|
||||||
// packageLevel: "",
|
amountDue: this.getAmountDue(),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
toggleClass: function (event) {
|
toggleClass: function (event) {
|
||||||
this.isActive = !this.isActive;
|
this.isActive = !this.isActive;
|
||||||
},
|
},
|
||||||
initializeComponent(apiResponses) {
|
|
||||||
//TO DO Do we need this?
|
|
||||||
// const vapsFromApi = [...apiResponses.wipers, apiResponses.rainDefense];
|
|
||||||
// this.availableVaps = vapsFromApi;
|
|
||||||
},
|
|
||||||
getPackagePriceString(packageName) {
|
getPackagePriceString(packageName) {
|
||||||
const formattedPriceFloat = parseFloat(this.getPackagePrice(packageName)).toFixed(2);
|
const formattedPriceFloat = parseFloat(this.getPackagePrice(packageName)).toFixed(2);
|
||||||
return (this.isInsuranceSelected ? "As little as $" : "$") + formattedPriceFloat;
|
return (this.isInsuranceSelected ? "As little as $" : "$") + formattedPriceFloat;
|
||||||
|
|
@ -103,7 +91,7 @@ export default {
|
||||||
let priceFloat = this.isInsuranceSelected
|
let priceFloat = this.isInsuranceSelected
|
||||||
? 0
|
? 0
|
||||||
: baseMixin.methods.getTierOnePackagePrice(
|
: baseMixin.methods.getTierOnePackagePrice(
|
||||||
baseMixin.methods.filterOutFees(this.nullSafeAvailableLineItems)
|
baseMixin.methods.filterOutFees(this.availableLineItems)
|
||||||
);
|
);
|
||||||
|
|
||||||
priceFloat += this.getVapsPrice(packageName);
|
priceFloat += this.getVapsPrice(packageName);
|
||||||
|
|
@ -121,13 +109,16 @@ export default {
|
||||||
|
|
||||||
return price;
|
return price;
|
||||||
},
|
},
|
||||||
|
getAmountDue() {
|
||||||
|
return baseMixin.methods.getDisplayAmountDue(this.storeLineItems);
|
||||||
|
},
|
||||||
removeItem(item) {
|
removeItem(item) {
|
||||||
this.$emit("cart-remove", item);
|
this.$emit("cart-remove", item);
|
||||||
},
|
},
|
||||||
getVapsLineItemsForSelectedPackage(packageName) {
|
getVapsLineItemsForSelectedPackage(packageName) {
|
||||||
const packageContentTypes = getPackageContents(
|
const packageContentTypes = getPackageContents(
|
||||||
this.glassToReplace,
|
this.glassToReplace,
|
||||||
this.nullSafeAvailableLineItems,
|
this.availableLineItems,
|
||||||
this.isRepair,
|
this.isRepair,
|
||||||
packageName
|
packageName
|
||||||
);
|
);
|
||||||
|
|
@ -136,10 +127,9 @@ export default {
|
||||||
|
|
||||||
packageContentTypes.forEach((vapType) => {
|
packageContentTypes.forEach((vapType) => {
|
||||||
vapsLineItemsForSelectedPackage.push(
|
vapsLineItemsForSelectedPackage.push(
|
||||||
...findLineItemsWithPartType(vapType, this.nullSafeAvailableLineItems)
|
...findLineItemsWithPartType(vapType, this.availableLineItems)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
// console.log("VapsLineItems:", vapsLineItemsForSelectedPackage);
|
|
||||||
return vapsLineItemsForSelectedPackage;
|
return vapsLineItemsForSelectedPackage;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -180,7 +170,7 @@ export default {
|
||||||
displayedPackageLineItems() {
|
displayedPackageLineItems() {
|
||||||
const displayedPackageLineItems = [];
|
const displayedPackageLineItems = [];
|
||||||
const packageLineItems = this.getVapsLineItemsForSelectedPackage(this.packageLevel);
|
const packageLineItems = this.getVapsLineItemsForSelectedPackage(this.packageLevel);
|
||||||
//TO DO Could we get the same list from this.storeLineItems?
|
// TODO Determine if we could get the same list from this.lineItems?
|
||||||
packageLineItems?.forEach((item) => {
|
packageLineItems?.forEach((item) => {
|
||||||
let price = item.sellingPrice + item.kitPrice + item.laborAmount;
|
let price = item.sellingPrice + item.kitPrice + item.laborAmount;
|
||||||
const packageLineItem = {
|
const packageLineItem = {
|
||||||
|
|
@ -191,7 +181,6 @@ export default {
|
||||||
};
|
};
|
||||||
displayedPackageLineItems.push(packageLineItem);
|
displayedPackageLineItems.push(packageLineItem);
|
||||||
});
|
});
|
||||||
// console.log("What displayedPackageLineItems is", packageLineItems);
|
|
||||||
return displayedPackageLineItems;
|
return displayedPackageLineItems;
|
||||||
},
|
},
|
||||||
displayedNonPackageLineItems() {
|
displayedNonPackageLineItems() {
|
||||||
|
|
@ -233,11 +222,11 @@ export default {
|
||||||
return this.damage.isRepair;
|
return this.damage.isRepair;
|
||||||
},
|
},
|
||||||
vaps() {
|
vaps() {
|
||||||
const vaps = this.storeLineItems?.vaps;
|
const vaps = this.lineItems?.vaps;
|
||||||
return vaps?.length > 0 ? vaps : [];
|
return vaps?.length > 0 ? vaps : [];
|
||||||
},
|
},
|
||||||
supportingItems() {
|
supportingItems() {
|
||||||
const supportingItems = this.storeLineItems?.supportingItems;
|
const supportingItems = this.lineItems?.supportingItems;
|
||||||
return supportingItems?.length > 0 ? supportingItems : [];
|
return supportingItems?.length > 0 ? supportingItems : [];
|
||||||
},
|
},
|
||||||
recycleLabel() {
|
recycleLabel() {
|
||||||
|
|
@ -255,9 +244,6 @@ export default {
|
||||||
// }
|
// }
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
nullSafeAvailableLineItems() {
|
|
||||||
return this.availableLineItems ?? [];
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
textBlock,
|
textBlock,
|
||||||
|
|
|
||||||
|
|
@ -159,8 +159,8 @@ export default {
|
||||||
.modal {
|
.modal {
|
||||||
&.menu-modal {
|
&.menu-modal {
|
||||||
left: auto;
|
left: auto;
|
||||||
height: calc(100% - 72px);
|
height: calc(100% - 56px);
|
||||||
top: 72px;
|
top: 56px;
|
||||||
border-top: 1px solid $gray-300;
|
border-top: 1px solid $gray-300;
|
||||||
overflow-x: visible;
|
overflow-x: visible;
|
||||||
overflow-y: visible;
|
overflow-y: visible;
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row justify-content-md-center">
|
<div class="row justify-content-md-center">
|
||||||
<div class="col-md-6 col-xl-4 col-xxl-3">
|
<div class="col-md-6 col-xl-4">
|
||||||
<div v-for="(questionsDatum, i) in questionsData" :key="questionsDatum.key">
|
<div v-for="(questionsDatum, i) in questionsData" :key="questionsDatum.key">
|
||||||
<questionChain
|
<questionChain
|
||||||
ref="questionChain"
|
ref="questionChain"
|
||||||
|
|
|
||||||
76
src/helpers/add-to-calendar-helper.js
Normal file
76
src/helpers/add-to-calendar-helper.js
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
import { calendarStatus } from "@/constants/calendar-status";
|
||||||
|
import { getDateFormat } from "@/helpers/date-helper";
|
||||||
|
export function getCalendarFile(calFile) {
|
||||||
|
const dateFormat = "yyyyMMddTHHmmss";
|
||||||
|
var calEvent = [];
|
||||||
|
calEvent.push("BEGIN:VCALENDAR");
|
||||||
|
calEvent.push("VERSION:2.0");
|
||||||
|
calEvent.push("BEGIN:VEVENT");
|
||||||
|
calEvent.push("DTSTAMP:" + calFile.TimeStamp);
|
||||||
|
calEvent.push("UID:" + calFile.UniqueId + "@safelite.com");
|
||||||
|
calEvent.push("PRODID:noreply@safelite.com");
|
||||||
|
switch (calFile.Status) {
|
||||||
|
case calendarStatus.BUSY:
|
||||||
|
calEvent.push("X-MICROSOFT-CDO-BUSYSTATUS:BUSY");
|
||||||
|
break;
|
||||||
|
case calendarStatus.FREE:
|
||||||
|
calEvent.push("TRANSP:TRANSPARENT");
|
||||||
|
break;
|
||||||
|
case calendarStatus.TENTATIVE:
|
||||||
|
calEvent.push("STATUS:TENTATIVE");
|
||||||
|
break;
|
||||||
|
case calendarStatus.OUT_OF_THE_OFFICE:
|
||||||
|
calEvent.push("X-MICROSOFT-CDO-BUSYSTATUS:OOF");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
//throw new Exception("Invalid CalendarStatus");
|
||||||
|
}
|
||||||
|
if (calFile.AllDayEvent) {
|
||||||
|
calEvent.push("DTSTART;VALUE=DATE:" + getDateFormat(calFile.StartDate, dateFormat));
|
||||||
|
calEvent.push("DTEND;;VALUE=DATE:" + getDateFormat(calFile.EndDate, dateFormat));
|
||||||
|
} else {
|
||||||
|
calEvent.push("DTSTART:" + getDateFormat(calFile.StartDate, dateFormat));
|
||||||
|
calEvent.push("DTEND:" + getDateFormat(calFile.EndDate, dateFormat));
|
||||||
|
}
|
||||||
|
|
||||||
|
calEvent.push("SUMMARY:" + ToCalendarFileString(calFile.Subject, " "));
|
||||||
|
if (calFile.Location != null && calFile.Location != "") {
|
||||||
|
calEvent.push("LOCATION:" + ToCalendarFileString(calFile.Location, " "));
|
||||||
|
}
|
||||||
|
if (calFile.Body != null && calFile.Body != "") {
|
||||||
|
if (calFile.IsHTML) {
|
||||||
|
calEvent.push("DESCRIPTION:" + ToCalendarFileString(calFile.Body));
|
||||||
|
calEvent.push(
|
||||||
|
"X-ALT-DESC;FMTTYPE=text/html:" +
|
||||||
|
'<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">' +
|
||||||
|
"<HTML><HEAD><TITLE></TITLE></HEAD><BODY>" +
|
||||||
|
'<P DIR=LTR><SPAN LANG="en-us">' +
|
||||||
|
"</SPAN>" +
|
||||||
|
ToCalendarFileString(calFile.Body) +
|
||||||
|
"</BODY></HTML>"
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
calEvent.push("DESCRIPTION:" + ToCalendarFileString(calFile.Body));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
calEvent.push("END:VEVENT");
|
||||||
|
calEvent.push("END:VCALENDAR");
|
||||||
|
return calEvent.join("\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
function ToCalendarFileString(str, replacementArg = "<p>") {
|
||||||
|
return str.replace("\r\n", replacementArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function download(filename, fileBody) {
|
||||||
|
var element = document.createElement("a");
|
||||||
|
element.setAttribute("href", "data:text/plain;charset=utf-8," + encodeURIComponent(fileBody));
|
||||||
|
element.setAttribute("download", filename);
|
||||||
|
|
||||||
|
element.style.display = "none";
|
||||||
|
document.body.appendChild(element);
|
||||||
|
|
||||||
|
element.click();
|
||||||
|
|
||||||
|
document.body.removeChild(element);
|
||||||
|
}
|
||||||
79
src/helpers/add-to-calendar-helper.spec.js
Normal file
79
src/helpers/add-to-calendar-helper.spec.js
Normal file
|
|
@ -0,0 +1,79 @@
|
||||||
|
import { download, getCalendarFile } from "./add-to-calendar-helper";
|
||||||
|
|
||||||
|
// Create a mock function
|
||||||
|
const mockDownload = jest.fn((filename, fileBody) => {
|
||||||
|
// Simulate creating an anchor element
|
||||||
|
const mockElement = {
|
||||||
|
setAttribute: jest.fn(),
|
||||||
|
style: { display: "none" },
|
||||||
|
click: jest.fn(),
|
||||||
|
};
|
||||||
|
// Mock the document.createElement method
|
||||||
|
document.createElement = jest.fn(() => mockElement);
|
||||||
|
// Mock the document.body.appendChild method
|
||||||
|
document.body.appendChild = jest.fn();
|
||||||
|
// Mock the document.body.removeChild method
|
||||||
|
document.body.removeChild = jest.fn();
|
||||||
|
|
||||||
|
// Call the original function with the mock element
|
||||||
|
download(filename, fileBody);
|
||||||
|
|
||||||
|
// Return the mock element for further assertions
|
||||||
|
return mockElement;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Use the mock function in a test
|
||||||
|
test("download function", () => {
|
||||||
|
// Call the mock function with some arguments
|
||||||
|
const mockElement = mockDownload("test.txt", "Hello world");
|
||||||
|
|
||||||
|
// Make assertions about the mock function and the mock element
|
||||||
|
expect(mockDownload).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockDownload).toHaveBeenCalledWith("test.txt", "Hello world");
|
||||||
|
expect(mockElement.setAttribute).toHaveBeenCalledTimes(2);
|
||||||
|
expect(mockElement.setAttribute).toHaveBeenCalledWith(
|
||||||
|
"href",
|
||||||
|
"data:text/plain;charset=utf-8," + encodeURIComponent("Hello world")
|
||||||
|
);
|
||||||
|
expect(mockElement.setAttribute).toHaveBeenCalledWith("download", "test.txt");
|
||||||
|
expect(mockElement.style.display).toBe("none");
|
||||||
|
expect(mockElement.click).toHaveBeenCalledTimes(1);
|
||||||
|
expect(document.body.appendChild).toHaveBeenCalledTimes(1);
|
||||||
|
expect(document.body.appendChild).toHaveBeenCalledWith(mockElement);
|
||||||
|
expect(document.body.removeChild).toHaveBeenCalledTimes(1);
|
||||||
|
expect(document.body.removeChild).toHaveBeenCalledWith(mockElement);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Use the mock function in a test
|
||||||
|
test("getCalendarFile function", () => {
|
||||||
|
// Create a mock calFile object
|
||||||
|
const mockCalFile = {
|
||||||
|
UniqueId: "123456",
|
||||||
|
Status: "Busy",
|
||||||
|
StartDate: new Date(2023, 9, 11, 12, 0, 0),
|
||||||
|
EndDate: new Date(2023, 9, 11, 13, 0, 0),
|
||||||
|
Subject: "Meeting with John",
|
||||||
|
Location: "Conference Room",
|
||||||
|
Body: "Discuss the project progress",
|
||||||
|
TimeStamp: "20231011T113118",
|
||||||
|
};
|
||||||
|
|
||||||
|
// Call the mock function with the mock calFile object
|
||||||
|
const mockCalendarFile = getCalendarFile(mockCalFile);
|
||||||
|
|
||||||
|
// Make assertions about the mock function and the mock calendar file
|
||||||
|
expect(mockCalendarFile).toContain("BEGIN:VCALENDAR");
|
||||||
|
expect(mockCalendarFile).toContain("VERSION:2.0");
|
||||||
|
expect(mockCalendarFile).toContain("BEGIN:VEVENT");
|
||||||
|
expect(mockCalendarFile).toContain("DTSTAMP:20231011T113118");
|
||||||
|
expect(mockCalendarFile).toContain("UID:123456@safelite.com");
|
||||||
|
expect(mockCalendarFile).toContain("PRODID:noreply@safelite.com");
|
||||||
|
expect(mockCalendarFile).toContain("DTSTART:20231011T120000");
|
||||||
|
expect(mockCalendarFile).toContain("DTEND:20231011T130000");
|
||||||
|
expect(mockCalendarFile).toContain("X-MICROSOFT-CDO-BUSYSTATUS:BUSY");
|
||||||
|
expect(mockCalendarFile).toContain("SUMMARY:Meeting with John");
|
||||||
|
expect(mockCalendarFile).toContain("LOCATION:Conference Room");
|
||||||
|
expect(mockCalendarFile).toContain("DESCRIPTION:Discuss the project progress");
|
||||||
|
expect(mockCalendarFile).toContain("END:VEVENT");
|
||||||
|
expect(mockCalendarFile).toContain("END:VCALENDAR");
|
||||||
|
});
|
||||||
|
|
@ -1,36 +1,27 @@
|
||||||
export function getDateDifferenceInDays(startDate, endDate) {
|
export function getDateDifferenceInDays(startDate, endDate) {
|
||||||
|
// Create date objects from the input strings
|
||||||
var date1 = new Date(endDate);
|
var date1 = new Date(endDate);
|
||||||
date1.setHours(0, 0, 0, 0);
|
|
||||||
var date2 = new Date(startDate);
|
var date2 = new Date(startDate);
|
||||||
date2.setHours(0, 0, 0, 0);
|
// Use the built-in method to get the difference in milliseconds
|
||||||
// To calculate the time difference of two dates
|
var difference = date1 - date2;
|
||||||
var Difference_In_Time = date1.getTime() - date2.getTime();
|
// Convert milliseconds to days and return the result
|
||||||
// To calculate the no. of days between two dates
|
return difference / (1000 * 3600 * 24);
|
||||||
return Difference_In_Time / (1000 * 3600 * 24);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getFullDayName(date) {
|
export function getFullDayName(date) {
|
||||||
if (date instanceof Date !== true) return;
|
// Use a ternary operator to check if the input is a valid date object
|
||||||
const days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
|
return date instanceof Date
|
||||||
return days[date.getDay()];
|
? // Use the built-in method toLocaleDateString() to get the full day name in the current locale
|
||||||
|
date.toLocaleDateString(undefined, { weekday: "long" })
|
||||||
|
: // Return undefined if the input is not a valid date object
|
||||||
|
undefined;
|
||||||
}
|
}
|
||||||
export function getFullMonthName(date) {
|
export function getFullMonthName(date) {
|
||||||
if (date instanceof Date !== true) return;
|
// Use a ternary operator to check if the input is a valid date object
|
||||||
const monthNames = [
|
return date instanceof Date
|
||||||
"January",
|
? // Use the built-in method toLocaleDateString() to get the full month name in the current locale
|
||||||
"February",
|
date.toLocaleDateString(undefined, { month: "long" })
|
||||||
"March",
|
: // Return undefined if the input is not a valid date object
|
||||||
"April",
|
undefined;
|
||||||
"May",
|
|
||||||
"June",
|
|
||||||
"July",
|
|
||||||
"August",
|
|
||||||
"September",
|
|
||||||
"October",
|
|
||||||
"November",
|
|
||||||
"December",
|
|
||||||
];
|
|
||||||
return monthNames[date.getMonth()];
|
|
||||||
}
|
}
|
||||||
export function get12HourTimeFormat(time) {
|
export function get12HourTimeFormat(time) {
|
||||||
// Check correct time format and split into components
|
// Check correct time format and split into components
|
||||||
|
|
@ -62,3 +53,66 @@ export function get12HourTimeMobileFormat(time) {
|
||||||
}
|
}
|
||||||
return time.join(""); // return adjusted time or original string
|
return time.join(""); // return adjusted time or original string
|
||||||
}
|
}
|
||||||
|
export function getDateFormat(date, format) {
|
||||||
|
if (date instanceof Date !== true) return;
|
||||||
|
const year = date.getFullYear();
|
||||||
|
const month = ("0" + (date.getMonth() + 1)).slice(-2);
|
||||||
|
const day = ("0" + date.getDate()).slice(-2);
|
||||||
|
const hour = ("0" + date.getHours()).slice(-2);
|
||||||
|
const minute = ("0" + date.getMinutes()).slice(-2);
|
||||||
|
const second = ("0" + date.getSeconds()).slice(-2);
|
||||||
|
return format
|
||||||
|
.replace("yyyy", year)
|
||||||
|
.replace("MM", month)
|
||||||
|
.replace("dd", day)
|
||||||
|
.replace("hh", hour)
|
||||||
|
.replace("HH", hour)
|
||||||
|
.replace("mm", minute)
|
||||||
|
.replace("ss", second);
|
||||||
|
}
|
||||||
|
export function calculateDuration(startDate, endDate) {
|
||||||
|
if (startDate instanceof Date !== true) return;
|
||||||
|
if (endDate instanceof Date !== true) return;
|
||||||
|
return convertMsToTime(endDate - startDate);
|
||||||
|
}
|
||||||
|
export function convertMsToTime(milliseconds) {
|
||||||
|
let seconds = Math.floor(milliseconds / 1000);
|
||||||
|
let minutes = Math.floor(seconds / 60);
|
||||||
|
let hours = Math.floor(minutes / 60);
|
||||||
|
|
||||||
|
seconds = seconds % 60;
|
||||||
|
minutes = minutes % 60;
|
||||||
|
//commenting to get 24 time format
|
||||||
|
//hours = hours % 24;
|
||||||
|
|
||||||
|
return `${padTo2Digits(hours)}${padTo2Digits(minutes)}`;
|
||||||
|
}
|
||||||
|
export function padTo2Digits(time) {
|
||||||
|
// Use the built-in method toString() with a radix of 10 to convert the time value to a decimal string
|
||||||
|
time = time.toString(10);
|
||||||
|
// Use the conditional operator to check if the length of the string is less than 2
|
||||||
|
return time.length < 2
|
||||||
|
? // If yes, prepend a '0' to the string and return it
|
||||||
|
"0" + time
|
||||||
|
: // If no, return the original string
|
||||||
|
time;
|
||||||
|
}
|
||||||
|
export function combineDateAndTime(date, time) {
|
||||||
|
// Use the Date.parse() method to convert the date and time strings to a numeric value
|
||||||
|
var timestamp = Date.parse(date + "T" + time);
|
||||||
|
// Use the new Date() constructor to create a new date object from the numeric value
|
||||||
|
var newDate = new Date(timestamp);
|
||||||
|
// Return the new date object
|
||||||
|
return newDate;
|
||||||
|
}
|
||||||
|
export function addMinutes(date, minutes) {
|
||||||
|
return new Date(date.getTime() + minutes * 60000);
|
||||||
|
}
|
||||||
|
export function shortTimeString(date) {
|
||||||
|
// Use a ternary operator to check if the input is a valid date object
|
||||||
|
return date instanceof Date
|
||||||
|
? // Use the built-in method toLocaleTimeString() to get the short time string in the current locale
|
||||||
|
date.toLocaleTimeString(undefined, { hour: "numeric", minute: "numeric", hour12: true })
|
||||||
|
: // Return undefined if the input is not a valid date object
|
||||||
|
undefined;
|
||||||
|
}
|
||||||
|
|
|
||||||
202
src/helpers/date-helper.spec.js
Normal file
202
src/helpers/date-helper.spec.js
Normal file
|
|
@ -0,0 +1,202 @@
|
||||||
|
import {
|
||||||
|
getFullDayName,
|
||||||
|
getFullMonthName,
|
||||||
|
get12HourTimeFormat,
|
||||||
|
get12HourTimeMobileFormat,
|
||||||
|
getDateFormat,
|
||||||
|
getDateDifferenceInDays,
|
||||||
|
calculateDuration,
|
||||||
|
padTo2Digits,
|
||||||
|
combineDateAndTime,
|
||||||
|
addMinutes,
|
||||||
|
shortTimeString,
|
||||||
|
} from "./date-helper";
|
||||||
|
|
||||||
|
describe("date-helper.js", () => {
|
||||||
|
it("get12HourTimeMobileFormat should return time in expected 12 hour mobile format.", () => {
|
||||||
|
// Define some sample times and their expected 12-hour formats
|
||||||
|
const testCases = [
|
||||||
|
{ time: "00:00", expected: "12 AM" },
|
||||||
|
{ time: "01:23", expected: "1:23 AM" },
|
||||||
|
{ time: "11:59", expected: "11:59 AM" },
|
||||||
|
{ time: "12:00", expected: "12 PM" },
|
||||||
|
{ time: "13:45", expected: "1:45 PM" },
|
||||||
|
{ time: "23:59", expected: "11:59 PM" },
|
||||||
|
];
|
||||||
|
// Loop through the test cases and call the function with each time
|
||||||
|
for (let testCase of testCases) {
|
||||||
|
// Call the function and store the result
|
||||||
|
const result = get12HourTimeMobileFormat(testCase.time);
|
||||||
|
// Assert that the result is equal to the expected 12-hour format
|
||||||
|
expect(result).toEqual(testCase.expected);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test if the function returns the correct 12-hour format for some sample times
|
||||||
|
it("should return the correct 12-hour format", function () {
|
||||||
|
// Define some sample times and their expected 12-hour formats
|
||||||
|
const testCases = [
|
||||||
|
{ time: "00:00", expected: "12:00 AM" },
|
||||||
|
{ time: "01:23", expected: "1:23 AM" },
|
||||||
|
{ time: "11:59", expected: "11:59 AM" },
|
||||||
|
{ time: "12:00", expected: "12:00 PM" },
|
||||||
|
{ time: "13:45", expected: "1:45 PM" },
|
||||||
|
{ time: "23:59", expected: "11:59 PM" },
|
||||||
|
];
|
||||||
|
// Loop through the test cases and call the function with each time
|
||||||
|
for (let testCase of testCases) {
|
||||||
|
// Call the function and store the result
|
||||||
|
const result = get12HourTimeFormat(testCase.time);
|
||||||
|
// Assert that the result is equal to the expected 12-hour format
|
||||||
|
expect(result).toEqual(testCase.expected);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// it("getFullMonthName should return full month format.", () => {
|
||||||
|
// // Arrange / Act
|
||||||
|
// const date = new Date("2023-10-01");
|
||||||
|
// const monthName = getFullMonthName(date);
|
||||||
|
|
||||||
|
// // Assert
|
||||||
|
// expect(monthName).toEqual("October");
|
||||||
|
// });
|
||||||
|
|
||||||
|
// it("getFullDayName should return full Day Name format.", () => {
|
||||||
|
// // Arrange / Act
|
||||||
|
// const date = new Date("2023-10-01");
|
||||||
|
// const dayName = getFullDayName(date);
|
||||||
|
|
||||||
|
// // Assert
|
||||||
|
// expect(dayName).toEqual("Sunday");
|
||||||
|
// });
|
||||||
|
|
||||||
|
// it("getDateFormat should return date in the given format.", () => {
|
||||||
|
// // Arrange / Act
|
||||||
|
// const date = new Date("2023-10-01");
|
||||||
|
// const format = "yyyy-MM-dd";
|
||||||
|
// const formattedDate = getDateFormat(date, format);
|
||||||
|
|
||||||
|
// // Assert
|
||||||
|
// expect(formattedDate).toEqual("2023-10-01");
|
||||||
|
// });
|
||||||
|
|
||||||
|
it("should return the correct difference in days", function () {
|
||||||
|
// Define some sample dates and their expected differences
|
||||||
|
const testCases = [
|
||||||
|
{ startDate: "2023-10-12", endDate: "2023-10-15", expected: 3 },
|
||||||
|
{ startDate: "2023-01-01", endDate: "2023-01-31", expected: 30 },
|
||||||
|
{ startDate: "2022-12-31", endDate: "2023-01-01", expected: 1 },
|
||||||
|
{ startDate: "2023-02-28", endDate: "2023-03-01", expected: 1 },
|
||||||
|
{ startDate: "2023-03-01", endDate: "2023-02-28", expected: -1 },
|
||||||
|
];
|
||||||
|
// Loop through the test cases and call the function with each pair of dates
|
||||||
|
for (let testCase of testCases) {
|
||||||
|
// Call the function and store the result
|
||||||
|
const result = getDateDifferenceInDays(testCase.startDate, testCase.endDate);
|
||||||
|
// Assert that the result is equal to the expected difference
|
||||||
|
expect(result).toEqual(testCase.expected);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("calculateDuration should return duration in hhmm format.", () => {
|
||||||
|
// Arrange / Act
|
||||||
|
const startDate = new Date("2023-10-01 10:00:00");
|
||||||
|
const endDate = new Date("2023-10-01 23:00:00");
|
||||||
|
const duration = calculateDuration(startDate, endDate);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(duration).toEqual("1300");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("padTo2Digits should return time in 2 digit format.", () => {
|
||||||
|
// Define some sample time values and their expected strings
|
||||||
|
const testCases = [
|
||||||
|
{ time: 0, expected: "00" },
|
||||||
|
{ time: 1, expected: "01" },
|
||||||
|
{ time: 9, expected: "09" },
|
||||||
|
{ time: 10, expected: "10" },
|
||||||
|
{ time: 59, expected: "59" },
|
||||||
|
];
|
||||||
|
// Loop through the test cases and call the function with each time value
|
||||||
|
for (let testCase of testCases) {
|
||||||
|
// Call the function and store the result
|
||||||
|
const result = padTo2Digits(testCase.time);
|
||||||
|
// Assert that the result is equal to the expected string
|
||||||
|
expect(result).toEqual(testCase.expected);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("combineDateAndTime should return combined dateTime format.", () => {
|
||||||
|
// Define some sample inputs and their expected outputs
|
||||||
|
const testCases = [
|
||||||
|
{ date: "2023-10-12", time: "12:00", expected: new Date(2023, 9, 12, 12, 0) },
|
||||||
|
{ date: "2023-01-01", time: "00:00", expected: new Date(2023, 0, 1, 0, 0) },
|
||||||
|
{ date: "2022-12-31", time: "23:59", expected: new Date(2022, 11, 31, 23, 59) },
|
||||||
|
{ date: "2023-02-28", time: "13:45", expected: new Date(2023, 1, 28, 13, 45) },
|
||||||
|
{ date: "2023-03-01", time: "01:23", expected: new Date(2023, 2, 1, 1, 23) },
|
||||||
|
];
|
||||||
|
// Loop through the test cases and call the function with each pair of date and time
|
||||||
|
for (let testCase of testCases) {
|
||||||
|
// Call the function and store the result
|
||||||
|
const result = combineDateAndTime(testCase.date, testCase.time);
|
||||||
|
// Assert that the result is equal to the expected new date object
|
||||||
|
expect(result.getTime()).toEqual(testCase.expected.getTime());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("addMinutes should add given time to date.", () => {
|
||||||
|
// Define some sample inputs and their expected outputs
|
||||||
|
const testCases = [
|
||||||
|
{
|
||||||
|
date: new Date(2023, 9, 12, 12, 0),
|
||||||
|
minutes: 15,
|
||||||
|
expected: new Date(2023, 9, 12, 12, 15),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
date: new Date(2023, 0, 1, 0, 0),
|
||||||
|
minutes: -30,
|
||||||
|
expected: new Date(2022, 11, 31, 23, 30),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
date: new Date(2022, 11, 31, 23, 59),
|
||||||
|
minutes: 1,
|
||||||
|
expected: new Date(2023, 0, 1, 0, 0),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
date: new Date(2023, 1, 28, 13, 45),
|
||||||
|
minutes: 60,
|
||||||
|
expected: new Date(2023, 1, 28, 14, 45),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
date: new Date(2023, 2, 1, 1, 23),
|
||||||
|
minutes: -1440,
|
||||||
|
expected: new Date(2023, 1, 28, 1, 23),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
// Loop through the test cases and call the function with each pair of date and minutes
|
||||||
|
for (let testCase of testCases) {
|
||||||
|
// Call the function and store the result
|
||||||
|
const result = addMinutes(testCase.date, testCase.minutes);
|
||||||
|
// Assert that the result is equal to the expected modified date object
|
||||||
|
expect(result.getTime()).toEqual(testCase.expected.getTime());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shortTimeString should return time in short format.", () => {
|
||||||
|
// Define some sample dates and their expected short time strings
|
||||||
|
const testCases = [
|
||||||
|
{ date: new Date(2023, 9, 12, 12, 0), expected: "12:00 PM" },
|
||||||
|
{ date: new Date(2023, 0, 1, 0, 0), expected: "12:00 AM" },
|
||||||
|
{ date: new Date(2022, 11, 31, 23, 59), expected: "11:59 PM" },
|
||||||
|
{ date: new Date(2023, 1, 28, 13, 45), expected: "1:45 PM" },
|
||||||
|
{ date: new Date(2023, 2, 1, 1, 23), expected: "1:23 AM" },
|
||||||
|
];
|
||||||
|
// Loop through the test cases and call the function with each date
|
||||||
|
for (let testCase of testCases) {
|
||||||
|
// Call the function and store the result
|
||||||
|
const result = shortTimeString(testCase.date);
|
||||||
|
// Assert that the result is equal to the expected short time string
|
||||||
|
expect(result).toEqual(testCase.expected);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
250
src/layouts/add-to-calendar/add-to-calendar.spec.js
Normal file
250
src/layouts/add-to-calendar/add-to-calendar.spec.js
Normal file
|
|
@ -0,0 +1,250 @@
|
||||||
|
// Components
|
||||||
|
import addToCalendar from "@/layouts/add-to-calendar/add-to-calendar.vue";
|
||||||
|
import { calendarOptions } from "@/constants/calendar-options";
|
||||||
|
import { AppointmentTypeStrings, RouteCodeFlags } from "@/constants/schedule-constants";
|
||||||
|
|
||||||
|
// Supporting Files
|
||||||
|
import { shallowMount } from "@vue/test-utils";
|
||||||
|
import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
||||||
|
import store from "@/store";
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.restoreAllMocks();
|
||||||
|
jest.clearAllMocks();
|
||||||
|
store.getters = {
|
||||||
|
order: {
|
||||||
|
schedule: {
|
||||||
|
date: "2019-01-01",
|
||||||
|
startTime: "09:00",
|
||||||
|
endTime: "10:00",
|
||||||
|
routeCode: "000",
|
||||||
|
},
|
||||||
|
lineItems: {
|
||||||
|
glassParts: [
|
||||||
|
{
|
||||||
|
partNumber: "ABC123",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
supportingItems: [],
|
||||||
|
},
|
||||||
|
serviceLocation: {
|
||||||
|
address: "",
|
||||||
|
address2: null,
|
||||||
|
city: "",
|
||||||
|
state: "AZ",
|
||||||
|
appointmentType: "Inshop",
|
||||||
|
zipCode: "12345",
|
||||||
|
zipCodeCtu: "01234",
|
||||||
|
provider: {
|
||||||
|
providerNumber: "123",
|
||||||
|
address: {
|
||||||
|
streetAddress: "test1",
|
||||||
|
city: "test",
|
||||||
|
state: "AZ",
|
||||||
|
zipCode: "12345",
|
||||||
|
zipCodeCtu: "01234",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
damage: {
|
||||||
|
isRepair: false,
|
||||||
|
},
|
||||||
|
referralNumber: "1234567",
|
||||||
|
},
|
||||||
|
payment: {
|
||||||
|
isInsurance: true,
|
||||||
|
},
|
||||||
|
lineItems: {
|
||||||
|
glassParts: [],
|
||||||
|
supportingItems: [],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
afterEach(() => {
|
||||||
|
store.getters = {};
|
||||||
|
jest.restoreAllMocks();
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Add-to-calendar methods...", () => {
|
||||||
|
test("Add-to-calendar should trigger openModal method", () => {
|
||||||
|
//Arrange
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
//Act
|
||||||
|
wrapper.vm.openCalendarModal();
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
expect(wrapper.vm.$refs.calendarModalQuestion.openModal).toBeCalled();
|
||||||
|
});
|
||||||
|
test("serviceType should return 'replacement and recalibration' when isRepair and funnelHasRecalibrationPart true.", () => {
|
||||||
|
//Arrange
|
||||||
|
store.getters.order.damage.isRepair = true;
|
||||||
|
store.getters.funnelHasRecalibrationPart = true;
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
//Act
|
||||||
|
const testValue = wrapper.vm.serviceType();
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
expect(testValue).toEqual("replacement and recalibration");
|
||||||
|
});
|
||||||
|
test("serviceType should return 'replacement' when isRepair is true and funnelHasRecalibrationPart is false.", () => {
|
||||||
|
//Arrange
|
||||||
|
store.getters.order.damage.isRepair = true;
|
||||||
|
store.getters.funnelHasRecalibrationPart = false;
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
//Act
|
||||||
|
const testValue = wrapper.vm.serviceType();
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
expect(testValue).toEqual("replacement");
|
||||||
|
});
|
||||||
|
test("serviceType should return 'repair' when isRepair is false.", () => {
|
||||||
|
//Arrange
|
||||||
|
store.getters.order.damage.isRepair = false;
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
//Act
|
||||||
|
const testValue = wrapper.vm.serviceType();
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
expect(testValue).toEqual("repair");
|
||||||
|
});
|
||||||
|
test("getCalendarData should return expected model value.", () => {
|
||||||
|
//Arrange
|
||||||
|
const type = calendarOptions.OUTLOOKCOM;
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
//Act
|
||||||
|
const testValue = wrapper.vm.getCalendarData(type);
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
expect(testValue.name).toEqual(calendarOptions.OUTLOOKCOM);
|
||||||
|
});
|
||||||
|
test("getappointmentData should return expected model value for appointmentType mobile.", () => {
|
||||||
|
//Arrange
|
||||||
|
store.getters.order.serviceLocation.appointmentType = AppointmentTypeStrings.MOBILE;
|
||||||
|
store.getters.order.damage.isRepair = true;
|
||||||
|
store.getters.funnelHasRecalibrationPart = true;
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
//Act
|
||||||
|
const testValue = wrapper.vm.getappointmentData();
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
expect(testValue.Subject).toEqual("Safelite replacement and recalibration appointment");
|
||||||
|
});
|
||||||
|
test("getappointmentData should return expected model value for appointmentType DROP_OFF and OVERNIGHT_DROP_OFF.", () => {
|
||||||
|
//Arrange
|
||||||
|
store.getters.order.serviceLocation.appointmentType = AppointmentTypeStrings.DROP_OFF;
|
||||||
|
store.getters.order.schedule.routeCode = RouteCodeFlags.OVERNIGHT_DROP_OFF;
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
//Act
|
||||||
|
const testValue = wrapper.vm.getappointmentData();
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
expect(testValue.Subject).toEqual("Drop off your vehicle by 5:30 PM");
|
||||||
|
});
|
||||||
|
test("getappointmentData should return expected model value for appointmentType DROP_OFF and ALL_DAY_DROP_OFF and isSameDayDropOff.", () => {
|
||||||
|
//Arrange
|
||||||
|
store.getters.order.serviceLocation.appointmentType = AppointmentTypeStrings.DROP_OFF;
|
||||||
|
store.getters.order.schedule.routeCode = RouteCodeFlags.ALL_DAY_DROP_OFF;
|
||||||
|
store.getters.order.schedule.date = new Date().toISOString().split("T")[0];
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
//Act
|
||||||
|
const testValue = wrapper.vm.getappointmentData();
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
expect(testValue.Subject).toEqual("Drop off your vehicle in the next 30 - 60 minutes");
|
||||||
|
});
|
||||||
|
test("getappointmentData should return expected model value for appointmentType DROP_OFF and ALL_DAY_DROP_OFF.", () => {
|
||||||
|
//Arrange
|
||||||
|
store.getters.order.serviceLocation.appointmentType = AppointmentTypeStrings.DROP_OFF;
|
||||||
|
store.getters.order.schedule.routeCode = RouteCodeFlags.ALL_DAY_DROP_OFF;
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
//Act
|
||||||
|
const testValue = wrapper.vm.getappointmentData();
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
expect(testValue.Subject).toEqual("Drop off your vehicle between 9:00 AM - 11:00 AM");
|
||||||
|
});
|
||||||
|
test("getappointmentData should return expected model value for appointmentType DROP_OFF.", () => {
|
||||||
|
//Arrange
|
||||||
|
store.getters.order.serviceLocation.appointmentType = AppointmentTypeStrings.DROP_OFF;
|
||||||
|
store.getters.order.damage.isRepair = false;
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
//Act
|
||||||
|
const testValue = wrapper.vm.getappointmentData();
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
expect(testValue.Subject).toEqual("Safelite repair appointment");
|
||||||
|
});
|
||||||
|
test("getappointmentData should return expected model value for appointmentType IN_SHOP.", () => {
|
||||||
|
//Arrange
|
||||||
|
store.getters.order.serviceLocation.appointmentType = AppointmentTypeStrings.IN_SHOP;
|
||||||
|
store.getters.order.damage.isRepair = false;
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
//Act
|
||||||
|
const testValue = wrapper.vm.getappointmentData();
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
expect(testValue.Subject).toEqual("Safelite repair appointment");
|
||||||
|
});
|
||||||
|
test("isSameDayDropOff should return true for current schedule date.", () => {
|
||||||
|
//Arrange
|
||||||
|
store.getters.order.schedule.date = new Date().toISOString().split("T")[0];
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
//Act
|
||||||
|
const testValue = wrapper.vm.isSameDayDropOff();
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
expect(testValue).toEqual(true);
|
||||||
|
});
|
||||||
|
test("setCalendarAppointment should call getAppointment method for iCAL and Outlook.", () => {
|
||||||
|
//Arrange
|
||||||
|
const newValue = { name: calendarOptions.OUTLOOK };
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
|
wrapper.vm.getAppointment = jest.fn();
|
||||||
|
//Act
|
||||||
|
wrapper.vm.setCalendarAppointment(newValue);
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
expect(wrapper.vm.getAppointment).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
test("setCalendarAppointment should call window.open for Google, yahoo and Outlook.com.", () => {
|
||||||
|
//Arrange
|
||||||
|
const newValue = { name: calendarOptions.GOOGLE };
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
|
window.open = jest.fn();
|
||||||
|
|
||||||
|
//Act
|
||||||
|
wrapper.vm.setCalendarAppointment(newValue);
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
expect(window.open).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function setupMocks({ customMountOptions }) {
|
||||||
|
const mountOptions = getMountOptions({
|
||||||
|
...customMountOptions,
|
||||||
|
});
|
||||||
|
|
||||||
|
const wrapper = shallowMount(addToCalendar, mountOptions);
|
||||||
|
|
||||||
|
//wrapper.vm.setCmsContent = jest.fn();
|
||||||
|
//wrapper.vm.$refs.datePicker.initializeComponent = jest.fn();
|
||||||
|
//wrapper.vm.$refs.locationAlerts.initializeComponent = jest.fn();
|
||||||
|
//wrapper.vm.$refs.funnelFooter.updateButtonText = jest.fn();
|
||||||
|
wrapper.vm.$refs.calendarModalQuestion.openModal = jest.fn();
|
||||||
|
|
||||||
|
return { wrapper };
|
||||||
|
}
|
||||||
300
src/layouts/add-to-calendar/add-to-calendar.vue
Normal file
300
src/layouts/add-to-calendar/add-to-calendar.vue
Normal file
|
|
@ -0,0 +1,300 @@
|
||||||
|
<template>
|
||||||
|
<div class="calendar-section">
|
||||||
|
<div class="add-to-calendar">
|
||||||
|
<button v-on:click="openCalendarModal">
|
||||||
|
<span class="add-to-calendar-span">
|
||||||
|
<img src="@/assets/img/icons/add-to-calendar.svg" class="add-to-calendar-img" />
|
||||||
|
<span class="add-to-calendar-text">Add to calendar</span></span
|
||||||
|
>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<calendarModalQuestion
|
||||||
|
ref="calendarModalQuestion"
|
||||||
|
customComponentId="calendarModalQuestion"
|
||||||
|
cmsWidgetName="CalendarModalQuestion"
|
||||||
|
@calendar-modal-closed="calendarModalClosed"
|
||||||
|
:calendarOptionsData="calendarValues"
|
||||||
|
v-model="selectedCalendarOption">
|
||||||
|
</calendarModalQuestion>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { calendarOptions } from "@/constants/calendar-options";
|
||||||
|
import { calendarStatus } from "@/constants/calendar-status";
|
||||||
|
import calendarModalQuestion from "@/layouts/add-to-calendar/calendar-modal-question/calendar-modal-question";
|
||||||
|
import {
|
||||||
|
getDateFormat,
|
||||||
|
calculateDuration,
|
||||||
|
combineDateAndTime,
|
||||||
|
shortTimeString,
|
||||||
|
addMinutes,
|
||||||
|
} from "@/helpers/date-helper";
|
||||||
|
import { getCalendarFile, download } from "@/helpers/add-to-calendar-helper";
|
||||||
|
import { AppointmentTypeStrings, RouteCodeFlags } from "@/constants/schedule-constants";
|
||||||
|
import store from "@/store";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "add-to-calendar",
|
||||||
|
components: {
|
||||||
|
calendarModalQuestion,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
appointment: this.getappointmentData(),
|
||||||
|
selectedCalendarOption: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
calendarValues: {
|
||||||
|
get: function () {
|
||||||
|
var obj = [];
|
||||||
|
const options = Object.values(calendarOptions);
|
||||||
|
for (var i = 0; i < options.length; i++) {
|
||||||
|
obj[i] = this.getCalendarData(options[i]);
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
selectedCalendarOption(newValue) {
|
||||||
|
if (newValue) {
|
||||||
|
this.setCalendarAppointment(newValue);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
setCalendarAppointment(calendarOption) {
|
||||||
|
if (
|
||||||
|
calendarOption.name.includes(calendarOptions.ICAL) ||
|
||||||
|
calendarOption.name.includes(calendarOptions.OUTLOOK)
|
||||||
|
) {
|
||||||
|
this.getAppointment();
|
||||||
|
} else {
|
||||||
|
window.open(calendarOption.url);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
calendarModalClosed() {
|
||||||
|
// Clear the selectedOption if close the modal
|
||||||
|
this.selectedCalendarOption = null;
|
||||||
|
},
|
||||||
|
openCalendarModal() {
|
||||||
|
this.$refs["calendarModalQuestion"].openModal();
|
||||||
|
},
|
||||||
|
scheduleDate() {
|
||||||
|
return store.getters.order.schedule.date;
|
||||||
|
},
|
||||||
|
scheduleStartTime() {
|
||||||
|
return store.getters.order.schedule.startTime;
|
||||||
|
},
|
||||||
|
scheduleEndTime() {
|
||||||
|
return store.getters.order.schedule.endTime;
|
||||||
|
},
|
||||||
|
serviceLocationAddress() {
|
||||||
|
return store.getters.order.serviceLocation.address;
|
||||||
|
},
|
||||||
|
ServiceLocationAddress2() {
|
||||||
|
return store.getters.order.serviceLocation.address2;
|
||||||
|
},
|
||||||
|
serviceLocationCity() {
|
||||||
|
return store.getters.order.serviceLocation.city;
|
||||||
|
},
|
||||||
|
serviceLocationState() {
|
||||||
|
return store.getters.order.serviceLocation.state;
|
||||||
|
},
|
||||||
|
serviceLocationZipCode() {
|
||||||
|
return store.getters.order.serviceLocation.zipCode;
|
||||||
|
},
|
||||||
|
providerAddress() {
|
||||||
|
return store.getters.order.serviceLocation.provider.address.streetAddress;
|
||||||
|
},
|
||||||
|
providerCity() {
|
||||||
|
return store.getters.order.serviceLocation.provider.address.city;
|
||||||
|
},
|
||||||
|
providerState() {
|
||||||
|
return store.getters.order.serviceLocation.provider.address.state;
|
||||||
|
},
|
||||||
|
providerZipCode() {
|
||||||
|
return store.getters.order.serviceLocation.provider.address.zipCode;
|
||||||
|
},
|
||||||
|
appointmentType() {
|
||||||
|
return store.getters.order.serviceLocation.appointmentType;
|
||||||
|
},
|
||||||
|
uniqueId() {
|
||||||
|
return store.getters.order.referralNumber?.toString();
|
||||||
|
},
|
||||||
|
serviceType() {
|
||||||
|
const isRepair = store.getters.order.damage.isRepair;
|
||||||
|
const funnelHasRecalibrationPart = store.getters.funnelHasRecalibrationPart;
|
||||||
|
if (isRepair) {
|
||||||
|
if (funnelHasRecalibrationPart) {
|
||||||
|
return "replacement and recalibration";
|
||||||
|
} else {
|
||||||
|
return "replacement";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "repair";
|
||||||
|
},
|
||||||
|
routeCode() {
|
||||||
|
return store.getters.order.schedule.routeCode;
|
||||||
|
},
|
||||||
|
isSameDayDropOff() {
|
||||||
|
const todaysDate = new Date().toISOString().split("T")[0];
|
||||||
|
return this.scheduleDate() === todaysDate;
|
||||||
|
},
|
||||||
|
getCalendarData(type) {
|
||||||
|
var URL = "";
|
||||||
|
const dateFormat = "yyyyMMddTHHmmss";
|
||||||
|
const outlookComTimeSpanFormat = "yyyy-MM-ddTHH:mm:ss";
|
||||||
|
if (type == calendarOptions.OUTLOOKCOM) {
|
||||||
|
URL = `https://outlook.office.com/calendar/deeplink/compose?path=/calendar/action/compose&rru=addevent&startdt=${encodeURIComponent(
|
||||||
|
getDateFormat(this.appointment.StartDate, outlookComTimeSpanFormat)
|
||||||
|
)}&enddt=${encodeURIComponent(
|
||||||
|
getDateFormat(this.appointment.EndDate, outlookComTimeSpanFormat)
|
||||||
|
)}&subject=${encodeURIComponent(
|
||||||
|
this.appointment.Subject
|
||||||
|
)}&body=${encodeURIComponent(this.appointment.Body)}&location=${encodeURIComponent(
|
||||||
|
this.appointment.Location
|
||||||
|
)}`;
|
||||||
|
}
|
||||||
|
if (type == calendarOptions.GOOGLE) {
|
||||||
|
URL = `https://www.google.com/calendar/render?action=TEMPLATE&text=${encodeURIComponent(
|
||||||
|
this.appointment.Subject
|
||||||
|
)}&dates=${encodeURIComponent(
|
||||||
|
getDateFormat(this.appointment.StartDate, dateFormat)
|
||||||
|
)}/${encodeURIComponent(
|
||||||
|
getDateFormat(this.appointment.EndDate, dateFormat)
|
||||||
|
)}&details=${encodeURIComponent(
|
||||||
|
this.appointment.Body
|
||||||
|
)}&location=${encodeURIComponent(this.appointment.Location)}&sf=true&output=xml`;
|
||||||
|
}
|
||||||
|
if (type == calendarOptions.YAHOO) {
|
||||||
|
URL = `https://calendar.yahoo.com/?v=60&TITLE=${encodeURIComponent(
|
||||||
|
this.appointment.Subject
|
||||||
|
)}&DESC=${encodeURIComponent(this.appointment.Body)}&ST=${encodeURIComponent(
|
||||||
|
getDateFormat(this.appointment.StartDate, dateFormat)
|
||||||
|
)}&DUR=${this.appointment.Duration}&in_loc=${encodeURIComponent(
|
||||||
|
this.appointment.Location
|
||||||
|
)}`;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
url: URL,
|
||||||
|
name: type,
|
||||||
|
icon: require(`@/assets/img/icons/${type}.svg`),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
getappointmentData() {
|
||||||
|
var subject = "";
|
||||||
|
var location = "";
|
||||||
|
var startDateTime = combineDateAndTime(this.scheduleDate(), this.scheduleStartTime());
|
||||||
|
var endDateTime = combineDateAndTime(this.scheduleDate(), this.scheduleEndTime());
|
||||||
|
var body = "";
|
||||||
|
var isHTML = false;
|
||||||
|
var duration = "";
|
||||||
|
|
||||||
|
if (this.appointmentType() === AppointmentTypeStrings.MOBILE) {
|
||||||
|
location = `${
|
||||||
|
this.ServiceLocationAddress2() ? this.ServiceLocationAddress2() + ", " : ""
|
||||||
|
}${this.serviceLocationAddress()}, ${this.serviceLocationCity()}, ${this.serviceLocationState()} ${this.serviceLocationZipCode()}`;
|
||||||
|
subject = `Safelite ${this.serviceType()} appointment`;
|
||||||
|
|
||||||
|
body =
|
||||||
|
"Your safety is our top priority, so we'll let you know who's coming to you and your tech will reach out to let you know when they're on the way.";
|
||||||
|
} else {
|
||||||
|
location = `${this.providerAddress()}, ${this.providerCity()}, ${this.providerState()} ${this.providerZipCode()}`;
|
||||||
|
if (this.appointmentType() === AppointmentTypeStrings.DROP_OFF) {
|
||||||
|
if (this.routeCode().includes(RouteCodeFlags.OVERNIGHT_DROP_OFF)) {
|
||||||
|
subject = "Drop off your vehicle by 5:30 PM";
|
||||||
|
|
||||||
|
body =
|
||||||
|
"Drop off your vehicle by 5:30 PM the night of your scheduled appointment. The shop will work with you on a convenient pick-up time.";
|
||||||
|
} else if (this.routeCode().includes(RouteCodeFlags.ALL_DAY_DROP_OFF)) {
|
||||||
|
if (this.isSameDayDropOff()) {
|
||||||
|
subject = "Drop off your vehicle in the next 30 - 60 minutes";
|
||||||
|
endDateTime = addMinutes(startDateTime, 120);
|
||||||
|
body =
|
||||||
|
"Drop your vehicle off in the next 30 - 60 minutes. We'll work your vehicle into our day's schedule. The shop will work with you on a convenient pick-up time.";
|
||||||
|
} else {
|
||||||
|
endDateTime = addMinutes(startDateTime, 120);
|
||||||
|
subject =
|
||||||
|
"Drop off your vehicle between " +
|
||||||
|
shortTimeString(startDateTime) +
|
||||||
|
" - " +
|
||||||
|
shortTimeString(endDateTime);
|
||||||
|
|
||||||
|
body = `Drop off your vehicle by ${shortTimeString(
|
||||||
|
endDateTime
|
||||||
|
)} and pick it up later in the day. When you arrive, park in an open space and follow the key drop instructions. We'll text you when your vehicle is ready.`;
|
||||||
|
}
|
||||||
|
} //normal time slot
|
||||||
|
else {
|
||||||
|
subject = `Safelite ${this.serviceType()} appointment`;
|
||||||
|
body = "Just a reminder that you're coming to us at " + location + ".";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
subject = `Safelite ${this.serviceType()} appointment`;
|
||||||
|
body = "Just a reminder that you're coming to us at " + location + ".";
|
||||||
|
}
|
||||||
|
duration = calculateDuration(startDateTime, endDateTime);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
Subject: subject,
|
||||||
|
Location: location,
|
||||||
|
StartDate: startDateTime,
|
||||||
|
EndDate: endDateTime,
|
||||||
|
RefrrlSeqNum: this.uniqueId(),
|
||||||
|
Body: body,
|
||||||
|
IsHTML: isHTML,
|
||||||
|
Duration: duration,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
getAppointment() {
|
||||||
|
let body = this.appointment.Body;
|
||||||
|
body = body.replace("|", "<").replace("~", ">");
|
||||||
|
|
||||||
|
const calFile = {
|
||||||
|
Location: this.appointment.Location,
|
||||||
|
Body: body,
|
||||||
|
IsHTML: this.appointment.IsHTML,
|
||||||
|
UniqueId: this.appointment.RefrrlSeqNum,
|
||||||
|
Subject: this.appointment.Subject,
|
||||||
|
StartDate: this.appointment.StartDate,
|
||||||
|
EndDate: this.appointment.EndDate,
|
||||||
|
Status: calendarStatus.BUSY,
|
||||||
|
};
|
||||||
|
var calBody = getCalendarFile(calFile);
|
||||||
|
download("SafeliteAppointment.ics", calBody);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
.add-to-calendar-text {
|
||||||
|
margin: 0 0 0 7px;
|
||||||
|
color: #1574a1;
|
||||||
|
vertical-align: middle;
|
||||||
|
font-weight: 500;
|
||||||
|
font-family: "Roboto";
|
||||||
|
}
|
||||||
|
.add-to-calendar button {
|
||||||
|
border: 0;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
.calendar-section {
|
||||||
|
display: table;
|
||||||
|
position: relative;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
.calendar-section .add-to-calendar {
|
||||||
|
padding-top: 15px;
|
||||||
|
}
|
||||||
|
.calendar-section .add-to-calendar .add-to-calendar-span {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.calendar-section .add-to-calendar .add-to-calendar-img {
|
||||||
|
display: inline;
|
||||||
|
width: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
<template>
|
||||||
|
<baseInputButton
|
||||||
|
v-bind="$props"
|
||||||
|
buttonWrapperClasses="list-group base-input-button list-button rounded-3 d-flex flex-column w-100 mb-2"
|
||||||
|
v-model="selectedValue">
|
||||||
|
<div
|
||||||
|
:aria-label="buttonLabel"
|
||||||
|
class="button-content list-button-content d-flex flex-column justify-content-center py-3 px-4">
|
||||||
|
<span class="m-0 position-relative" :class="textPosition">
|
||||||
|
<img :src="buttonLabel" />
|
||||||
|
<span class="calendar-text">{{ value }}</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</baseInputButton>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import baseInputButton from "@/digital-components/base-input-button/base-input-button";
|
||||||
|
import inputButtonWrapperMixin from "@/mixins/input-button-wrapper-mixin";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "calendarModalListButton",
|
||||||
|
mixins: [inputButtonWrapperMixin],
|
||||||
|
components: {
|
||||||
|
baseInputButton,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.list-button {
|
||||||
|
outline: none;
|
||||||
|
input[type="radio"],
|
||||||
|
input[type="checkbox"] {
|
||||||
|
position: static; //override bootstrap
|
||||||
|
|
||||||
|
&:focus-visible + .list-button-content {
|
||||||
|
box-shadow: 0 0 0 2.5px $blue;
|
||||||
|
}
|
||||||
|
&:focus + .list-button-content {
|
||||||
|
box-shadow: 0 0 0 2.5px $blue;
|
||||||
|
}
|
||||||
|
&:checked + .list-button-content {
|
||||||
|
color: $black;
|
||||||
|
font-weight: 500;
|
||||||
|
background: $blue-100;
|
||||||
|
box-shadow: 0 0 0 1px $blue;
|
||||||
|
}
|
||||||
|
&:checked:focus + .list-button-content {
|
||||||
|
box-shadow: 0 0 0 2.5px $blue;
|
||||||
|
}
|
||||||
|
&:checked + .list-button-content p,
|
||||||
|
&:checked + .list-button-content span {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
&:checked + .list-button-content span:nth-child(2) {
|
||||||
|
font-weight: 400;
|
||||||
|
color: $gray-600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.list-button-content {
|
||||||
|
color: $gray-600;
|
||||||
|
position: relative;
|
||||||
|
background: $white;
|
||||||
|
transition: all 150ms linear;
|
||||||
|
border-radius: $border-radius-lg;
|
||||||
|
border: 1px solid $gray-500;
|
||||||
|
width: 100%;
|
||||||
|
outline: none;
|
||||||
|
|
||||||
|
span.calendar-text {
|
||||||
|
margin-left: 5px;
|
||||||
|
padding: 2px 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.position-relative {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
import { shallowMount } from "@vue/test-utils";
|
||||||
|
import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
||||||
|
import calendarModalQuestion from "@/layouts/add-to-calendar/calendar-modal-question/calendar-modal-question.vue";
|
||||||
|
|
||||||
|
describe("calendar-modal-question.vue", () => {
|
||||||
|
test("When selected calendar option is changed, a correctly selectedCalendarOption should be emitted", async () => {
|
||||||
|
//Arrange
|
||||||
|
const url = "iCal url";
|
||||||
|
const name = "iCal";
|
||||||
|
const icon = "iCal.svg";
|
||||||
|
const expectedEmit = [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
url: url,
|
||||||
|
name: name,
|
||||||
|
icon: icon,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
const { wrapper } = setupMocks({
|
||||||
|
customMountOptions: {
|
||||||
|
propsData: {
|
||||||
|
calendarOptionsData: [
|
||||||
|
{
|
||||||
|
url: url,
|
||||||
|
name: name,
|
||||||
|
icon: icon,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
wrapper.vm.$refs.calendarOptions.closeModal = jest.fn();
|
||||||
|
wrapper.vm.selectedCalendarOption = name;
|
||||||
|
|
||||||
|
//Act
|
||||||
|
wrapper.vm.setSelectedCalendarOption();
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
expect(wrapper.emitted()["update:modelValue"]).toEqual(expectedEmit);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function setupMocks({ customMountOptions }) {
|
||||||
|
const mountOptions = getMountOptions({
|
||||||
|
...customMountOptions,
|
||||||
|
});
|
||||||
|
|
||||||
|
const wrapper = shallowMount(calendarModalQuestion, mountOptions);
|
||||||
|
return { wrapper };
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,179 @@
|
||||||
|
<template>
|
||||||
|
<modal
|
||||||
|
:ref="modalName"
|
||||||
|
:fontSize="true"
|
||||||
|
:headerText="modalHeaderText"
|
||||||
|
:footerButtonText="footerCloseButtonText"
|
||||||
|
:onModalClosedCallback="onModalClosed"
|
||||||
|
@isModalOpened="setModalStatus"
|
||||||
|
@footer-button-event="setSelectedCalendarOption"
|
||||||
|
class="calendar-modal">
|
||||||
|
<template v-if="isModalOpened">
|
||||||
|
<buttonQuestion
|
||||||
|
ref="buttonQuestion"
|
||||||
|
buttonTypeString="calendarModalListButton"
|
||||||
|
:buttonTypeObject="calendarModalListButton"
|
||||||
|
:answers="getCalendarOptionData"
|
||||||
|
groupName="chooseCalendarType"
|
||||||
|
class="mt-1"
|
||||||
|
textPosition="text-center"
|
||||||
|
v-model="selectedCalendarOption"
|
||||||
|
isRequired
|
||||||
|
validationRules="calendar-option-required" />
|
||||||
|
</template>
|
||||||
|
</modal>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import modal from "@/digital-components/modal/modal";
|
||||||
|
import buttonQuestion from "@/digital-components/button-question/button-question";
|
||||||
|
import calendarModalListButton from "./calendar-modal-list-button/calendar-modal-list-button";
|
||||||
|
import { defineRule, useField } from "vee-validate";
|
||||||
|
import { errorMessages } from "@/constants/error-messages";
|
||||||
|
import { required } from "@/helpers/validation-rules";
|
||||||
|
import { deepClone } from "@/helpers/object-helper";
|
||||||
|
import { v4 as uuidv4 } from "uuid";
|
||||||
|
// Validation for the modal button
|
||||||
|
defineRule("calendar-option-required", required(errorMessages.OPTION_REQUIRED));
|
||||||
|
export default {
|
||||||
|
name: "calendar-modal-question",
|
||||||
|
components: {
|
||||||
|
modal,
|
||||||
|
buttonQuestion,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
calendarOptionsData: Object,
|
||||||
|
modelValue: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({
|
||||||
|
url: null,
|
||||||
|
name: null,
|
||||||
|
icon: null,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isModalOpened: false,
|
||||||
|
selectedValue: null,
|
||||||
|
calendarModalListButton: calendarModalListButton,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
setup(props) {
|
||||||
|
const uuid = uuidv4();
|
||||||
|
const componentId = !props.customComponentId
|
||||||
|
? `component-${uuid}`
|
||||||
|
: props.customComponentId;
|
||||||
|
|
||||||
|
const modelValue = deepClone(props).modelValue;
|
||||||
|
const initialValue = modelValue;
|
||||||
|
|
||||||
|
const fieldOptions = {
|
||||||
|
value: modelValue,
|
||||||
|
initialValue: initialValue,
|
||||||
|
};
|
||||||
|
|
||||||
|
const { errorMessage, handleChange, meta, validate, errors } = useField(
|
||||||
|
componentId,
|
||||||
|
props.validationRules,
|
||||||
|
fieldOptions
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
componentId,
|
||||||
|
errorMessage,
|
||||||
|
handleChange,
|
||||||
|
validate,
|
||||||
|
meta,
|
||||||
|
errors,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
modelValue: {
|
||||||
|
handler(newValue) {
|
||||||
|
this.handleChange(newValue);
|
||||||
|
},
|
||||||
|
deep: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
modalName() {
|
||||||
|
return "calendarOptions";
|
||||||
|
},
|
||||||
|
modalHeaderText() {
|
||||||
|
return "Add to Calendar";
|
||||||
|
},
|
||||||
|
footerCloseButtonText() {
|
||||||
|
return "Add to Calendar";
|
||||||
|
},
|
||||||
|
modal() {
|
||||||
|
return this.$refs[this.modalName];
|
||||||
|
},
|
||||||
|
selectedCalendarOption: {
|
||||||
|
get: function () {
|
||||||
|
return this.modelValue?.name;
|
||||||
|
},
|
||||||
|
set: function (newValue) {
|
||||||
|
// Button Question only supports Number, or String data types so we must convert to the full object before emitting
|
||||||
|
this.selectedValue = this.getSelectedCalendarObject(newValue);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
getCalendarOptionData() {
|
||||||
|
return this.calendarOptionsData.map((item) => {
|
||||||
|
return {
|
||||||
|
value: item.name,
|
||||||
|
buttonLabel: item.icon,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
openModal() {
|
||||||
|
this.modal.openModal();
|
||||||
|
},
|
||||||
|
setModalStatus(isOpened) {
|
||||||
|
this.isModalOpened = isOpened;
|
||||||
|
},
|
||||||
|
closeModal() {
|
||||||
|
this.modal.closeModal();
|
||||||
|
},
|
||||||
|
onModalClosed() {
|
||||||
|
this.$emit("calendar-modal-closed");
|
||||||
|
},
|
||||||
|
async setSelectedCalendarOption() {
|
||||||
|
this.$emit("update:modelValue", this.selectedValue);
|
||||||
|
this.closeModal();
|
||||||
|
},
|
||||||
|
|
||||||
|
getSelectedCalendarObject(calendarOptionId) {
|
||||||
|
const calendarOption = this.calendarOptionsData?.find(
|
||||||
|
(calendarOption) => calendarOption.name == calendarOptionId
|
||||||
|
);
|
||||||
|
|
||||||
|
if (calendarOption) {
|
||||||
|
return {
|
||||||
|
url: calendarOption.url,
|
||||||
|
name: calendarOption.name,
|
||||||
|
icon: calendarOption.icon,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
url: null,
|
||||||
|
name: null,
|
||||||
|
icon: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.calendar-modal.modal.modal-component {
|
||||||
|
> .modal-dialog > .modal-content {
|
||||||
|
margin-top: 24px;
|
||||||
|
}
|
||||||
|
.modal-header {
|
||||||
|
padding-bottom: 0;
|
||||||
|
margin-bottom: 0 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
// Components
|
// Components
|
||||||
import confirmation from "@/layouts/confirmation/confirmation.vue";
|
import confirmation from "@/layouts/confirmation/confirmation.vue";
|
||||||
|
import { AppointmentTypeStrings } from "@/constants/schedule-constants";
|
||||||
|
import { applicationConfig } from "@/constants/application-config.js";
|
||||||
|
|
||||||
// Supporting Files
|
// Supporting Files
|
||||||
import { shallowMount } from "@vue/test-utils";
|
import { shallowMount } from "@vue/test-utils";
|
||||||
|
|
@ -12,14 +14,14 @@ import router from "@/router";
|
||||||
jest.mock("@/helpers/layout-helper.js", () => ({
|
jest.mock("@/helpers/layout-helper.js", () => ({
|
||||||
settleAllPromises: jest.fn(),
|
settleAllPromises: jest.fn(),
|
||||||
}));
|
}));
|
||||||
|
var wordingText = "wording Text {custom:ADDRESS}";
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
jest.restoreAllMocks();
|
jest.restoreAllMocks();
|
||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
store.getters = {
|
store.getters = {
|
||||||
order: {
|
order: {
|
||||||
schedule: {
|
schedule: {
|
||||||
date: "2019-01-01",
|
date: "2023-01-01",
|
||||||
startTime: "09:00",
|
startTime: "09:00",
|
||||||
endTime: "10:00",
|
endTime: "10:00",
|
||||||
routeCode: "000",
|
routeCode: "000",
|
||||||
|
|
@ -33,9 +35,9 @@ beforeEach(() => {
|
||||||
supportingItems: [],
|
supportingItems: [],
|
||||||
},
|
},
|
||||||
serviceLocation: {
|
serviceLocation: {
|
||||||
address: "",
|
address: "test",
|
||||||
address2: null,
|
address2: "123",
|
||||||
city: "",
|
city: "test",
|
||||||
state: "AZ",
|
state: "AZ",
|
||||||
appointmentType: "Inshop",
|
appointmentType: "Inshop",
|
||||||
zipCode: "12345",
|
zipCode: "12345",
|
||||||
|
|
@ -112,8 +114,188 @@ describe("confirmation.vue", () => {
|
||||||
expect(window.location.assign).toHaveBeenCalled();
|
expect(window.location.assign).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
describe("computed properties...", () => {
|
||||||
|
// test("ScheduleDateFormatted should return date in expected format.", () => {
|
||||||
|
// //Arrange
|
||||||
|
// const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
// // Act
|
||||||
|
// const testValue = wrapper.vm.ScheduleDateFormatted;
|
||||||
|
|
||||||
|
// // Assert
|
||||||
|
// expect(testValue).toEqual("Sunday, January 1");
|
||||||
|
// });
|
||||||
|
test("ScheduleTimeFormatted should return mobile time in expected format.", () => {
|
||||||
|
//Arrange
|
||||||
|
store.getters.order.serviceLocation.appointmentType = AppointmentTypeStrings.MOBILE;
|
||||||
|
store.getters.order.schedule.startTime = "09:00";
|
||||||
|
store.getters.order.schedule.endTime = "11:00";
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const testValue = wrapper.vm.ScheduleTimeFormatted;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(testValue).toEqual("Between 9 AM - 11 AM");
|
||||||
|
});
|
||||||
|
test("ScheduleTimeFormatted should return DROP_OFF time in expected format.", () => {
|
||||||
|
//Arrange
|
||||||
|
store.getters.order.serviceLocation.appointmentType = AppointmentTypeStrings.DROP_OFF;
|
||||||
|
store.getters.order.schedule.startTime = "09:00";
|
||||||
|
store.getters.order.schedule.endTime = "11:00";
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const testValue = wrapper.vm.ScheduleTimeFormatted;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(testValue).toEqual("Drop off before 9:30 AM");
|
||||||
|
});
|
||||||
|
test("ScheduleTimeFormatted should return Inshop time in expected format.", () => {
|
||||||
|
//Arrange
|
||||||
|
store.getters.order.serviceLocation.appointmentType = AppointmentTypeStrings.IN_SHOP;
|
||||||
|
store.getters.order.schedule.startTime = "09:00";
|
||||||
|
store.getters.order.schedule.endTime = "11:00";
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const testValue = wrapper.vm.ScheduleTimeFormatted;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(testValue).toEqual("at 9:00 AM");
|
||||||
|
});
|
||||||
|
test("AppointmentWordingText should return mobile text in expected format.", () => {
|
||||||
|
//Arrange
|
||||||
|
store.getters.order.serviceLocation.appointmentType = AppointmentTypeStrings.MOBILE;
|
||||||
|
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
|
// Act
|
||||||
|
const testValue = wrapper.vm.AppointmentWordingText;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(testValue).toEqual("wording Text 123, test,<br/> test, AZ 12345");
|
||||||
|
});
|
||||||
|
test("AppointmentWordingText should return DROP_OFF text in expected format.", () => {
|
||||||
|
//Arrange
|
||||||
|
store.getters.order.serviceLocation.appointmentType = AppointmentTypeStrings.DROP_OFF;
|
||||||
|
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const testValue = wrapper.vm.AppointmentWordingText;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(testValue).toEqual("wording Text test1,<br/> test, AZ 12345");
|
||||||
|
});
|
||||||
|
test("AppointmentWordingText should return IN_SHOP text in expected format.", () => {
|
||||||
|
//Arrange
|
||||||
|
store.getters.order.serviceLocation.appointmentType = AppointmentTypeStrings.IN_SHOP;
|
||||||
|
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const testValue = wrapper.vm.AppointmentWordingText;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(testValue).toEqual("wording Text test1,<br/> test, AZ 12345");
|
||||||
|
});
|
||||||
|
test("ConfirmationEmailText should return text in expected format.", () => {
|
||||||
|
//Arrange
|
||||||
|
wordingText = "{custom:MY_ACCOUNT_URL}";
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
|
// Act
|
||||||
|
const testValue = wrapper.vm.ConfirmationEmailText;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(testValue).toEqual(applicationConfig.MY_ACCOUNT);
|
||||||
|
});
|
||||||
|
test("ScheduleDate should return date in expected format.", () => {
|
||||||
|
//Arrange
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const testValue = wrapper.vm.ScheduleDate;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(testValue).toEqual("2023-01-01");
|
||||||
|
});
|
||||||
|
test("ScheduleStartTime should return time in expected format.", () => {
|
||||||
|
//Arrange
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const testValue = wrapper.vm.ScheduleStartTime;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(testValue).toEqual("09:00");
|
||||||
|
});
|
||||||
|
test("ScheduleEndTime should return time in expected format.", () => {
|
||||||
|
//Arrange
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const testValue = wrapper.vm.ScheduleEndTime;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(testValue).toEqual("10:00");
|
||||||
|
});
|
||||||
|
test("InShopWordingText should return text in expected format.", () => {
|
||||||
|
//Arrange
|
||||||
|
wordingText = "InShopWordingText format";
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const testValue = wrapper.vm.InShopWordingText;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(testValue).toEqual(wordingText);
|
||||||
|
});
|
||||||
|
test("MobileWordingText should return text in expected format.", () => {
|
||||||
|
//Arrange
|
||||||
|
wordingText = "MobileWordingText format";
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const testValue = wrapper.vm.MobileWordingText;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(testValue).toEqual(wordingText);
|
||||||
|
});
|
||||||
|
test("DropOffWordingText should return text in expected format.", () => {
|
||||||
|
//Arrange
|
||||||
|
wordingText = "DropOffWordingText format";
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const testValue = wrapper.vm.DropOffWordingText;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(testValue).toEqual(wordingText);
|
||||||
|
});
|
||||||
|
test("ServiceLocationFullAddress should return text in expected format.", () => {
|
||||||
|
//Arrange
|
||||||
|
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const testValue = wrapper.vm.ServiceLocationFullAddress;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(testValue).toEqual("123, test,<br/> test, AZ 12345");
|
||||||
|
});
|
||||||
|
test("ProviderFullAddress should return text in expected format.", () => {
|
||||||
|
//Arrange
|
||||||
|
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const testValue = wrapper.vm.ProviderFullAddress;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(testValue).toEqual("test1,<br/> test, AZ 12345");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
const wordingText = "wording Text";
|
|
||||||
function setupMocks({ customMountOptions }) {
|
function setupMocks({ customMountOptions }) {
|
||||||
const mountOptions = getMountOptions({
|
const mountOptions = getMountOptions({
|
||||||
...customMountOptions,
|
...customMountOptions,
|
||||||
|
|
|
||||||
|
|
@ -1,65 +1,61 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="page-container-grouped-styles position-relative">
|
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm">
|
||||||
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
|
<div class="page-container-grouped-styles position-relative">
|
||||||
<div>{{ workOrderNumber }}</div>
|
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
|
||||||
<div>
|
<div>
|
||||||
<div class="container-fluid pb-2">
|
<div class="container-fluid pb-2">
|
||||||
<div class="row justify-content-md-center">
|
<div class="row justify-content-md-center">
|
||||||
<div class="col-md-6 col-xl-4">
|
<div class="col-md-6 col-xl-4">
|
||||||
<div class="header-container">
|
<div class="header-container">
|
||||||
<img :src="ScheduleConfirmationImage" />
|
<img :src="ScheduleConfirmationImage" />
|
||||||
<span v-html="ScheduleConfirmationText"></span>
|
<span v-html="ScheduleConfirmationText"></span>
|
||||||
</div>
|
|
||||||
<div class="main">
|
|
||||||
<div>
|
|
||||||
<vehicleBanner
|
|
||||||
cmsWidgetName="VehicleBannerWidget"
|
|
||||||
:displayGenericVehicleImage="false" />
|
|
||||||
</div>
|
|
||||||
<div class="scheduleText">
|
|
||||||
<p>{{ this.ScheduleDateFormatted }}</p>
|
|
||||||
<p>{{ this.ScheduleTimeFormatted }}</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="appoinmenttext"
|
||||||
|
v-html="this.getAppointmentWordingText"></div>
|
||||||
|
|
||||||
<div class="calendar-section">
|
<div class="main">
|
||||||
<div class="add-to-calendar">
|
<div>
|
||||||
<button>
|
<vehicleBanner
|
||||||
<span class="add-to-calendar-span">
|
cmsWidgetName="VehicleBannerWidget"
|
||||||
<img
|
:displayGenericVehicleImage="false" />
|
||||||
src="~@/assets/img/icons/add-to-calendar.svg"
|
|
||||||
class="add-to-calendar-img" />
|
|
||||||
<span class="add-to-calendar-text"
|
|
||||||
>Add to calendar</span
|
|
||||||
></span
|
|
||||||
>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="scheduleText">
|
||||||
|
<p>{{ this.ScheduleDateFormatted }}</p>
|
||||||
|
<p>{{ this.ScheduleTimeFormatted }}</p>
|
||||||
|
</div>
|
||||||
|
<addToCalendar></addToCalendar>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="appoinmenttext"
|
||||||
|
v-html="this.AppointmentWordingText"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="appoinmenttext" v-html="this.AppointmentWordingText"></div>
|
<div class="emailtext" v-html="this.ConfirmationEmailText"></div>
|
||||||
|
<navbar
|
||||||
|
cmsWidgetName="FunnelFooterWidget"
|
||||||
|
ref="navbar"
|
||||||
|
isBackButtonHidden="true"
|
||||||
|
@ForwardClicked="forwardButtonAction"
|
||||||
|
:buttonSize="true" />
|
||||||
</div>
|
</div>
|
||||||
<div class="emailtext" v-html="this.ConfirmationEmailText"></div>
|
|
||||||
<navbar
|
|
||||||
cmsWidgetName="FunnelFooterWidget"
|
|
||||||
ref="navbar"
|
|
||||||
isBackButtonHidden="true"
|
|
||||||
@ForwardClicked="forwardButtonAction"
|
|
||||||
:buttonSize="true" />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Form>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
// Components
|
// Components
|
||||||
import funnelHeader from "@/fmg-components/funnel-header/funnel-header";
|
import funnelHeader from "@/fmg-components/funnel-header/funnel-header";
|
||||||
import vehicleBanner from "@/fmg-components/vehicle-banner/vehicle-banner";
|
import vehicleBanner from "@/fmg-components/vehicle-banner/vehicle-banner";
|
||||||
import navbar from "@/fmg-components/nav-bar/nav-bar";
|
import navbar from "@/fmg-components/nav-bar/nav-bar";
|
||||||
|
import addToCalendar from "@/layouts/add-to-calendar/add-to-calendar";
|
||||||
//Supporting files
|
//Supporting files
|
||||||
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||||
import { AppointmentTypeStrings } from "@/constants/schedule-constants";
|
import { AppointmentTypeStrings } from "@/constants/schedule-constants";
|
||||||
import { settleAllPromises } from "@/helpers/layout-helper";
|
import { settleAllPromises } from "@/helpers/layout-helper";
|
||||||
import { applicationConfig } from "@/constants/application-config.js";
|
import { applicationConfig } from "@/constants/application-config.js";
|
||||||
|
import { Form } from "vee-validate";
|
||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
|
@ -236,8 +232,10 @@ export default {
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
funnelHeader,
|
funnelHeader,
|
||||||
|
Form,
|
||||||
navbar,
|
navbar,
|
||||||
vehicleBanner,
|
vehicleBanner,
|
||||||
|
addToCalendar,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row justify-content-md-center">
|
<div class="row justify-content-md-center">
|
||||||
<div class="col-md-6 col-xl-4 col-xxl-3">
|
<div class="col-md-6 col-xl-4">
|
||||||
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" class="my-5" />
|
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" class="my-5" />
|
||||||
<textboxQuestion
|
<textboxQuestion
|
||||||
class="mb-4"
|
class="mb-4"
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row justify-content-md-center">
|
<div class="row justify-content-md-center">
|
||||||
<div class="col-md-6 col-xl-4 col-xxl-3">
|
<div class="col-md-6 col-xl-4">
|
||||||
<vehicleBanner cmsWidgetName="VehicleBannerWidget" />
|
<vehicleBanner cmsWidgetName="VehicleBannerWidget" />
|
||||||
<funnelSubHeader class="pb-4" cmsWidgetName="FunnelSubHeaderWidget" />
|
<funnelSubHeader class="pb-4" cmsWidgetName="FunnelSubHeaderWidget" />
|
||||||
<div v-if="!skipVin">
|
<div v-if="!skipVin">
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,104 @@
|
||||||
|
import { shallowMount } from "@vue/test-utils";
|
||||||
|
import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
||||||
|
|
||||||
|
import paymentMethodListButton from "@/layouts/payment-method/payment-method-question/payment-method-list-button/payment-method-list-button";
|
||||||
|
|
||||||
|
const testConstants = {
|
||||||
|
images: {
|
||||||
|
A: "imageA",
|
||||||
|
B: "imageB",
|
||||||
|
NONE: null,
|
||||||
|
},
|
||||||
|
names: {
|
||||||
|
A: "nameA",
|
||||||
|
B: "nameB",
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
withInline: "Button text with {custom:inlineImage} inline.",
|
||||||
|
noInline: "Button text with no inline",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let cmsContent;
|
||||||
|
|
||||||
|
describe("Payment Method Question", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
cmsContent = {};
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Side image", () => {
|
||||||
|
it("Renders side image if image is present and not inline", () => {
|
||||||
|
// Arrange
|
||||||
|
const props = generateDefaultProps();
|
||||||
|
const { wrapper } = setupMocks({
|
||||||
|
propsData: props,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const showSideImage = wrapper.vm.shouldDisplaySideImage;
|
||||||
|
|
||||||
|
expect(showSideImage).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Does not render side image if no image is present", () => {
|
||||||
|
// Arrange
|
||||||
|
let props = generateDefaultProps();
|
||||||
|
|
||||||
|
props.buttonImage = testConstants.images.NONE;
|
||||||
|
|
||||||
|
const { wrapper } = setupMocks({
|
||||||
|
propsData: props,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const showSideImage = wrapper.vm.shouldDisplaySideImage;
|
||||||
|
|
||||||
|
expect(showSideImage).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Does not render side image if inline", () => {
|
||||||
|
// Arrange
|
||||||
|
let props = generateDefaultProps();
|
||||||
|
|
||||||
|
props.buttonLabel = testConstants.content.withInline;
|
||||||
|
props.altText = testConstants.content.withInline;
|
||||||
|
|
||||||
|
const { wrapper } = setupMocks({
|
||||||
|
propsData: props,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const showSideImage = wrapper.vm.shouldDisplaySideImage;
|
||||||
|
|
||||||
|
expect(showSideImage).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function generateDefaultProps() {
|
||||||
|
return {
|
||||||
|
buttonLabel: testConstants.noInline,
|
||||||
|
altText: testConstants.noInline,
|
||||||
|
groupName: "payment-method",
|
||||||
|
value: testConstants.names.A,
|
||||||
|
buttonImage: testConstants.images.A,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupMocks(customMountOptions) {
|
||||||
|
const mountOptions = getMountOptions(customMountOptions);
|
||||||
|
|
||||||
|
const mockMixin = {
|
||||||
|
methods: {
|
||||||
|
getCmsContent: jest.fn((widgetName, cmsFieldName) => {
|
||||||
|
return cmsContent?.[widgetName]?.[cmsFieldName] ?? "";
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
mountOptions.global.mixins = [mockMixin];
|
||||||
|
|
||||||
|
const wrapper = shallowMount(paymentMethodListButton, mountOptions);
|
||||||
|
wrapper.vm.setCmsContent = jest.fn();
|
||||||
|
return { wrapper };
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,123 @@
|
||||||
|
<template>
|
||||||
|
<baseInputButton
|
||||||
|
v-bind="$props"
|
||||||
|
buttonWrapperClasses="list-group base-input-button list-button rounded-3 d-flex flex-column w-100 mb-2"
|
||||||
|
v-model="selectedValue">
|
||||||
|
<div class="button-content list-button-content d-flex flex-row py-3 px-4">
|
||||||
|
<img
|
||||||
|
v-if="shouldDisplaySideImage"
|
||||||
|
:id="buttonImageId"
|
||||||
|
class="ms-auto order-3"
|
||||||
|
:src="buttonImage"
|
||||||
|
:alt="altText" />
|
||||||
|
<div class="order-2">
|
||||||
|
<p class="m-0">
|
||||||
|
<span v-for="token in tokens" :key="token">
|
||||||
|
<span v-if="isInlineImageToken(token)">
|
||||||
|
<img
|
||||||
|
v-if="hasImage"
|
||||||
|
:src="buttonImage"
|
||||||
|
:alt="getInlineAltText(token)" />
|
||||||
|
<span v-else> {{ getInlineAltText(token) }}</span>
|
||||||
|
</span>
|
||||||
|
<span v-else>
|
||||||
|
{{ token }}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</baseInputButton>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import baseInputButton from "@/digital-components/base-input-button/base-input-button";
|
||||||
|
import inputButtonWrapperMixin from "@/mixins/input-button-wrapper-mixin";
|
||||||
|
import { splitCopyOnCMSPlaceHolder } from "@/helpers/cms-content-helper";
|
||||||
|
|
||||||
|
const INLINE_IMAGE_TOKEN = "custom:inlineImage";
|
||||||
|
|
||||||
|
function isInlineImageToken(token) {
|
||||||
|
return token.includes(INLINE_IMAGE_TOKEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getInlineAltText(token) {
|
||||||
|
let innerTokens = token.split(",");
|
||||||
|
|
||||||
|
return innerTokens[1] ?? "";
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "payment-method-list-button",
|
||||||
|
mixins: [inputButtonWrapperMixin],
|
||||||
|
components: {
|
||||||
|
baseInputButton,
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
isInlineImageToken,
|
||||||
|
getInlineAltText,
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
tokens() {
|
||||||
|
return splitCopyOnCMSPlaceHolder(this.buttonLabel ?? "");
|
||||||
|
},
|
||||||
|
hasImage() {
|
||||||
|
return !!this.buttonImage;
|
||||||
|
},
|
||||||
|
shouldDisplaySideImage() {
|
||||||
|
return this.hasImage && this.tokens.every((token) => !isInlineImageToken(token));
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.list-button {
|
||||||
|
outline: none;
|
||||||
|
input[type="radio"],
|
||||||
|
input[type="checkbox"] {
|
||||||
|
position: static; //override bootstrap
|
||||||
|
|
||||||
|
&:focus-visible + .list-button-content {
|
||||||
|
box-shadow: 0 0 0 2.5px $blue;
|
||||||
|
}
|
||||||
|
&:focus + .list-button-content {
|
||||||
|
box-shadow: 0 0 0 2.5px $blue;
|
||||||
|
}
|
||||||
|
&:checked + .list-button-content {
|
||||||
|
color: $black;
|
||||||
|
font-weight: 500;
|
||||||
|
background: $blue-100;
|
||||||
|
box-shadow: 0 0 0 1px $blue;
|
||||||
|
}
|
||||||
|
&:checked:focus + .list-button-content {
|
||||||
|
box-shadow: 0 0 0 2.5px $blue;
|
||||||
|
}
|
||||||
|
&:checked + .list-button-content p,
|
||||||
|
&:checked + .list-button-content span {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
&:checked + .list-button-content span:nth-child(2) {
|
||||||
|
font-weight: 400;
|
||||||
|
color: $gray-600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.list-button-content {
|
||||||
|
color: $gray-600;
|
||||||
|
position: relative;
|
||||||
|
background: $white;
|
||||||
|
transition: all 150ms linear;
|
||||||
|
border-radius: $border-radius-lg;
|
||||||
|
border: 1px solid $gray-500;
|
||||||
|
width: 100%;
|
||||||
|
outline: none;
|
||||||
|
|
||||||
|
span {
|
||||||
|
&.small {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: $gray-550;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,130 @@
|
||||||
|
import { shallowMount } from "@vue/test-utils";
|
||||||
|
import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
||||||
|
|
||||||
|
import paymentMethodQuestion from "@/layouts/payment-method/payment-method-question/payment-method-question";
|
||||||
|
|
||||||
|
const testConstants = {
|
||||||
|
cms: {
|
||||||
|
question: {
|
||||||
|
text: "Choose a payment option",
|
||||||
|
},
|
||||||
|
answers: {
|
||||||
|
optionA: {
|
||||||
|
name: "NameA",
|
||||||
|
text: "TextA",
|
||||||
|
subText: "SubTextA",
|
||||||
|
imageId: "idA",
|
||||||
|
image: "imageA",
|
||||||
|
subWidget: "subWidgetA",
|
||||||
|
},
|
||||||
|
optionB: {
|
||||||
|
name: "NameB",
|
||||||
|
text: "TextB",
|
||||||
|
subText: "SubTextB",
|
||||||
|
imageId: "idB",
|
||||||
|
image: "imageB",
|
||||||
|
subWidget: "subWidgetB",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let cmsContent;
|
||||||
|
|
||||||
|
describe("Payment Method Question", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
cmsContent = {
|
||||||
|
PaymentMethodWidget: {
|
||||||
|
QuestionText: testConstants.cms.question,
|
||||||
|
Answers: [
|
||||||
|
{
|
||||||
|
Name: testConstants.cms.answers.optionA.name,
|
||||||
|
Text: testConstants.cms.answers.optionA.text,
|
||||||
|
SubText: testConstants.cms.answers.optionA.subText,
|
||||||
|
ImageId: testConstants.cms.answers.optionA.imageId,
|
||||||
|
AnswerImageUrl: testConstants.cms.answers.optionA.image,
|
||||||
|
SubWidgetName: testConstants.cms.answers.optionA.subWidget,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: testConstants.cms.answers.optionB.name,
|
||||||
|
Text: testConstants.cms.answers.optionB.text,
|
||||||
|
SubText: testConstants.cms.answers.optionB.subText,
|
||||||
|
ImageId: testConstants.cms.answers.optionB.imageId,
|
||||||
|
AnswerImageUrl: testConstants.cms.answers.optionB.image,
|
||||||
|
SubWidgetName: testConstants.cms.answers.optionB.subWidget,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should map cms content correctly", () => {
|
||||||
|
// Arrange
|
||||||
|
const props = generateDefaultProps();
|
||||||
|
|
||||||
|
const { wrapper } = setupMocks({
|
||||||
|
propsData: props,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const mappedContent = wrapper.vm.paymentMethodAnswerData;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(mappedContent).toEqual([
|
||||||
|
{
|
||||||
|
buttonLabel: testConstants.cms.answers.optionA.text,
|
||||||
|
altText: testConstants.cms.answers.optionA.text,
|
||||||
|
groupName: "payment-method",
|
||||||
|
value: testConstants.cms.answers.optionA.name,
|
||||||
|
buttonImage: testConstants.cms.answers.optionA.image,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
buttonLabel: testConstants.cms.answers.optionB.text,
|
||||||
|
altText: testConstants.cms.answers.optionB.text,
|
||||||
|
groupName: "payment-method",
|
||||||
|
value: testConstants.cms.answers.optionB.name,
|
||||||
|
buttonImage: testConstants.cms.answers.optionB.image,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Handles null cms content gracefully", () => {
|
||||||
|
// Arrange
|
||||||
|
cmsContent = {};
|
||||||
|
const props = generateDefaultProps();
|
||||||
|
|
||||||
|
const { wrapper } = setupMocks({
|
||||||
|
propsData: props,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const mappedContent = wrapper.vm.paymentMethodAnswerData;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(mappedContent).toEqual([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function generateDefaultProps() {
|
||||||
|
return {
|
||||||
|
modelValue: "modelValue",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupMocks(customMountOptions) {
|
||||||
|
const mountOptions = getMountOptions(customMountOptions);
|
||||||
|
|
||||||
|
const mockMixin = {
|
||||||
|
methods: {
|
||||||
|
getCmsContent: jest.fn((widgetName, cmsFieldName) => {
|
||||||
|
return cmsContent?.[widgetName]?.[cmsFieldName] ?? "";
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
mountOptions.global.mixins = [mockMixin];
|
||||||
|
|
||||||
|
const wrapper = shallowMount(paymentMethodQuestion, mountOptions);
|
||||||
|
wrapper.vm.setCmsContent = jest.fn();
|
||||||
|
return { wrapper };
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,80 @@
|
||||||
|
<template>
|
||||||
|
<div class="payment-method-question">
|
||||||
|
<buttonQuestion
|
||||||
|
groupName="payment-method"
|
||||||
|
buttonTypeString="payment-method-list-button"
|
||||||
|
:buttonTypeObject="paymentMethodListButton"
|
||||||
|
isWide
|
||||||
|
:questionText="paymentMethodQuestionText"
|
||||||
|
:answers="paymentMethodAnswerData"
|
||||||
|
isRequired
|
||||||
|
v-model="selectedMethod"
|
||||||
|
textPosition="text-start" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import buttonQuestion from "@/digital-components/button-question/button-question";
|
||||||
|
import paymentMethodListButton from "./payment-method-list-button/payment-method-list-button";
|
||||||
|
import { paymentMethods, paymentTimes } from "@/constants/payment-method-constants";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "payment-method-question",
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
paymentMethodListButton: paymentMethodListButton,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
modelValue: String,
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getAnswersNullSafe(widgetName) {
|
||||||
|
const rawData = this.getCmsContent(widgetName, "Answers");
|
||||||
|
|
||||||
|
if (!rawData) {
|
||||||
|
return [];
|
||||||
|
} else {
|
||||||
|
return rawData;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
selectedMethod: {
|
||||||
|
get: function () {
|
||||||
|
return this.modelValue;
|
||||||
|
},
|
||||||
|
set: function (selectedMethod) {
|
||||||
|
this.$emit("update:modelValue", selectedMethod);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
paymentMethodQuestionText() {
|
||||||
|
return this.getCmsContent("PaymentMethodWidget", "QuestionText");
|
||||||
|
},
|
||||||
|
paymentMethodAnswerData() {
|
||||||
|
const cmsData = this.getAnswersNullSafe("PaymentMethodWidget");
|
||||||
|
|
||||||
|
return cmsData.map((answer) => {
|
||||||
|
return {
|
||||||
|
buttonLabel: answer.Text,
|
||||||
|
altText: answer.Text,
|
||||||
|
groupName: "payment-method",
|
||||||
|
value: answer.Name,
|
||||||
|
buttonImage: answer.AnswerImageUrl,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
buttonQuestion,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.payment-method-question {
|
||||||
|
.question-text span {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -5,6 +5,7 @@ import customerDetails from "@/layouts/payment-method/payment-method.vue";
|
||||||
import { shallowMount } from "@vue/test-utils";
|
import { shallowMount } from "@vue/test-utils";
|
||||||
import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
|
import { experimentSettings } from "@/constants/experiments";
|
||||||
|
|
||||||
// Mock our module for promises.
|
// Mock our module for promises.
|
||||||
jest.mock("@/helpers/layout-helper.js", () => ({
|
jest.mock("@/helpers/layout-helper.js", () => ({
|
||||||
|
|
@ -15,6 +16,8 @@ jest.mock("@/helpers/heritage-integration/order-helper.js", () => ({
|
||||||
submitWorkOrder: jest.fn(),
|
submitWorkOrder: jest.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
let piaDisabledFlag = false;
|
||||||
|
|
||||||
describe("payment-method.vue", () => {
|
describe("payment-method.vue", () => {
|
||||||
describe("navigation", () => {
|
describe("navigation", () => {
|
||||||
test("if the back button is clicked, navigate back", async () => {
|
test("if the back button is clicked, navigate back", async () => {
|
||||||
|
|
@ -52,19 +55,37 @@ function setupMocks() {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const wrapper = shallowMount(
|
const mountOptions = getMountOptions({
|
||||||
customerDetails,
|
router: {
|
||||||
getMountOptions({
|
navigate: jest.fn(),
|
||||||
router: {
|
navigateWithSaving: jest.fn(),
|
||||||
navigate: jest.fn(),
|
navigateWithoutSaving: jest.fn(),
|
||||||
navigateWithSaving: jest.fn(),
|
},
|
||||||
navigateWithoutSaving: jest.fn(),
|
store: {
|
||||||
},
|
getters: store.getters,
|
||||||
store: {
|
},
|
||||||
getters: store.getters,
|
});
|
||||||
},
|
|
||||||
})
|
const mockMixin = {
|
||||||
);
|
methods: {
|
||||||
|
getSettingValue: jest.fn((settingName) => {
|
||||||
|
if (!piaDisabledFlag) {
|
||||||
|
if (settingName === experimentSettings.PIA_EXPERIENCE) {
|
||||||
|
return "PIA Optional";
|
||||||
|
}
|
||||||
|
if (settingName === experimentSettings.SUBMIT_ORDER_ENABLE_PIA) {
|
||||||
|
return "true";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "false";
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
mountOptions.global.mixins = [mockMixin];
|
||||||
|
|
||||||
|
const wrapper = shallowMount(customerDetails, mountOptions);
|
||||||
|
|
||||||
return { wrapper };
|
return { wrapper };
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row justify-content-md-center">
|
<div class="row justify-content-md-center">
|
||||||
<div class="col-md-6 col-xl-4 col-xxl-3">
|
<div class="col-md-6 col-xl-4">
|
||||||
<vehicleBanner
|
<vehicleBanner
|
||||||
cmsWidgetName="VehicleBannerWidget"
|
cmsWidgetName="VehicleBannerWidget"
|
||||||
:displayGenericVehicleImage="false" />
|
:displayGenericVehicleImage="false" />
|
||||||
|
|
@ -17,8 +17,8 @@
|
||||||
|
|
||||||
<cart
|
<cart
|
||||||
:damage="damageInfo"
|
:damage="damageInfo"
|
||||||
:storeLineItems="storeLineItems"
|
:availableLineItems="availableLineItems"
|
||||||
:availableLineItems="cartItems"
|
:lineItems="lineItems"
|
||||||
servicePackageOptionsCmsName="ServicePackageTitle"
|
servicePackageOptionsCmsName="ServicePackageTitle"
|
||||||
@cart-remove="cartRemove" />
|
@cart-remove="cartRemove" />
|
||||||
|
|
||||||
|
|
@ -34,9 +34,9 @@
|
||||||
|
|
||||||
<add-vaps-modal-buttons
|
<add-vaps-modal-buttons
|
||||||
:cartItems="cartItems"
|
:cartItems="cartItems"
|
||||||
:wiperItems="availableWipers"
|
:availableLineItems="availableLineItems"
|
||||||
:rainDefenseItem="availableRainDefense"
|
vapsTilesCmsName="VapsProductTiles"
|
||||||
vapsTilesCmsName="VapsProductTiles" />
|
v-on="{ 'buttonEvent.openModal': openModal }" />
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<button v-on:click="ccdPayment">
|
<button v-on:click="ccdPayment">
|
||||||
|
|
@ -76,8 +76,15 @@
|
||||||
alt="Afterpay" />
|
alt="Afterpay" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<button @click="openModal('RainDefenseModal')">OPEN MODAL</button>
|
||||||
|
<contentGroupModal ref="RainDefenseModal" cmsWidgetName="RainDefenseModal" />
|
||||||
|
<paymentMethodQuestion
|
||||||
|
v-if="!isPiaDisabled"
|
||||||
|
v-model="paymentMethodInternalModel" />
|
||||||
|
|
||||||
<navbar
|
<navbar
|
||||||
cmsWidgetName="FunnelFooterWidget"
|
cmsWidgetName="FunnelFooterWidget"
|
||||||
|
ref="navbar"
|
||||||
:isForwardActionDisabled="!meta.valid"
|
:isForwardActionDisabled="!meta.valid"
|
||||||
:isBackButtonHidden="shouldHideBackButton"
|
:isBackButtonHidden="shouldHideBackButton"
|
||||||
@back-clicked="backButtonAction"
|
@back-clicked="backButtonAction"
|
||||||
|
|
@ -93,9 +100,11 @@
|
||||||
import funnelHeader from "@/fmg-components/funnel-header/funnel-header";
|
import funnelHeader from "@/fmg-components/funnel-header/funnel-header";
|
||||||
import navbar from "@/fmg-components/nav-bar/nav-bar";
|
import navbar from "@/fmg-components/nav-bar/nav-bar";
|
||||||
import funnelSubHeader from "@/fmg-components/funnel-sub-header/funnel-sub-header";
|
import funnelSubHeader from "@/fmg-components/funnel-sub-header/funnel-sub-header";
|
||||||
|
import paymentMethodQuestion from "@/layouts/payment-method/payment-method-question/payment-method-question";
|
||||||
import loadingModal from "@/fmg-components/loading-modal/loading-modal.vue";
|
import loadingModal from "@/fmg-components/loading-modal/loading-modal.vue";
|
||||||
import cart from "@/fmg-components/cart/cart";
|
import cart from "@/fmg-components/cart/cart";
|
||||||
import addVapsModalButtons from "@/fmg-components/add-vaps-modal-buttons/add-vaps-modal-buttons";
|
import addVapsModalButtons from "@/fmg-components/add-vaps-modal-buttons/add-vaps-modal-buttons";
|
||||||
|
import contentGroupModal from "@/fmg-components/content-group-modal/content-group-modal";
|
||||||
import { submitWorkOrder } from "@/helpers/heritage-integration/order-helper.js";
|
import { submitWorkOrder } from "@/helpers/heritage-integration/order-helper.js";
|
||||||
import alert from "@/ux-components/alert/alert";
|
import alert from "@/ux-components/alert/alert";
|
||||||
|
|
||||||
|
|
@ -104,7 +113,11 @@ import { settleAllPromises } from "@/helpers/layout-helper";
|
||||||
import { storeActions } from "@/constants/store-actions";
|
import { storeActions } from "@/constants/store-actions";
|
||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||||
|
import { paymentMethods } from "@/constants/payment-method-constants";
|
||||||
|
import { experimentSettings } from "@/constants/experiments";
|
||||||
|
|
||||||
import { Form } from "vee-validate";
|
import { Form } from "vee-validate";
|
||||||
|
import { AppointmentTypeStrings } from "@/constants/schedule-constants";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "paymentMethod",
|
name: "paymentMethod",
|
||||||
|
|
@ -116,11 +129,14 @@ export default {
|
||||||
null,
|
null,
|
||||||
"payment-method"
|
"payment-method"
|
||||||
);
|
);
|
||||||
|
|
||||||
const rainDefensePromise = baseMixin.methods.dispatchStoreActionWithLogging(
|
const rainDefensePromise = baseMixin.methods.dispatchStoreActionWithLogging(
|
||||||
storeActions.GET_RAIN_DEFENSE,
|
storeActions.GET_RAIN_DEFENSE,
|
||||||
null,
|
null,
|
||||||
"payment-method"
|
"payment-method"
|
||||||
);
|
);
|
||||||
|
console.log("rainDefensePromise: ", rainDefensePromise);
|
||||||
|
|
||||||
const supportingItemsPromise = baseMixin.methods.dispatchStoreActionWithLogging(
|
const supportingItemsPromise = baseMixin.methods.dispatchStoreActionWithLogging(
|
||||||
storeActions.GET_SUPPORTING_ITEMS,
|
storeActions.GET_SUPPORTING_ITEMS,
|
||||||
null,
|
null,
|
||||||
|
|
@ -147,16 +163,17 @@ export default {
|
||||||
];
|
];
|
||||||
|
|
||||||
const resultMap = await settleAllPromises(promiseResultMap);
|
const resultMap = await settleAllPromises(promiseResultMap);
|
||||||
const clonedGlassParts = store.getters.order.lineItems.glassParts
|
|
||||||
? JSON.parse(JSON.stringify(store.getters.order.lineItems.glassParts))
|
const glassParts = (await store.getters.order.lineItems.glassParts) ?? [];
|
||||||
: [];
|
|
||||||
const availableLineItems = [
|
const availableLineItems = [
|
||||||
resultMap.rainDefense,
|
resultMap.rainDefense,
|
||||||
...resultMap.supportingItems,
|
...resultMap.supportingItems,
|
||||||
...resultMap.wipers,
|
...resultMap.wipers,
|
||||||
...clonedGlassParts,
|
...glassParts,
|
||||||
];
|
];
|
||||||
const pricingResults = await baseMixin.methods.dispatchStoreActionWithLogging(
|
|
||||||
|
const pricedAvailableLineItems = await baseMixin.methods.dispatchStoreActionWithLogging(
|
||||||
storeActions.PRICE_ORDER_ITEMS_AND_SAVE_SERVER_DATA,
|
storeActions.PRICE_ORDER_ITEMS_AND_SAVE_SERVER_DATA,
|
||||||
{
|
{
|
||||||
availableLineItems: availableLineItems,
|
availableLineItems: availableLineItems,
|
||||||
|
|
@ -164,36 +181,84 @@ export default {
|
||||||
"payment-method",
|
"payment-method",
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
const storeLineItems = store.getters.lineItems;
|
const lineItems = store.getters.lineItems;
|
||||||
const cartItems = [
|
|
||||||
...clonedGlassParts,
|
const cartItems = [...glassParts, ...lineItems.supportingItems, ...lineItems.vaps]; // TOBEREMOVED BY AJC IN CSR-1384
|
||||||
...storeLineItems.supportingItems,
|
|
||||||
...storeLineItems.vaps,
|
|
||||||
];
|
|
||||||
|
|
||||||
// Call the "next" function to complete the transition to this page.
|
// Call the "next" function to complete the transition to this page.
|
||||||
next((vm) => {
|
next((vm) => {
|
||||||
|
vm.cartItems = cartItems; // TOBEREMOVED BY AJC IN CSR-1384
|
||||||
|
|
||||||
vm.setCmsContent(resultMap.cmsContent);
|
vm.setCmsContent(resultMap.cmsContent);
|
||||||
vm.availableLineItems = pricingResults;
|
vm.availableLineItems = pricedAvailableLineItems;
|
||||||
vm.availableWipers = resultMap.wipers;
|
vm.lineItems = lineItems;
|
||||||
vm.availableRainDefense = resultMap.rainDefense;
|
|
||||||
vm.cartItems = cartItems;
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
cartItems: [],
|
cartItems: [], // TOBEREMOVED BY AJC IN CSR-1384
|
||||||
availableWipers: null,
|
|
||||||
availableRainDefense: null,
|
lineItems: [],
|
||||||
availableLineItems: null,
|
availableLineItems: [],
|
||||||
supportingItems: null,
|
|
||||||
pricedGlassParts: null,
|
|
||||||
displayPaypalAlert: this.getPaypalAlert(),
|
displayPaypalAlert: this.getPaypalAlert(),
|
||||||
|
paymentMethodInternalModel: null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
arePagePrerequisitesValid() {
|
arePagePrerequisitesValid() {
|
||||||
return true;
|
// Line Items
|
||||||
|
const packageReqs = !!store.getters.order.lineItems.supportingItems;
|
||||||
|
|
||||||
|
// Service Location
|
||||||
|
const serviceLocation = store.getters.order.serviceLocation;
|
||||||
|
const mobileReqs = !!(
|
||||||
|
serviceLocation.address &&
|
||||||
|
serviceLocation.city &&
|
||||||
|
serviceLocation.state &&
|
||||||
|
serviceLocation.zipCode
|
||||||
|
);
|
||||||
|
|
||||||
|
const providerLocation = serviceLocation.provider.address;
|
||||||
|
const dropOffInshopReqs = !!(
|
||||||
|
providerLocation.streetAddress &&
|
||||||
|
providerLocation.city &&
|
||||||
|
providerLocation.state &&
|
||||||
|
providerLocation.zipCode
|
||||||
|
);
|
||||||
|
|
||||||
|
const isMobile = serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE;
|
||||||
|
|
||||||
|
const serviceLocationReqs =
|
||||||
|
(isMobile && mobileReqs) || (!isMobile && dropOffInshopReqs);
|
||||||
|
|
||||||
|
// Insurance
|
||||||
|
const isInsuranceSet = store.getters.order.payment.isInsurance !== null;
|
||||||
|
|
||||||
|
// Schedule
|
||||||
|
const schedule = store.getters.order.schedule;
|
||||||
|
const scheduleReqs = !!(
|
||||||
|
schedule.date &&
|
||||||
|
schedule.startTime &&
|
||||||
|
(!isMobile || schedule.endTime) &&
|
||||||
|
schedule.jobMaxMinutes &&
|
||||||
|
schedule.jobMinMinutes
|
||||||
|
);
|
||||||
|
|
||||||
|
// Customer
|
||||||
|
const customer = store.getters.order.customer;
|
||||||
|
const customerReqs = !!(
|
||||||
|
customer.firstName &&
|
||||||
|
customer.lastName &&
|
||||||
|
customer.phoneNumber &&
|
||||||
|
customer.emailAddress
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
packageReqs && serviceLocationReqs && isInsuranceSet && scheduleReqs && customerReqs
|
||||||
|
);
|
||||||
|
},
|
||||||
|
openModal(modalName) {
|
||||||
|
this.$refs[modalName].openModal();
|
||||||
},
|
},
|
||||||
getPaypalAlert() {
|
getPaypalAlert() {
|
||||||
return store.getters.order.payment.piaErrorCode ? true : false;
|
return store.getters.order.payment.piaErrorCode ? true : false;
|
||||||
|
|
@ -209,7 +274,7 @@ export default {
|
||||||
const matchedIndices = [];
|
const matchedIndices = [];
|
||||||
this.cartItems.forEach((cartItem, i) => {
|
this.cartItems.forEach((cartItem, i) => {
|
||||||
if (cartItem.partType === item.partType) {
|
if (cartItem.partType === item.partType) {
|
||||||
console.log("adding this to matchedIndices: ", i);
|
// console.log("adding this to matchedIndices: ", i);
|
||||||
matchedIndices.push(i);
|
matchedIndices.push(i);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -231,32 +296,29 @@ export default {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
async ccdPayment() {
|
async ccdPayment() {
|
||||||
//leaving site, clear any save session promises that we will not be able to resolve when we return
|
this.piaSetup("cc");
|
||||||
this.$refs.loadingModal.showModal();
|
|
||||||
this.dispatchStoreAction(storeActions.SAVE_PIA_ERROR_CODE, null, false);
|
|
||||||
await this.dispatchStoreAction(storeActions.RESET_SAVE_SESSION_PROMISE);
|
|
||||||
await this.dispatchStoreAction(storeActions.SAVE_PAY_NOW_TYPE, "cc", false);
|
|
||||||
this.$router.navigateWithoutSaving(
|
|
||||||
this.navigationScenarios.CLICKED_PAY_NOW,
|
|
||||||
this.$route
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
async paypal() {
|
async paypal() {
|
||||||
//leaving site, clear any save session promises that we will not be able to resolve when we return
|
this.piaSetup("pp");
|
||||||
this.$refs.loadingModal.showModal();
|
|
||||||
this.dispatchStoreAction(storeActions.SAVE_PIA_ERROR_CODE, null, false);
|
|
||||||
await this.dispatchStoreAction(storeActions.RESET_SAVE_SESSION_PROMISE);
|
|
||||||
await this.dispatchStoreAction(storeActions.SAVE_PAY_NOW_TYPE, "pp", false);
|
|
||||||
this.$router.navigateWithoutSaving(
|
|
||||||
this.navigationScenarios.CLICKED_PAY_NOW,
|
|
||||||
this.$route
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
async afterpay() {
|
async afterpay() {
|
||||||
|
this.piaSetup("ap");
|
||||||
|
},
|
||||||
|
async piaSetup(payNowType) {
|
||||||
this.$refs.loadingModal.showModal();
|
this.$refs.loadingModal.showModal();
|
||||||
this.dispatchStoreAction(storeActions.SAVE_PIA_ERROR_CODE, null, false);
|
|
||||||
|
//leaving site, clear any save session promises that we will not be able to resolve when we return
|
||||||
await this.dispatchStoreAction(storeActions.RESET_SAVE_SESSION_PROMISE);
|
await this.dispatchStoreAction(storeActions.RESET_SAVE_SESSION_PROMISE);
|
||||||
await this.dispatchStoreAction(storeActions.SAVE_PAY_NOW_TYPE, "ap", false);
|
await this.dispatchStoreAction(storeActions.SAVE_PAY_NOW_TYPE, payNowType, false);
|
||||||
|
|
||||||
|
// make sure pia error codes are reset, and get a pia work order in delete status
|
||||||
|
this.dispatchStoreAction(storeActions.SAVE_PIA_ERROR_CODE, null, false);
|
||||||
|
this.dispatchStoreAction(storeActions.SAVE_PIA_WORK_ORDER, true);
|
||||||
|
|
||||||
|
if (!store.getters.order.workOrderNumber) {
|
||||||
|
await submitWorkOrder({ pageNameToLog: "payment-method" });
|
||||||
|
}
|
||||||
|
|
||||||
this.$router.navigateWithoutSaving(
|
this.$router.navigateWithoutSaving(
|
||||||
this.navigationScenarios.CLICKED_PAY_NOW,
|
this.navigationScenarios.CLICKED_PAY_NOW,
|
||||||
this.$route
|
this.$route
|
||||||
|
|
@ -265,11 +327,40 @@ export default {
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
damageInfo() {
|
damageInfo() {
|
||||||
|
// console.log("this.$store.getters.damage ", this.$store.getters.damage)
|
||||||
return this.$store.getters.damage;
|
return this.$store.getters.damage;
|
||||||
},
|
},
|
||||||
storeLineItems() {
|
customCtaCopy() {
|
||||||
// console.log("storeLineItems, aka lineItems from store: ", this.$store.getters.lineItems)
|
switch (this.paymentMethod) {
|
||||||
return this.$store.getters.lineItems;
|
case paymentMethods.CREDIT_CARD:
|
||||||
|
return "Continue to checkout";
|
||||||
|
case paymentMethods.PAYPAL:
|
||||||
|
return "Continue to Paypal";
|
||||||
|
case paymentMethods.AFTERPAY:
|
||||||
|
return "Continue to Afterpay";
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
isPiaDisabled() {
|
||||||
|
const piaExperience =
|
||||||
|
this.getSettingValue(experimentSettings.PIA_EXPERIENCE) === "PIA Optional";
|
||||||
|
const enablePIA =
|
||||||
|
this.getSettingValue(experimentSettings.SUBMIT_ORDER_ENABLE_PIA) === "true";
|
||||||
|
|
||||||
|
return !(piaExperience && enablePIA);
|
||||||
|
},
|
||||||
|
paymentMethod() {
|
||||||
|
if (this.isPiaDisabled) {
|
||||||
|
return paymentMethods.LATER;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.paymentMethodInternalModel;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
customCtaCopy(newValue) {
|
||||||
|
this.$refs.navbar.updateButtonText(newValue);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
|
|
@ -281,6 +372,8 @@ export default {
|
||||||
cart,
|
cart,
|
||||||
alert,
|
alert,
|
||||||
addVapsModalButtons,
|
addVapsModalButtons,
|
||||||
|
contentGroupModal,
|
||||||
|
paymentMethodQuestion,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="confirm" ref="confirm" @click="forwardButtonAction"></div>
|
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm">
|
||||||
|
<loadingModal notFullScreen ref="loadingModal" />
|
||||||
|
</Form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
@ -8,17 +10,20 @@ import { queryStrings } from "@/constants/query-strings";
|
||||||
import { getQuerystringParameter } from "@/helpers/querystring-helper";
|
import { getQuerystringParameter } from "@/helpers/querystring-helper";
|
||||||
import { storeActions } from "@/constants/store-actions";
|
import { storeActions } from "@/constants/store-actions";
|
||||||
import baseMixin from "@/mixins/base-mixin.js";
|
import baseMixin from "@/mixins/base-mixin.js";
|
||||||
|
import loadingModal from "@/fmg-components/loading-modal/loading-modal.vue";
|
||||||
|
import { Form } from "vee-validate";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "payment-pia-return",
|
name: "payment-pia-return",
|
||||||
async mounted() {
|
async mounted() {
|
||||||
|
this.$refs.loadingModal.showModal();
|
||||||
const piaError = getQuerystringParameter(queryStrings.ERROR);
|
const piaError = getQuerystringParameter(queryStrings.ERROR);
|
||||||
const token = getQuerystringParameter(queryStrings.TOKEN);
|
const token = getQuerystringParameter(queryStrings.TOKEN);
|
||||||
const payerId = getQuerystringParameter(queryStrings.PAYERID);
|
const payerId = getQuerystringParameter(queryStrings.PAYERID);
|
||||||
|
|
||||||
if (piaError) {
|
if (piaError) {
|
||||||
baseMixin.methods.dispatchStoreAction(
|
baseMixin.methods.dispatchStoreAction(
|
||||||
storeActions.SAVE_PAYPAL_ERROR_CODE,
|
storeActions.SAVE_PIA_ERROR_CODE,
|
||||||
piaError,
|
piaError,
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
@ -26,7 +31,7 @@ export default {
|
||||||
} else {
|
} else {
|
||||||
if (token && payerId) {
|
if (token && payerId) {
|
||||||
baseMixin.methods.dispatchStoreAction(
|
baseMixin.methods.dispatchStoreAction(
|
||||||
storeActions.SAVE_PAYPAL_ERROR_CODE,
|
storeActions.SAVE_PIA_ERROR_CODE,
|
||||||
null,
|
null,
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
@ -35,11 +40,18 @@ export default {
|
||||||
true,
|
true,
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Final work order submit after returning from PIA.
|
||||||
|
await this.dispatchStoreAction(storeActions.RESET_SAVE_SESSION_PROMISE);
|
||||||
await submitWorkOrder({ pageNameToLog: "payment-pia-return" });
|
await submitWorkOrder({ pageNameToLog: "payment-pia-return" });
|
||||||
this.$refs.confirm.click();
|
this.$refs.loadingModal.isModalVisible = false;
|
||||||
|
this.$router.navigateWithoutSaving(
|
||||||
|
this.navigationScenarios.PIA_SUCCESS,
|
||||||
|
this.$route
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
baseMixin.methods.dispatchStoreAction(
|
baseMixin.methods.dispatchStoreAction(
|
||||||
storeActions.SAVE_PAYPAL_ERROR_CODE,
|
storeActions.SAVE_PIA_ERROR_CODE,
|
||||||
piaError,
|
piaError,
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
@ -55,7 +67,10 @@ export default {
|
||||||
this.$router.navigateWithoutSaving(this.navigationScenarios.PIA_SUCCESS, this.$route);
|
this.$router.navigateWithoutSaving(this.navigationScenarios.PIA_SUCCESS, this.$route);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
components: {},
|
components: {
|
||||||
|
loadingModal,
|
||||||
|
Form,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,7 @@
|
||||||
|
|
||||||
<input type="hidden" name="sgtotalamount" :value="displayAmount" />
|
<input type="hidden" name="sgtotalamount" :value="displayAmount" />
|
||||||
<input type="hidden" name="totalAmountDecimal" :value="totalAmount" />
|
<input type="hidden" name="totalAmountDecimal" :value="totalAmount" />
|
||||||
<input type="hidden" name="lineItems" :value="lineItems" />
|
<input type="hidden" name="lineItems" :value="piaLineItems" />
|
||||||
|
|
||||||
<input type="hidden" name="sgdiscountstrikethruamount" value="" />
|
<input type="hidden" name="sgdiscountstrikethruamount" value="" />
|
||||||
|
|
||||||
|
|
@ -152,7 +152,7 @@
|
||||||
value="https://fixmyglasstest.safelite.com/FixMyGlass/Payment.aspx?error=ccTimeout" />
|
value="https://fixmyglasstest.safelite.com/FixMyGlass/Payment.aspx?error=ccTimeout" />
|
||||||
|
|
||||||
<input type="hidden" name="sgTransactionType" value="authorization" />
|
<input type="hidden" name="sgTransactionType" value="authorization" />
|
||||||
<input type="hidden" name="amount" :value="totalAmount" />
|
<input type="hidden" name="amount" :value="amountDue" />
|
||||||
<input type="hidden" name="ctu" :value="ctu" />
|
<input type="hidden" name="ctu" :value="ctu" />
|
||||||
<input type="hidden" name="orderNumber" :value="workOrderNumber" />
|
<input type="hidden" name="orderNumber" :value="workOrderNumber" />
|
||||||
|
|
||||||
|
|
@ -186,6 +186,7 @@ import { externalUrls } from "@/router/router-constants/externalUrl-values";
|
||||||
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||||
import { settleAllPromises } from "@/helpers/layout-helper";
|
import { settleAllPromises } from "@/helpers/layout-helper";
|
||||||
import { applicationConfig } from "@/constants/application-config";
|
import { applicationConfig } from "@/constants/application-config";
|
||||||
|
import baseMixin from "@/mixins/base-mixin.js";
|
||||||
|
|
||||||
// Validation
|
// Validation
|
||||||
import { Form } from "vee-validate";
|
import { Form } from "vee-validate";
|
||||||
|
|
@ -213,12 +214,11 @@ export default {
|
||||||
phoneNumber: store.getters.order.customer.phoneNumber,
|
phoneNumber: store.getters.order.customer.phoneNumber,
|
||||||
ctu: store.getters.order.serviceLocation.zipCodeCtu,
|
ctu: store.getters.order.serviceLocation.zipCodeCtu,
|
||||||
referralCorrelationId: store.getters.order.referralCorrelationId,
|
referralCorrelationId: store.getters.order.referralCorrelationId,
|
||||||
// TODO: replace these
|
workOrderNumber: this.getWOrkOrderNumber(),
|
||||||
workOrderNumber: Math.floor(Math.random() * 90000) + 10000,
|
invoiceNumber: this.getInvoiceNumber(),
|
||||||
invoiceNumber: Math.floor(Math.random() * 90000) + 10000,
|
totalAmount: this.getAmountDue(),
|
||||||
totalAmount: "321.00", // TODO: get from cart?
|
displayAmount: this.getDisplayAmountDue(),
|
||||||
displayAmount: "$321.00", // TODO: get from cart?
|
piaLineItems: this.getPiaLineItems(),
|
||||||
lineItems: "Labor|189.99|1||Repair supplies|7.99|1",
|
|
||||||
sgHeaderline1: this.getHeaderLine1(),
|
sgHeaderline1: this.getHeaderLine1(),
|
||||||
sgHeaderline2: this.getHeaderLine2(),
|
sgHeaderline2: this.getHeaderLine2(),
|
||||||
paypalPayment: this.isPaypal(),
|
paypalPayment: this.isPaypal(),
|
||||||
|
|
@ -249,6 +249,25 @@ export default {
|
||||||
// TODO
|
// TODO
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
getWOrkOrderNumber() {
|
||||||
|
if (store.getters.order.workOrderNumber) {
|
||||||
|
var items = store.getters.order.workOrderNumber.split("-");
|
||||||
|
return items[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// no work order, set pia error and return
|
||||||
|
this.dispatchStoreAction(storeActions.SAVE_PIA_ERROR_CODE, "NO WORK ORDER", false);
|
||||||
|
this.$router.navigateWithoutSaving(this.navigationScenarios.CLICKED_BACK, this.$route);
|
||||||
|
},
|
||||||
|
getInvoiceNumber() {
|
||||||
|
if (store.getters.order.workOrderNumber) {
|
||||||
|
return store.getters.order.workOrderNumber.replace("-", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
// no work order, set pia error and return
|
||||||
|
this.dispatchStoreAction(storeActions.SAVE_PIA_ERROR_CODE, "NO WORK ORDER", false);
|
||||||
|
this.$router.navigateWithoutSaving(this.navigationScenarios.CLICKED_BACK, this.$route);
|
||||||
|
},
|
||||||
getAddress1() {
|
getAddress1() {
|
||||||
return store.getters.order.serviceLocation.address
|
return store.getters.order.serviceLocation.address
|
||||||
? store.getters.order.serviceLocation.address
|
? store.getters.order.serviceLocation.address
|
||||||
|
|
@ -289,6 +308,27 @@ export default {
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
},
|
},
|
||||||
|
getPiaLineItems() {
|
||||||
|
const itemsClone = { ...store.getters.order.lineItems };
|
||||||
|
delete itemsClone.serverData;
|
||||||
|
|
||||||
|
var itemsForPia = "";
|
||||||
|
for (var propertyName in itemsClone) {
|
||||||
|
for (var item in itemsClone[propertyName]) {
|
||||||
|
var amount =
|
||||||
|
+itemsClone[propertyName][item].laborAmount +
|
||||||
|
+itemsClone[propertyName][item].sellingPrice;
|
||||||
|
itemsForPia += itemsClone[propertyName][item].partType + "|" + amount + "|1|";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return itemsForPia;
|
||||||
|
},
|
||||||
|
getAmountDue() {
|
||||||
|
return baseMixin.methods.getAmountDue(store.getters.order.lineItems);
|
||||||
|
},
|
||||||
|
getDisplayAmountDue() {
|
||||||
|
return baseMixin.methods.getDisplayAmountDue(store.getters.order.lineItems);
|
||||||
|
},
|
||||||
isPaypal() {
|
isPaypal() {
|
||||||
if (this.getPaymentType() == "pp") {
|
if (this.getPaymentType() == "pp") {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row justify-content-md-center">
|
<div class="row justify-content-md-center">
|
||||||
<div class="col-md-6 col-xl-4 col-xxl-3">
|
<div class="col-md-6 col-xl-4">
|
||||||
<vehicleBanner
|
<vehicleBanner
|
||||||
cmsWidgetName="VehicleBannerWidget"
|
cmsWidgetName="VehicleBannerWidget"
|
||||||
:displayGenericVehicleImage="false" />
|
:displayGenericVehicleImage="false" />
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row justify-content-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-md-6 col-xl-4 col-xxl-3">
|
<div class="col-md-6 col-xl-4">
|
||||||
<vehicleBanner
|
<vehicleBanner
|
||||||
cmsWidgetName="VehicleBannerWidget"
|
cmsWidgetName="VehicleBannerWidget"
|
||||||
:displayGenericVehicleImage="false" />
|
:displayGenericVehicleImage="false" />
|
||||||
|
|
@ -177,8 +177,7 @@ export default {
|
||||||
// damageReqs handles checking for damage, even though it is also required for this section.
|
// damageReqs handles checking for damage, even though it is also required for this section.
|
||||||
const packageReqs = !!(
|
const packageReqs = !!(
|
||||||
(damage.isRepair || lineItems.glassParts) &&
|
(damage.isRepair || lineItems.glassParts) &&
|
||||||
lineItems.supportingItems &&
|
lineItems.supportingItems
|
||||||
lineItems.vaps
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Service Location
|
// Service Location
|
||||||
|
|
@ -208,7 +207,7 @@ export default {
|
||||||
const scheduleReqs = !!(
|
const scheduleReqs = !!(
|
||||||
schedule.date &&
|
schedule.date &&
|
||||||
schedule.startTime &&
|
schedule.startTime &&
|
||||||
schedule.endTime &&
|
(!isMobile || schedule.endTime) &&
|
||||||
schedule.jobMaxMinutes &&
|
schedule.jobMaxMinutes &&
|
||||||
schedule.jobMinMinutes
|
schedule.jobMinMinutes
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row justify-content-md-center">
|
<div class="row justify-content-md-center">
|
||||||
<div class="col-md-6 col-xl-4 col-xxl-3">
|
<div class="col-md-6 col-xl-4">
|
||||||
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" class="mt-5" />
|
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" class="mt-5" />
|
||||||
<template v-if="ChangeShopLink.length">
|
<template v-if="ChangeShopLink.length">
|
||||||
<textBlock
|
<textBlock
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row justify-content-md-center appointment-type">
|
<div class="row justify-content-md-center appointment-type">
|
||||||
<div class="col-md-6 col-xxl-4">
|
<div class="col-md-6">
|
||||||
<appointmentTypeQuestion
|
<appointmentTypeQuestion
|
||||||
v-model="selectedAppointmentType"
|
v-model="selectedAppointmentType"
|
||||||
v-show="isAppointmentTypeDisplayed"
|
v-show="isAppointmentTypeDisplayed"
|
||||||
|
|
@ -74,7 +74,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row justify-content-md-center">
|
<div class="row justify-content-md-center">
|
||||||
<div class="col-md-6 col-xl-4 col-xxl-3">
|
<div class="col-md-6 col-xl-4">
|
||||||
<mobileLocationModalQuestions
|
<mobileLocationModalQuestions
|
||||||
customComponentId="mobileLocationQuestions"
|
customComponentId="mobileLocationQuestions"
|
||||||
v-if="selectedAppointmentType === 'Mobile'"
|
v-if="selectedAppointmentType === 'Mobile'"
|
||||||
|
|
@ -532,6 +532,20 @@ export default {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
.appointment-type-question {
|
||||||
|
.button-question {
|
||||||
|
.col {
|
||||||
|
padding: 0 0.5rem;
|
||||||
|
@include media-breakpoint-up(md) {
|
||||||
|
padding: 0 0.75rem;
|
||||||
|
}
|
||||||
|
@include media-breakpoint-up(xl) {
|
||||||
|
width: 33.3333333%;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
.question-text {
|
.question-text {
|
||||||
& > span {
|
& > span {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
|
||||||
|
|
@ -24,13 +24,16 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row justify-content-md-center">
|
<div class="row justify-content-md-center">
|
||||||
<div class="col-md-6 col-xl-4 col-xxl-3">
|
<div class="col-md-6">
|
||||||
<damageLocationQuestion
|
<damageLocationQuestion
|
||||||
ref="damageLocation"
|
ref="damageLocation"
|
||||||
cmsWidgetName="DamageLocationQuestion"
|
cmsWidgetName="DamageLocationQuestion"
|
||||||
v-model="selectedDamageLocations"
|
v-model="selectedDamageLocations"
|
||||||
groupName="DamageLocationQuestion" />
|
groupName="DamageLocationQuestion" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row justify-content-md-center">
|
||||||
|
<div class="col-md-6 col-xl-4">
|
||||||
<windshieldOptions
|
<windshieldOptions
|
||||||
class="windshield-options"
|
class="windshield-options"
|
||||||
ref="windshieldOptions"
|
ref="windshieldOptions"
|
||||||
|
|
@ -547,14 +550,10 @@ export default {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.vehicle-damage {
|
.replace-options-question {
|
||||||
.windshield-options {
|
.two-list-card-width {
|
||||||
@include media-breakpoint-up(md) {
|
width: 100%;
|
||||||
.col {
|
flex: 0 auto;
|
||||||
flex: 0 0 auto;
|
|
||||||
width: 33.33333333%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row justify-content-md-center">
|
<div class="row justify-content-md-center">
|
||||||
<div class="col-md-6 col-xl-4 col-xxl-3">
|
<div class="col-md-6 col-xl-4">
|
||||||
<div v-for="(item, i) in PartsOrQuestions" :key="i">
|
<div v-for="(item, i) in PartsOrQuestions" :key="i">
|
||||||
<!-- Render horizontal lines if there is multi-glass (aka if i > 0) -->
|
<!-- Render horizontal lines if there is multi-glass (aka if i > 0) -->
|
||||||
<hr v-if="i > 0" />
|
<hr v-if="i > 0" />
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row justify-content-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-md-6 col-xl-4 col-xxl-3">
|
<div class="col-md-6 col-xl-4">
|
||||||
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" class="siteSubHeader" />
|
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" class="siteSubHeader" />
|
||||||
<vehicleQuestion
|
<vehicleQuestion
|
||||||
ref="vehicleYearQuestion"
|
ref="vehicleYearQuestion"
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row justify-content-md-center">
|
<div class="row justify-content-md-center">
|
||||||
<div class="col-md-6 col-xl-4 col-xxl-3">
|
<div class="col-md-6 col-xl-4">
|
||||||
<vehicleBanner
|
<vehicleBanner
|
||||||
cmsWidgetName="VehicleBannerWidget"
|
cmsWidgetName="VehicleBannerWidget"
|
||||||
:displayGenericVehicleImage="false" />
|
:displayGenericVehicleImage="false" />
|
||||||
|
|
|
||||||
|
|
@ -115,6 +115,27 @@ export default {
|
||||||
getTotalLineItemPrice(lineItem) {
|
getTotalLineItemPrice(lineItem) {
|
||||||
return lineItem.kitPrice + lineItem.laborAmount + lineItem.sellingPrice;
|
return lineItem.kitPrice + lineItem.laborAmount + lineItem.sellingPrice;
|
||||||
},
|
},
|
||||||
|
getDisplayAmountDue(lineItems) {
|
||||||
|
return this.getAmountDue(lineItems).toLocaleString("en-US", {
|
||||||
|
style: "currency",
|
||||||
|
currency: "USD",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getAmountDue(lineItems) {
|
||||||
|
// TODO: verify tax is included in lineitems
|
||||||
|
// Price everything in lineitems except the server data string
|
||||||
|
var amountDue = 0;
|
||||||
|
const itemsClone = { ...lineItems };
|
||||||
|
delete itemsClone.serverData;
|
||||||
|
for (var propertyName in itemsClone) {
|
||||||
|
if (itemsClone[propertyName]) {
|
||||||
|
amountDue += this.getTotalPriceOfAllLineItemsAndChildParts(
|
||||||
|
itemsClone[propertyName]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return amountDue;
|
||||||
|
},
|
||||||
scrollToPageTop() {
|
scrollToPageTop() {
|
||||||
const container = document.getElementsByClassName("page-container-grouped-styles")[0];
|
const container = document.getElementsByClassName("page-container-grouped-styles")[0];
|
||||||
container.scrollTo({ top: 0, left: 0, behavior: "smooth" });
|
container.scrollTo({ top: 0, left: 0, behavior: "smooth" });
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import { deleteFunnelCookie } from "@/helpers/heritage-integration/cookie-helper
|
||||||
import { deepEqual } from "@/helpers/object-helper";
|
import { deepEqual } from "@/helpers/object-helper";
|
||||||
import { AppointmentTypeStrings, PREMIUM_FEE_PART_TYPE } from "@/constants/schedule-constants";
|
import { AppointmentTypeStrings, PREMIUM_FEE_PART_TYPE } from "@/constants/schedule-constants";
|
||||||
import { getQuerystringParameter } from "@/helpers/querystring-helper";
|
import { getQuerystringParameter } from "@/helpers/querystring-helper";
|
||||||
|
import { deepClone } from "@/helpers/object-helper";
|
||||||
import { queryStrings } from "@/constants/query-strings";
|
import { queryStrings } from "@/constants/query-strings";
|
||||||
import { partTypeStrings } from "@/constants/part-type-strings";
|
import { partTypeStrings } from "@/constants/part-type-strings";
|
||||||
import {
|
import {
|
||||||
|
|
@ -111,6 +112,7 @@ const getDefaultState = () => {
|
||||||
referralCorrelationId: null,
|
referralCorrelationId: null,
|
||||||
eon: null,
|
eon: null,
|
||||||
workOrderNumber: null,
|
workOrderNumber: null,
|
||||||
|
createDeleteStatusWorkOrderForPia: false,
|
||||||
},
|
},
|
||||||
applicationUser: {
|
applicationUser: {
|
||||||
eventBus: [],
|
eventBus: [],
|
||||||
|
|
@ -231,6 +233,9 @@ export const mutations = {
|
||||||
updatePiaErrorCode(state, piaErrorCode) {
|
updatePiaErrorCode(state, piaErrorCode) {
|
||||||
state.order.payment.piaErrorCode = piaErrorCode;
|
state.order.payment.piaErrorCode = piaErrorCode;
|
||||||
},
|
},
|
||||||
|
updatePiaWorkOrder(state, piaWorkOrder) {
|
||||||
|
state.order.createDeleteStatusWorkOrderForPia = piaWorkOrder;
|
||||||
|
},
|
||||||
updateWorkOrderNumber(state, workOrderNumber) {
|
updateWorkOrderNumber(state, workOrderNumber) {
|
||||||
state.order.workOrderNumber = workOrderNumber;
|
state.order.workOrderNumber = workOrderNumber;
|
||||||
},
|
},
|
||||||
|
|
@ -602,7 +607,10 @@ export const getters = {
|
||||||
funnelServiceCity: state.order.serviceLocation.city,
|
funnelServiceCity: state.order.serviceLocation.city,
|
||||||
funnelServiceState: state.order.serviceLocation.state,
|
funnelServiceState: state.order.serviceLocation.state,
|
||||||
funnelServiceZipCode: state.order.serviceLocation.zipCode,
|
funnelServiceZipCode: state.order.serviceLocation.zipCode,
|
||||||
|
funnelServiceZipCodeCtu: state.order.serviceLocation.zipCodeCtu,
|
||||||
|
funnelProviderNumber: state.order.serviceLocation.provider.providerNumber,
|
||||||
funnelParentAccountNumber: state.order.payment.parentAccountNumber,
|
funnelParentAccountNumber: state.order.payment.parentAccountNumber,
|
||||||
|
funnelReferralType: state.order.payment.isInsurance ? "INSURANCE" : "CASH QUOTE",
|
||||||
funnelIsCoverageVerified: state.order.payment.insuranceCoverage.isVerified,
|
funnelIsCoverageVerified: state.order.payment.insuranceCoverage.isVerified,
|
||||||
funnelHasRecalibrationPart: getHasRecalibrationPart(state),
|
funnelHasRecalibrationPart: getHasRecalibrationPart(state),
|
||||||
funnelSelectedMultiGlass: state.order.damage.glassToReplace?.length > 1,
|
funnelSelectedMultiGlass: state.order.damage.glassToReplace?.length > 1,
|
||||||
|
|
@ -1633,6 +1641,7 @@ export const actions = {
|
||||||
referralNumber: order.referralNumber?.toString(),
|
referralNumber: order.referralNumber?.toString(),
|
||||||
referralSequenceNumber: order.referralSequenceNumber,
|
referralSequenceNumber: order.referralSequenceNumber,
|
||||||
eon: order.eon,
|
eon: order.eon,
|
||||||
|
createDeleteStatusWorkOrderForPia: order.createDeleteStatusWorkOrderForPia,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
logApiCall: true,
|
logApiCall: true,
|
||||||
|
|
@ -1709,6 +1718,9 @@ export const actions = {
|
||||||
savePiaErrorCode(context, piaErrorCode) {
|
savePiaErrorCode(context, piaErrorCode) {
|
||||||
context.commit(storeMutations.UPDATE_PIA_ERROR_CODE, piaErrorCode);
|
context.commit(storeMutations.UPDATE_PIA_ERROR_CODE, piaErrorCode);
|
||||||
},
|
},
|
||||||
|
savePiaWorkOrder(context, piaWorkOrder) {
|
||||||
|
context.commit(storeMutations.UPDATE_PIA_WORK_ORDER, piaWorkOrder);
|
||||||
|
},
|
||||||
|
|
||||||
// Business domain actions
|
// Business domain actions
|
||||||
|
|
||||||
|
|
@ -2075,9 +2087,9 @@ export const actions = {
|
||||||
|
|
||||||
context.commit(storeMutations.UPDATE_LINE_ITEMS_SERVER_DATA, response.data.serverData);
|
context.commit(storeMutations.UPDATE_LINE_ITEMS_SERVER_DATA, response.data.serverData);
|
||||||
|
|
||||||
availableLineItems = addPricesToLineItems(availableLineItems, response.data.lineItems);
|
const pricedLineItems = addPricesToLineItems(availableLineItems, response.data.lineItems);
|
||||||
|
|
||||||
return availableLineItems;
|
return pricedLineItems;
|
||||||
},
|
},
|
||||||
|
|
||||||
async validateOrderPromoAndSaveServerData(
|
async validateOrderPromoAndSaveServerData(
|
||||||
|
|
@ -2374,7 +2386,8 @@ function convertGlassPieceNamingFromApi(glassArray) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function addPricesToLineItems(lineItems, pricingLineItems) {
|
function addPricesToLineItems(lineItems, pricingLineItems) {
|
||||||
lineItems.forEach((lineItem) => {
|
const pricedLineItems = deepClone(lineItems);
|
||||||
|
pricedLineItems.forEach((lineItem) => {
|
||||||
let lineItemIndex = pricingLineItems.findIndex(
|
let lineItemIndex = pricingLineItems.findIndex(
|
||||||
(pricingLineItem) => pricingLineItem.partNumber === lineItem.partNumber
|
(pricingLineItem) => pricingLineItem.partNumber === lineItem.partNumber
|
||||||
);
|
);
|
||||||
|
|
@ -2386,7 +2399,7 @@ function addPricesToLineItems(lineItems, pricingLineItems) {
|
||||||
lineItem.sellingPrice = pricedLineItem.sellingPrice;
|
lineItem.sellingPrice = pricedLineItem.sellingPrice;
|
||||||
lineItem.kitPrice = pricedLineItem.kitPrice;
|
lineItem.kitPrice = pricedLineItem.kitPrice;
|
||||||
});
|
});
|
||||||
return lineItems;
|
return pricedLineItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getArrayOfAllLineItems(lineItems) {
|
function getArrayOfAllLineItems(lineItems) {
|
||||||
|
|
|
||||||
|
|
@ -2945,6 +2945,9 @@ describe("Getters", () => {
|
||||||
funnelSupportingItems: null,
|
funnelSupportingItems: null,
|
||||||
funnelVaps: null,
|
funnelVaps: null,
|
||||||
funnelGlassToReplace: null,
|
funnelGlassToReplace: null,
|
||||||
|
funnelProviderNumber: "1",
|
||||||
|
funnelReferralType: "CASH QUOTE",
|
||||||
|
funnelServiceZipCodeCtu: "11111",
|
||||||
};
|
};
|
||||||
|
|
||||||
//Act
|
//Act
|
||||||
|
|
@ -2959,12 +2962,17 @@ describe("Getters", () => {
|
||||||
city: mockStateValues.funnelServiceCity,
|
city: mockStateValues.funnelServiceCity,
|
||||||
state: mockStateValues.funnelServiceState,
|
state: mockStateValues.funnelServiceState,
|
||||||
zipCode: mockStateValues.funnelServiceZipCode,
|
zipCode: mockStateValues.funnelServiceZipCode,
|
||||||
|
zipCodeCtu: mockStateValues.funnelServiceZipCodeCtu,
|
||||||
|
provider: {
|
||||||
|
providerNumber: mockStateValues.funnelProviderNumber,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
mutations.updateParentAcctNumber(storeState, mockStateValues.funnelParentAccountNumber);
|
mutations.updateParentAcctNumber(storeState, mockStateValues.funnelParentAccountNumber);
|
||||||
mutations.updateInsuranceVerifiedStatus(
|
mutations.updateInsuranceVerifiedStatus(
|
||||||
storeState,
|
storeState,
|
||||||
mockStateValues.funnelIsCoverageVerified
|
mockStateValues.funnelIsCoverageVerified
|
||||||
);
|
);
|
||||||
|
mutations.updateIsInsurance(storeState, false);
|
||||||
mutations.updateGlassParts(storeState, mockStateValues.funnelGlassParts);
|
mutations.updateGlassParts(storeState, mockStateValues.funnelGlassParts);
|
||||||
mutations.updateSupportingItems(storeState, mockStateValues.funnelSupportingItems);
|
mutations.updateSupportingItems(storeState, mockStateValues.funnelSupportingItems);
|
||||||
mutations.updateVaps(storeState, mockStateValues.funnelVaps);
|
mutations.updateVaps(storeState, mockStateValues.funnelVaps);
|
||||||
|
|
@ -2992,6 +3000,9 @@ describe("Getters", () => {
|
||||||
funnelSelectedBackGlass: false,
|
funnelSelectedBackGlass: false,
|
||||||
funnelSelectedDriverSideGlass: false,
|
funnelSelectedDriverSideGlass: false,
|
||||||
funnelSelectedPassengerSideGlass: false,
|
funnelSelectedPassengerSideGlass: false,
|
||||||
|
funnelProviderNumber: mockStateValues.funnelProviderNumber,
|
||||||
|
funnelReferralType: mockStateValues.funnelReferralType,
|
||||||
|
funnelServiceZipCodeCtu: mockStateValues.funnelServiceZipCodeCtu,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -3014,6 +3025,9 @@ describe("Getters", () => {
|
||||||
funnelGlassParts: [],
|
funnelGlassParts: [],
|
||||||
funnelOtherParts: [],
|
funnelOtherParts: [],
|
||||||
funnelGlassToReplace: [],
|
funnelGlassToReplace: [],
|
||||||
|
funnelProviderNumber: "1",
|
||||||
|
funnelReferralType: "CASH QUOTE",
|
||||||
|
funnelServiceZipCodeCtu: "11111",
|
||||||
};
|
};
|
||||||
|
|
||||||
//Act
|
//Act
|
||||||
|
|
@ -3028,12 +3042,17 @@ describe("Getters", () => {
|
||||||
city: mockStateValues.funnelServiceCity,
|
city: mockStateValues.funnelServiceCity,
|
||||||
state: mockStateValues.funnelServiceState,
|
state: mockStateValues.funnelServiceState,
|
||||||
zipCode: mockStateValues.funnelServiceZipCode,
|
zipCode: mockStateValues.funnelServiceZipCode,
|
||||||
|
zipCodeCtu: mockStateValues.funnelServiceZipCodeCtu,
|
||||||
|
provider: {
|
||||||
|
providerNumber: mockStateValues.funnelProviderNumber,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
mutations.updateParentAcctNumber(storeState, mockStateValues.funnelParentAccountNumber);
|
mutations.updateParentAcctNumber(storeState, mockStateValues.funnelParentAccountNumber);
|
||||||
mutations.updateInsuranceVerifiedStatus(
|
mutations.updateInsuranceVerifiedStatus(
|
||||||
storeState,
|
storeState,
|
||||||
mockStateValues.funnelIsCoverageVerified
|
mockStateValues.funnelIsCoverageVerified
|
||||||
);
|
);
|
||||||
|
mutations.updateIsInsurance(storeState, false);
|
||||||
mutations.updateGlassParts(storeState, mockStateValues.funnelGlassParts);
|
mutations.updateGlassParts(storeState, mockStateValues.funnelGlassParts);
|
||||||
mutations.updateSupportingItems(storeState, mockStateValues.funnelSupportingItems);
|
mutations.updateSupportingItems(storeState, mockStateValues.funnelSupportingItems);
|
||||||
mutations.updateVaps(storeState, mockStateValues.funnelVaps);
|
mutations.updateVaps(storeState, mockStateValues.funnelVaps);
|
||||||
|
|
@ -3061,6 +3080,9 @@ describe("Getters", () => {
|
||||||
funnelSelectedBackGlass: false,
|
funnelSelectedBackGlass: false,
|
||||||
funnelSelectedDriverSideGlass: false,
|
funnelSelectedDriverSideGlass: false,
|
||||||
funnelSelectedPassengerSideGlass: false,
|
funnelSelectedPassengerSideGlass: false,
|
||||||
|
funnelProviderNumber: mockStateValues.funnelProviderNumber,
|
||||||
|
funnelReferralType: mockStateValues.funnelReferralType,
|
||||||
|
funnelServiceZipCodeCtu: mockStateValues.funnelServiceZipCodeCtu,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -3096,6 +3118,9 @@ describe("Getters", () => {
|
||||||
glassName: "Single",
|
glassName: "Single",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
funnelProviderNumber: "1",
|
||||||
|
funnelReferralType: "CASH QUOTE",
|
||||||
|
funnelServiceZipCodeCtu: "11111",
|
||||||
};
|
};
|
||||||
|
|
||||||
//Act
|
//Act
|
||||||
|
|
@ -3110,9 +3135,14 @@ describe("Getters", () => {
|
||||||
city: mockStateValues.serviceCity,
|
city: mockStateValues.serviceCity,
|
||||||
state: mockStateValues.serviceState,
|
state: mockStateValues.serviceState,
|
||||||
zipCode: mockStateValues.serviceZipCode,
|
zipCode: mockStateValues.serviceZipCode,
|
||||||
|
zipCodeCtu: mockStateValues.funnelServiceZipCodeCtu,
|
||||||
|
provider: {
|
||||||
|
providerNumber: mockStateValues.funnelProviderNumber,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
mutations.updateParentAcctNumber(storeState, mockStateValues.parentAccountNumber);
|
mutations.updateParentAcctNumber(storeState, mockStateValues.parentAccountNumber);
|
||||||
mutations.updateInsuranceVerifiedStatus(storeState, mockStateValues.isCoverageVerified);
|
mutations.updateInsuranceVerifiedStatus(storeState, mockStateValues.isCoverageVerified);
|
||||||
|
mutations.updateIsInsurance(storeState, false);
|
||||||
mutations.updateGlassParts(storeState, mockStateValues.glassParts);
|
mutations.updateGlassParts(storeState, mockStateValues.glassParts);
|
||||||
mutations.updateSupportingItems(storeState, mockStateValues.funnelSupportingItems);
|
mutations.updateSupportingItems(storeState, mockStateValues.funnelSupportingItems);
|
||||||
mutations.updateVaps(storeState, mockStateValues.funnelVaps);
|
mutations.updateVaps(storeState, mockStateValues.funnelVaps);
|
||||||
|
|
@ -3140,6 +3170,9 @@ describe("Getters", () => {
|
||||||
funnelSelectedBackGlass: false,
|
funnelSelectedBackGlass: false,
|
||||||
funnelSelectedDriverSideGlass: false,
|
funnelSelectedDriverSideGlass: false,
|
||||||
funnelSelectedPassengerSideGlass: false,
|
funnelSelectedPassengerSideGlass: false,
|
||||||
|
funnelProviderNumber: mockStateValues.funnelProviderNumber,
|
||||||
|
funnelReferralType: mockStateValues.funnelReferralType,
|
||||||
|
funnelServiceZipCodeCtu: mockStateValues.funnelServiceZipCodeCtu,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -3201,6 +3234,9 @@ describe("Getters", () => {
|
||||||
glassName: "Quarter",
|
glassName: "Quarter",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
funnelProviderNumber: "1",
|
||||||
|
funnelReferralType: "CASH QUOTE",
|
||||||
|
funnelServiceZipCodeCtu: "11111",
|
||||||
};
|
};
|
||||||
|
|
||||||
//Act
|
//Act
|
||||||
|
|
@ -3215,9 +3251,14 @@ describe("Getters", () => {
|
||||||
city: mockStateValues.serviceCity,
|
city: mockStateValues.serviceCity,
|
||||||
state: mockStateValues.serviceState,
|
state: mockStateValues.serviceState,
|
||||||
zipCode: mockStateValues.serviceZipCode,
|
zipCode: mockStateValues.serviceZipCode,
|
||||||
|
zipCodeCtu: mockStateValues.funnelServiceZipCodeCtu,
|
||||||
|
provider: {
|
||||||
|
providerNumber: mockStateValues.funnelProviderNumber,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
mutations.updateParentAcctNumber(storeState, mockStateValues.parentAccountNumber);
|
mutations.updateParentAcctNumber(storeState, mockStateValues.parentAccountNumber);
|
||||||
mutations.updateInsuranceVerifiedStatus(storeState, mockStateValues.isCoverageVerified);
|
mutations.updateInsuranceVerifiedStatus(storeState, mockStateValues.isCoverageVerified);
|
||||||
|
mutations.updateIsInsurance(storeState, false);
|
||||||
mutations.updateGlassParts(storeState, mockStateValues.glassParts);
|
mutations.updateGlassParts(storeState, mockStateValues.glassParts);
|
||||||
mutations.updateSupportingItems(storeState, mockStateValues.funnelSupportingItems);
|
mutations.updateSupportingItems(storeState, mockStateValues.funnelSupportingItems);
|
||||||
mutations.updateVaps(storeState, mockStateValues.funnelVaps);
|
mutations.updateVaps(storeState, mockStateValues.funnelVaps);
|
||||||
|
|
@ -3245,6 +3286,9 @@ describe("Getters", () => {
|
||||||
funnelSelectedBackGlass: true,
|
funnelSelectedBackGlass: true,
|
||||||
funnelSelectedDriverSideGlass: true,
|
funnelSelectedDriverSideGlass: true,
|
||||||
funnelSelectedPassengerSideGlass: true,
|
funnelSelectedPassengerSideGlass: true,
|
||||||
|
funnelProviderNumber: mockStateValues.funnelProviderNumber,
|
||||||
|
funnelReferralType: mockStateValues.funnelReferralType,
|
||||||
|
funnelServiceZipCodeCtu: mockStateValues.funnelServiceZipCodeCtu,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ body {
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
color: #4d5151;
|
color: #4d5151;
|
||||||
.container-fluid {
|
.container-fluid {
|
||||||
|
padding: 0 1.5rem;
|
||||||
.prevent-squish {
|
.prevent-squish {
|
||||||
overflow-x: unset;
|
overflow-x: unset;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue