Merge pull request #2436 from Safelite/feature/Digital/CASH-205
Feature/digital/cash 205
This commit is contained in:
commit
3f14178321
8 changed files with 311 additions and 1 deletions
6
src/assets/img/icons/calendar-icon-black.svg
Normal file
6
src/assets/img/icons/calendar-icon-black.svg
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M2.65567 6.44287H13.35" stroke="#0A0A0A" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M10.4256 2V3.97449" stroke="#0A0A0A" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M5.57864 2V3.97449" stroke="#0A0A0A" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.5427 2.94792H5.4625C3.70011 2.94792 2.60001 3.92933 2.60001 5.73323V11.1637C2.60001 12.9962 3.70011 14.0003 5.4625 14.0003H10.5375C12.3051 14.0003 13.4001 13.0137 13.4001 11.2091V5.73323C13.4052 3.92933 12.3103 2.94792 10.5427 2.94792Z" stroke="#0A0A0A" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 748 B |
6
src/assets/img/icons/calendar-icon-grey.svg
Normal file
6
src/assets/img/icons/calendar-icon-grey.svg
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
<svg width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M3.15567 6.44287H13.85" stroke="#4B4C4E" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M10.9256 2V3.97449" stroke="#4B4C4E" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M6.07864 2V3.97449" stroke="#4B4C4E" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.0427 2.94791H5.9625C4.20011 2.94791 3.10001 3.92931 3.10001 5.73321V11.1637C3.10001 12.9962 4.20011 14.0003 5.9625 14.0003H11.0375C12.8051 14.0003 13.9001 13.0137 13.9001 11.2091V5.73321C13.9052 3.92931 12.8103 2.94791 11.0427 2.94791Z" stroke="#4B4C4E" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 748 B |
4
src/assets/img/icons/info-circle.svg
Normal file
4
src/assets/img/icons/info-circle.svg
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M3 12C3 16.9699 7.02908 21 12 21C16.9709 21 21 16.9699 21 12C21 7.02908 16.9709 3 12 3C7.02908 3 3 7.02908 3 12Z" stroke="#0070D1" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M12.0057 15.6932V11.3936M12 8.35426V8.29102" stroke="#0070D1" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 450 B |
6
src/assets/img/icons/payment-icon-green.svg
Normal file
6
src/assets/img/icons/payment-icon-green.svg
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M2 13.8143H13.5894" stroke="#00723E" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.20013 2.88802L3.08796 7.00022C2.09364 7.99456 2.17407 9.32289 3.17099 10.3198L5.86013 13.0096C6.85706 14.0065 8.18213 14.0896 9.18039 13.092L13.2919 8.98042C14.2901 7.98222 14.2065 6.6571 13.2095 5.66018L10.5197 2.97039C9.52286 1.97348 8.19773 1.89046 7.20013 2.88802Z" stroke="#00723E" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M3.6315 10.782L10.9809 3.4326" stroke="#00723E" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M7.51559 11.2542L8.72786 10.042" stroke="#00723E" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 800 B |
6
src/assets/img/icons/payment-icon-grey.svg
Normal file
6
src/assets/img/icons/payment-icon-grey.svg
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
<svg width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M2.5 13.8143H14.0894" stroke="#4B4C4E" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.70014 2.88802L3.58797 7.00022C2.59365 7.99456 2.67408 9.32289 3.671 10.3198L6.36014 13.0096C7.35707 14.0065 8.68214 14.0896 9.68041 13.092L13.7919 8.98042C14.7901 7.98222 14.7065 6.6571 13.7095 5.66018L11.0197 2.97039C10.0229 1.97348 8.69774 1.89046 7.70014 2.88802Z" stroke="#4B4C4E" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M4.13152 10.782L11.4809 3.43262" stroke="#4B4C4E" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M8.01558 11.2543L9.22785 10.042" stroke="#4B4C4E" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 802 B |
|
|
@ -0,0 +1,259 @@
|
||||||
|
<template>
|
||||||
|
<div class="alert fade show my-2 py-2 border-0 alert-info" role="alert">
|
||||||
|
<component :is="'script'" src="https://js.afterpay.com/afterpay-1.x.js" async></component>
|
||||||
|
<div class="my-4 alert-heading text-center">
|
||||||
|
<span v-html="afterpayHeaderCopy" />
|
||||||
|
|
||||||
|
<span class="afterpay-amount">{{ this.afterpayPrice }}</span>
|
||||||
|
<br />
|
||||||
|
<span class="alert-sub-heading" v-html="afterpayHeaderSubHeaderCopy" />
|
||||||
|
|
||||||
|
<img :src="afterPayImageUrl" />
|
||||||
|
|
||||||
|
<a
|
||||||
|
id="afterpay-learnmore"
|
||||||
|
href="#"
|
||||||
|
data-afterpay-modal="en_US-safelite"
|
||||||
|
data-bind="click:afterpayLearnMore">
|
||||||
|
<img :src="infoIcon" alt="Info Icon" class="info-icon" />
|
||||||
|
</a>
|
||||||
|
<div class="after-pay">
|
||||||
|
<div
|
||||||
|
class="after-pay-toggle"
|
||||||
|
:class="[isExpanded ? 'expanded' : '']"
|
||||||
|
@click="toggleIsExpanded">
|
||||||
|
<a href="#!" v-html="paymentPlanToggleHeading" />
|
||||||
|
</div>
|
||||||
|
<div class="after-pay-details">
|
||||||
|
<div class="payment-section">
|
||||||
|
<div class="payment-card">
|
||||||
|
<div class="calendar-section">
|
||||||
|
<div class="icon"><img :src="calendarIcon" /></div>
|
||||||
|
<div>Today</div>
|
||||||
|
</div>
|
||||||
|
<div class="payment-amount-section">
|
||||||
|
<div class="icon"><img :src="paymentIcon" /></div>
|
||||||
|
<div class="amount-due">{{ this.afterpayPrice }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="payment-card">
|
||||||
|
<div class="calendar-section">
|
||||||
|
<div class="icon"><img :src="calendarIcon" /></div>
|
||||||
|
<div>2 weeks</div>
|
||||||
|
</div>
|
||||||
|
<div class="payment-amount-section">
|
||||||
|
<div class="icon"><img :src="paymentIcon" /></div>
|
||||||
|
<div class="amount-due">{{ this.afterpayPrice }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="payment-card">
|
||||||
|
<div class="calendar-section">
|
||||||
|
<div class="icon"><img :src="calendarIcon" /></div>
|
||||||
|
<div>4 weeks</div>
|
||||||
|
</div>
|
||||||
|
<div class="payment-amount-section">
|
||||||
|
<div class="icon"><img :src="paymentIcon" /></div>
|
||||||
|
<div class="amount-due">{{ this.afterpayPrice }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="payment-card">
|
||||||
|
<div class="calendar-section">
|
||||||
|
<div class="icon"><img :src="calendarIcon" /></div>
|
||||||
|
<div>6 weeks</div>
|
||||||
|
</div>
|
||||||
|
<div class="payment-amount-section">
|
||||||
|
<div class="icon"><img :src="paymentIcon" /></div>
|
||||||
|
<div class="amount-due">{{ this.afterpayPrice }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="legend">
|
||||||
|
<div class="legend-item">
|
||||||
|
<img :src="calendarKeyIcon" />
|
||||||
|
<span>Payment Schedule</span>
|
||||||
|
</div>
|
||||||
|
<div class="legend-item">
|
||||||
|
<img :src="paymentKeyIcon" />
|
||||||
|
<span>Amount Due</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "afterpay-breakout",
|
||||||
|
|
||||||
|
props: {
|
||||||
|
cmsWidgetName: String,
|
||||||
|
totalAmount: Number,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return { isExpanded: false };
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
toggleIsExpanded() {
|
||||||
|
this.isExpanded = !this.isExpanded;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
afterPayImageUrl() {
|
||||||
|
return this.getCmsContent(this.cmsWidgetName, "Image");
|
||||||
|
},
|
||||||
|
hasImage() {
|
||||||
|
return !!this.imageUrl;
|
||||||
|
},
|
||||||
|
afterpayHeaderCopy() {
|
||||||
|
// Text for Afterpay Breakout experiment
|
||||||
|
|
||||||
|
return this.getCmsContent(this.cmsWidgetName, "HeaderText");
|
||||||
|
},
|
||||||
|
afterpayHeaderSubHeaderCopy() {
|
||||||
|
// Text for Afterpay Breakout experiment
|
||||||
|
|
||||||
|
return this.getCmsContent(this.cmsWidgetName, "SubheaderText");
|
||||||
|
},
|
||||||
|
afterpayPrice() {
|
||||||
|
const adjusted = (this.totalAmount / 4).toFixed(2);
|
||||||
|
return `$${adjusted}`;
|
||||||
|
},
|
||||||
|
infoIcon() {
|
||||||
|
return require(`@/assets/img/icons/info-circle.svg`);
|
||||||
|
},
|
||||||
|
calendarIcon() {
|
||||||
|
return require(`@/assets/img/icons/calendar-icon-black.svg`);
|
||||||
|
},
|
||||||
|
paymentIcon() {
|
||||||
|
return require(`@/assets/img/icons/payment-icon-green.svg`);
|
||||||
|
},
|
||||||
|
calendarKeyIcon() {
|
||||||
|
return require(`@/assets/img/icons/calendar-icon-grey.svg`);
|
||||||
|
},
|
||||||
|
paymentKeyIcon() {
|
||||||
|
return require(`@/assets/img/icons/payment-icon-grey.svg`);
|
||||||
|
},
|
||||||
|
paymentPlanToggleHeading() {
|
||||||
|
return this.getCmsContent(this.cmsWidgetName, "BodyText");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.alert {
|
||||||
|
border-radius: 5px;
|
||||||
|
margin-top: 1rem;
|
||||||
|
|
||||||
|
&.alert-info {
|
||||||
|
.alert-heading {
|
||||||
|
color: #000000;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-left: 0.5rem;
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
@include media-breakpoint-up(md) {
|
||||||
|
margin-left: 1rem;
|
||||||
|
margin-right: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.afterpay-amount {
|
||||||
|
color: #0c7e47;
|
||||||
|
}
|
||||||
|
.alert-sub-heading {
|
||||||
|
color: #4b4c4e;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
.after-pay {
|
||||||
|
.after-pay-toggle {
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
content: "";
|
||||||
|
transition: all 0.5s ease;
|
||||||
|
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 8.9' xml:space='preserve'%3e%3cpath d='M8 8.9c-.2 0-.5-.1-.6-.3L.3 1.5C.1 1.4 0 1.1 0 .9 0 .7.1.4.3.3.4.1.7 0 .9 0c.2 0 .5.1.6.3L8 6.7 14.5.2c.1-.1.4-.2.6-.2.2 0 .5.1.6.3s.3.4.3.6c0 .2-.1.5-.3.6L8.6 8.6c-.1.2-.4.3-.6.3z' fill='%231474a2'/%3e%3c/svg%3e");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: right center;
|
||||||
|
margin-left: 0.5rem;
|
||||||
|
width: 1rem;
|
||||||
|
height: 9px;
|
||||||
|
display: inline-flex;
|
||||||
|
}
|
||||||
|
&.expanded:after {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
&.expanded + .after-pay-details {
|
||||||
|
max-height: 500px;
|
||||||
|
transition: all 250ms ease-in;
|
||||||
|
opacity: 1;
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.after-pay-toggle a {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #0070d1;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.after-pay-details {
|
||||||
|
max-height: 0;
|
||||||
|
transition: all 250ms ease-out;
|
||||||
|
overflow: hidden;
|
||||||
|
opacity: 0;
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.payment-section {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
gap: 0.5rem;
|
||||||
|
margin-top: 1rem;
|
||||||
|
@include media-breakpoint-up(md) {
|
||||||
|
gap: 0rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.payment-card {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 0.25px #b3b4b5;
|
||||||
|
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
.calendar-section {
|
||||||
|
background-color: #fff; /* White background for calendar area */
|
||||||
|
padding: 0.5rem;
|
||||||
|
text-align: center;
|
||||||
|
gap: 2px;
|
||||||
|
font-weight: 700;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.payment-amount-section {
|
||||||
|
background-color: #e0f7e9; /* Light green background for payment area */
|
||||||
|
padding: 0.5rem;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.amount-due {
|
||||||
|
color: #00723e; /* Green text */
|
||||||
|
}
|
||||||
|
.legend {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
margin-top: 1rem;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
.legend-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 2px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.legend-item span {
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -117,6 +117,12 @@ function setupMocks() {
|
||||||
|
|
||||||
return "false";
|
return "false";
|
||||||
}),
|
}),
|
||||||
|
hasSettingEqualTo: jest.fn((settingName) => {
|
||||||
|
if (settingName === experimentSettings.DISPLAY_AFTERPAY_BREAKOUT_DISPLAY) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}),
|
||||||
getCmsContent: jest.fn().mockImplementation(() => {
|
getCmsContent: jest.fn().mockImplementation(() => {
|
||||||
return "test text";
|
return "test text";
|
||||||
}),
|
}),
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,10 @@
|
||||||
:isExpandedOnLoad="false"
|
:isExpandedOnLoad="false"
|
||||||
:isMSRFeeApplicable="isMSRFeeApplicable" />
|
:isMSRFeeApplicable="isMSRFeeApplicable" />
|
||||||
|
|
||||||
|
<afterpayBreakout
|
||||||
|
v-if="isAfterpayBreakoutDisplay"
|
||||||
|
cmsWidgetName="AfterpayBreakoutAlertWidget"
|
||||||
|
:totalAmount="totalAmountDue" />
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -118,6 +122,7 @@ import alert from "@/ux-components/alert/alert";
|
||||||
import textBlock from "@/digital-components/text-block/text-block";
|
import textBlock from "@/digital-components/text-block/text-block";
|
||||||
import checkboxQuestion from "@/digital-components/checkbox-question/checkbox-question";
|
import checkboxQuestion from "@/digital-components/checkbox-question/checkbox-question";
|
||||||
import contentGroupModal from "@/fmg-components/content-group-modal/content-group-modal";
|
import contentGroupModal from "@/fmg-components/content-group-modal/content-group-modal";
|
||||||
|
import afterpayBreakout from "@/layouts/payment-method/afterpay-breakout/afterpay-breakout";
|
||||||
|
|
||||||
// Supporting Items
|
// Supporting Items
|
||||||
import baseMixin from "@/mixins/base-mixin.js";
|
import baseMixin from "@/mixins/base-mixin.js";
|
||||||
|
|
@ -131,7 +136,6 @@ import {
|
||||||
} from "@/helpers/cms-content-helper";
|
} from "@/helpers/cms-content-helper";
|
||||||
import { paymentMethods } from "@/constants/payment-method-constants";
|
import { paymentMethods } from "@/constants/payment-method-constants";
|
||||||
import { experimentSettings } from "@/constants/experiments";
|
import { experimentSettings } from "@/constants/experiments";
|
||||||
import experimentMixin from "@/mixins/experiment-mixin.js";
|
|
||||||
import {
|
import {
|
||||||
revalidatePromosAndValidateQueryStringPromo,
|
revalidatePromosAndValidateQueryStringPromo,
|
||||||
buildToastMessagesFromRevalidateOrValidatePromoResponse,
|
buildToastMessagesFromRevalidateOrValidatePromoResponse,
|
||||||
|
|
@ -805,6 +809,18 @@ export default {
|
||||||
lineItemsCloneForWatcher() {
|
lineItemsCloneForWatcher() {
|
||||||
return Object.assign({}, this.lineItems);
|
return Object.assign({}, this.lineItems);
|
||||||
},
|
},
|
||||||
|
isRecalPriceRemove() {
|
||||||
|
return (
|
||||||
|
this.getSettingValue(experimentSettings.RECAL_PRICE_REMOVE)?.toLowerCase() ===
|
||||||
|
"true"
|
||||||
|
);
|
||||||
|
},
|
||||||
|
isAfterpayBreakoutDisplay() {
|
||||||
|
return (
|
||||||
|
(!this.isRecalPriceRemove || !this.isRecalibrationOnOrder) &&
|
||||||
|
this.hasSettingEqualTo(experimentSettings.DISPLAY_AFTERPAY_BREAKOUT_DISPLAY, "true")
|
||||||
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
customCtaCopy(newValue) {
|
customCtaCopy(newValue) {
|
||||||
|
|
@ -856,6 +872,7 @@ export default {
|
||||||
textBlock,
|
textBlock,
|
||||||
checkboxQuestion,
|
checkboxQuestion,
|
||||||
contentGroupModal,
|
contentGroupModal,
|
||||||
|
afterpayBreakout,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue