Merge branch 'develop' into CASH-191-button-fix-2

This commit is contained in:
bmauger 2025-02-20 10:01:09 -05:00 committed by GitHub
commit e3c3d08fbe
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 434 additions and 1 deletions

View file

@ -34,7 +34,7 @@ module.exports = {
testMatch: ["**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)"],
coverageThreshold: {
global: {
statements: 75,
statements: 74,
},
},
// Uncomment this to avoid the massive amount of warnings we are getting for onSubmit and onInvalidSubmit

View file

@ -107,6 +107,7 @@ const storeActions = {
CREATE_SUBMITTED_ORDER: "createSubmittedOrder",
RESET_SUBMITTED_ORDER: "resetSubmittedOrder",
ADD_DONATION_TO_SUBMITTED_ORDER: "addDonationToSubmittedOrder",
RESET_EXTERNAL_PARAMETER_STATE: "resetExternalParameterState",
UPDATE_EXTERNAL_PARAMETER_MMS: "updateExternalParameterMMS",

View file

@ -0,0 +1,274 @@
<template>
<div class="donation-block">
<form ref="donationForm" id="donation-form" @submit.prevent="handleDonationAction">
<div class="row g-0">
<div class="col-12">
<p class="headline text-center" v-html="DonationHeadline"></p>
</div>
</div>
<div class="row-align d-flex px-4">
<div>
<img class="w-100 mt-1 me-4" :src="LogoImage" />
</div>
<div>
<p class="small subheadline mb-2" v-html="DonationSubHeadline"></p>
<p class="caption mb-0" v-html="DonationBodyText"></p>
<textLink
linkType="newWindowLink"
text="Learn more"
:href="DonationBodySubText"
target="_blank" />
</div>
</div>
<div class="px-4" :class="[showDonationSuccess ? 'hide-donation-block' : 'show-donation-block']">
<div class="row">
<div class="col-12">
<div class="donation-slider my-4">
<input
name="amount"
id="one"
type="radio"
:value="donationValues[0]"
checked />
<label for="one" class="label">$1</label>
<input name="amount" id="three" type="radio" :value="donationValues[1]" />
<label for="three" class="label">$3</label>
<input name="amount" id="five" type="radio" :value="donationValues[2]" />
<label for="five" class="label">$5</label>
<div class="indicator-bar"></div>
</div>
</div>
</div>
<div class="row">
<div class="col-12">
<button
class="btn btn-secondary w-100 mb-2"
v-html="DonationAddButtonWidget"></button>
</div>
</div>
</div>
<div class="row px-4">
<div class="col-12">
<div
v-if="showDonationSuccess"
class="d-flex justify-content-center align-items-center btn-success w-100 mb-2"
v-html="DonationThanksAlertWidget"></div>
</div>
</div>
<div class="row px-4">
<div class="col">
<alert
v-if="showDonationError"
cmsWidgetName="DonationErrorWidget"
alertClass="alert-danger" />
</div>
</div>
<div class="row">
<div class="col-12">
<p class="my-4 mx-0 text-center caption" v-html="DonationFooterText"></p>
</div>
</div>
</form>
</div>
</template>
<script>
import textLink from "@/ux-components/text-link/text-link";
import alert from "@/ux-components/alert/alert";
export default {
name: "donationBlock",
props: {
cmsWidgetName: String,
modelValue: String,
showDonationSuccess: Boolean,
showDonationError: Boolean,
donationValues: Array,
},
computed: {
DonationHeadline() {
return this.getCmsContent(this.cmsWidgetName, "HeaderText");
},
DonationSubHeadline() {
return this.getCmsContent(this.cmsWidgetName, "SubheaderText");
},
DonationBodyText() {
return this.getCmsContent(this.cmsWidgetName, "BodyText");
},
DonationBodySubText() {
return this.getCmsContent(this.cmsWidgetName, "BodyText2");
},
DonationAddButtonWidget() {
return this.getCmsContent("DonationAddButtonWidget", "Text");
},
DonationThanksAlertWidget() {
return this.getCmsContent("DonationThanksAlertWidget", "Text");
},
DonationFooterText() {
return this.getCmsContent(this.cmsWidgetName, "FooterText");
},
LogoImage() {
return this.getCmsContent("DonationWidget", "Image");
},
selectedValue: {
get: function () {
return this.modelValue?.toString();
},
set: function (newValue) {
this.$emit("update:modelValue", newValue);
},
},
},
methods: {
handleDonationAction(submitEvent) {
console.log(
"running handleDonationAction(), submitEvent.target.elements.amount.value: ",
submitEvent.target.elements.amount.value
);
this.selectedValue = submitEvent.target.elements.amount.value;
},
},
components: {
textLink,
alert,
},
};
</script>
<style lang="scss" scoped>
.donation-block {
padding: 1rem 0;
background-color: $gray-100;
border-radius: 0;
.hide-donation-block {
max-height: 0;
overflow: hidden;
margin-top: 1rem;
opacity: 0;
transition: all 250ms ease-in;
}
.show-donation-block {
max-height: 136px;
overflow: hidden;
margin-top: 0;
opacity: 1;
transition: all 250ms ease-in;
}
p {
&.headline {
font-family: AvertaBold;
color: $red;
font-weight: 600;
}
&.subheadline {
font-family: AvertaBold;
font-weight: 600;
}
}
:deep(a) {
&.new-window-link {
color: $blue;
font-size: 0.75rem;
}
}
img {
width: 5rem;
}
.donation-slider {
position: relative;
width: 100%;
padding: 0;
display: flex;
flex-direction: row;
justify-content: center;
background: $blue-100;
border-radius: 3rem;
box-shadow: 0px 0px 4px 1px rgba(0, 112, 209, 1) inset;
font-size: 0.875rem;
.label {
position: relative;
display: inline-flex;
width: 33.3333%;
height: 3rem;
padding: 0;
cursor: pointer;
transition: color 200ms ease-out;
align-items: center;
justify-content: center;
color: $blue;
z-index: 1;
}
.indicator-bar {
width: 33.3333%;
height: 3rem;
position: absolute;
top: 0;
left: -2rem;
background: $blue;
border-radius: 3rem;
box-shadow: 0px 0px 0px 1px rgba(0, 112, 209, 1) inset;
transition: transform 750ms cubic-bezier(0.02, 0.94, 0.09, 0.97);
transform: translate3d(2rem, 0, 0);
}
.indicator-bar {
background: $blue;
}
input {
&#one:checked {
~ .indicator-bar {
transform: translateX(2rem);
}
}
&#three:checked {
~ .indicator-bar {
transform: translateX(calc(100% + 2rem));
}
}
&#five:checked {
~ .indicator-bar {
transform: translateX(calc(200% + 2rem));
}
}
}
input:checked {
+ label {
color: $white;
}
}
input[type="radio"] {
&:not(:checked),
&:checked {
position: absolute;
left: -999999px;
display: none;
}
}
}
.alert {
border: 1px solid $red;
}
.btn-success {
background: $green-100;
border: 1px solid $green-400;
border-radius: 0.5rem;
height: 3rem;
padding: 0.5rem;
font-weight: 600;
&:before {
content: "";
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 17 16'%3E%3Cg clip-path='url(%23a)'%3E%3Cpath fill='%230C7E47' d='M8.5 0a8 8 0 1 0 0 16 8 8 0 0 0 0-16Zm3.7 6.171-4.449 4.45a.559.559 0 0 1-.8 0l-2.16-2.16a.563.563 0 0 1 .792-.8l1.76 1.76 4.066-4.042a.563.563 0 1 1 .792.8v-.008Z'/%3E%3C/g%3E%3Cdefs%3E%3CclipPath id='a'%3E%3Cpath fill='%23fff' d='M.5 0h16v16H.5z'/%3E%3C/clipPath%3E%3C/defs%3E%3C/svg%3E");
width: 1rem;
height: 0.95rem;
position: relative;
left: -0.25rem;
}
}
}
</style>

View file

@ -64,6 +64,19 @@
:isItac="isItac"
:isNoComp="isNoComp" />
<div class="mt-3">
<donationBlock
cmsWidgetName="DonationWidget"
v-model="donationAmount"
:donationValues="donationValues"
:showDonationSuccess="showDonationSuccess"
:showDonationError="showDonationError" />
</div>
<!-- <button type="button" @click="addDonation(3)">
Add Donation of $3
</button> -->
<hr class="mb-5" />
<div class="emailtext" v-html="ConfirmationEmailText"></div>
@ -108,6 +121,7 @@ import experimentMixin from "@/mixins/experiment-mixin.js";
import { partTypeStrings } from "@/constants/part-type-strings";
import { containsLineItemWithPartType } from "@/helpers/service-package-helper";
import { containsRecalParts } from "@/helpers/recal-helper.js";
import donationBlock from "@/experiment-components/donation-block.vue";
export default {
name: "confirmation",
@ -204,6 +218,11 @@ export default {
return hasRecalPriceRemoveExperiment && isRecalibrationOnOrder;
};
const donationItems = lineItemsFromSubmittedOrder.supportingItems?.filter(
(item) => item.partNumber === "DONATION"
);
const donationAmount = donationItems?.length > 0 ? donationItems[0].sellingPrice : 0;
// Call the "next" function to complete the transition to this page.
next((vm) => {
vm.setCmsContent(resultMap.cmsContent);
@ -211,6 +230,7 @@ export default {
vm.vaps = availableVaps;
vm.submittedOrder = submittedOrder;
vm.shouldHideRecalibration = shouldHideRecalibration();
vm.donationAmount = donationAmount;
});
},
data() {
@ -219,6 +239,9 @@ export default {
vaps: [],
submittedOrder: null,
shouldHideRecalibration: null,
donationAmount: 0,
showDonationSuccess: null,
showDonationError: null,
};
},
computed: {
@ -447,6 +470,9 @@ export default {
);
}
},
donationValues() {
return [1, 3, 5];
},
},
methods: {
arePagePrerequisitesValid() {
@ -489,6 +515,56 @@ export default {
forwardButtonAction() {
window.location.assign(location.protocol + "//" + location.host);
},
async addDonation(amount, plateTemp) {
// try to call API to add donation to order
// TEMP DUMMY CALL TO SIMULATE SUCCESS OR ERROR FROM API CALL
// TO BE REPLACED BY REAL CALL LATER
const donationResponse = await this.dispatchStoreActionWithLogging(
storeActions.LOOKUP_VIN_BY_PLATE,
{ licensePlate: plateTemp, licenseState: "OH" },
"test",
false
).catch((error) => {
// this.$refs.navbar.removeLoader();
console.error("Error!!! error: ", error);
return error;
});
console.log("ran addDonation()... (*fake call*) donationResponse: ", donationResponse);
if (donationResponse?.status == "200") {
this.showDonationSuccess = true;
this.showDonationError = false;
const donationResponse = await baseMixin.methods.dispatchStoreAction(
storeActions.ADD_DONATION_TO_SUBMITTED_ORDER,
amount,
false
);
} else {
this.showDonationError = true;
this.showDonationSuccess = false;
const donationResponse = await baseMixin.methods.dispatchStoreAction(
storeActions.ADD_DONATION_TO_SUBMITTED_ORDER,
0,
false
);
}
},
},
watch: {
donationAmount(newValue) {
newValue = parseInt(newValue);
console.log("WATCHED donationAmount changed: ", newValue);
// this.addDonation(newValue);
// TODO - when backend code is final, then remove the below and restore the line above
if (newValue === 5) {
this.addDonation(newValue, "FAILING_PLATE"); // throws error
} else {
this.addDonation(newValue, "2fast"); // should give successful response
}
},
},
components: {
funnelHeader,
@ -497,6 +573,7 @@ export default {
addToCalendar,
cart,
textBlock,
donationBlock,
},
};
</script>

View file

@ -12,6 +12,7 @@ import { singleWindshieldCarIds } from "@/constants/single-windshield-carids";
import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
import { deleteFunnelCookie } from "@/helpers/heritage-integration/cookie-helper.js";
import { deepEqual } from "@/helpers/object-helper";
import baseMixin from "@/mixins/base-mixin.js";
import {
AppointmentTypeStrings,
PREMIUM_FEE_PART_TYPE,
@ -524,6 +525,7 @@ export const mutations = {
},
resetExternalParameterQuoteState(state) {
externalParameterState.quote.servicePackage = null;
externalParameterState.quote.isInsurance = null;
saveExternalParameterState(externalParameterState);
},
resetIsExternalParameter(state) {
@ -3159,6 +3161,42 @@ export const actions = {
context.commit(storeMutations.UPDATE_AFFILIATE_COOKIES, affiliateCookies);
},
addDonationToSubmittedOrder(context, donationAmount) {
console.log("running addDonationToSubmittedOrder()... donationAmount: ", donationAmount);
const submittedOrder = deepClone(baseMixin.methods.getSubmittedOrder());
if (donationAmount > 0) {
console.log(
"running addDonationToSubmittedOrder()... adding donation to supportingItems in session storage "
);
submittedOrder.lineItems.supportingItems.push({
partNumber: "DONATION",
description: null,
partType: "DONATION",
isInsurable: false,
isChildPart: false,
laborAmount: 0,
sellingPrice: donationAmount,
kitPrice: 0,
salesTax: null,
id: "donation-" + donationAmount,
});
} else {
// filter out all donations
console.log(
"running addDonationToSubmittedOrder()... removing all donation items in supportingItems in session storage "
);
const nonDonationItems = submittedOrder.lineItems.supportingItems.filter((item) => {
return item.partType !== "DONATION";
});
submittedOrder.lineItems.supportingItems = nonDonationItems;
}
// set to local storage
window.sessionStorage.setItem("submittedOrder", JSON.stringify(submittedOrder));
},
resetSubmittedOrder(context) {
// clear from local storage
window.sessionStorage.removeItem("submittedOrder");

View file

@ -156,5 +156,48 @@ export default {
transition: background 0s 0s ease-in-out;
}
}
&.btn-success {
position: relative;
background: $green-100;
border: 1px solid $green-400;
border-radius: $border-radius-lg;
color: $black;
font-weight: 500;
transition: all 150ms linear;
height: 3rem;
&:hover {
color: $black;
background: $green-200;
}
&:focus, // Mouse, touch, stylus focus
&:focus-visible {
// Keyboard focus for accessibility
outline: none;
box-shadow:
0 0 0 3px $white,
0 0 0 5.5px $green-400;
color: $black;
background: $green-100;
}
&:disabled {
background: transparent;
color: $gray-550 !important;
font-weight: 400;
height: 48px;
border: 1px solid $gray-550;
border-radius: $border-radius-lg;
cursor: pointer;
pointer-events: all;
}
&.has-loader {
color: $white;
@include blue-gradient;
pointer-events: none;
}
&.delay {
// fixes flicker while transitioning between states
transition: background 0s 0s ease-in-out;
}
}
}
</style>