DigitalConsumer.FixMyGlass/src/experiment-components/service-package-radio-for-afterpay.vue
2025-07-30 15:13:02 -04:00

485 lines
16 KiB
Vue

<template>
<baseInputButton v-bind="$props" @buttonClicked="handleAnswerChange" v-model="selectedValue">
<div
class="package-label"
:class="[
this.buttonLabelSubCopy ? 'has-subheader' : '',
this.additionalButtonData.isInsuranceSelected ? 'is-insurance' : '',
this.hasPackageDiscount() ? 'has-package-discount' : '',
]"
for="testradio">
<div class="package-specs">
<div>
<p class="m-0">
<span v-html="this.buttonLabel"></span>
</p>
<p
class="sub-label m-0"
v-if="this.buttonLabelSubCopy"
v-html="this.buttonLabelSubCopy"></p>
</div>
<div class="hide-when-closed">
<!-- Parse the body text containing <ul> with logic -->
<ul>
<li v-for="listItem in this.arrayOfListItemsFromBodyText" :key="listItem">
<!-- no textLink -->
<span
v-if="!doesCopyContainTextLink(listItem)"
v-html="listItem"></span>
<!-- with textLink -->
<template
v-else
v-for="copy in splitCopyOnCMSPlaceHolder(listItem)"
:key="copy">
<span v-if="!doesCopyContainTextLink(copy)" v-html="copy"></span>
<span v-else>
<textLink
linkType="text"
:text="getRouterLinkDisplayTextFromCopy(copy)"
href="#!"
@click-event="
$emit('buttonEvent', {
eventName: 'openModal',
args: getRouterLinkRouteFromCopy(copy),
})
"
:data-bs-target="'#' + getRouterLinkRouteFromCopy(copy)" />
</span>
</template>
</li>
</ul>
<!-- End of body text parsing -->
<div
class="package-footer fw-bold caption ms-n4 mt-4"
v-if="this.buttonFooterCopy"
v-html="this.buttonFooterCopy"></div>
</div>
<div v-if="hasPackageDiscount()" class="special-save-box">
<span v-html="this.additionalButtonData.Text"></span>
</div>
<span class="pricing-info">
<div
class="insurance-pricing"
v-if="this.additionalButtonData.isInsuranceSelected">
<span
class="strikethrough-price"
v-if="shouldDisplayStrikeThroughPrice"
v-html="this.additionalButtonData.strikeThroughPrice"></span>
<span v-html="this.buttonAuxillaryCopy"></span>
</div>
<div v-else>
<span v-html="afterpayPrice()"></span>
<span class="afterpay-breakout">
<span>in 4 interest-free payments</span>
<div>
or
<span v-html="truncatedSinglePayment()"></span>
<span
class="strikethrough-price"
v-if="shouldDisplayStrikeThroughPrice"
v-html="this.additionalButtonData.strikeThroughPrice"></span>
in single payment
</div>
</span>
</div>
</span>
</div>
</div>
</baseInputButton>
</template>
<script>
import baseInputButton from "@/digital-components/base-input-button/base-input-button";
import textLink from "@/ux-components/text-link/text-link";
import inputButtonWrapperMixin from "@/mixins/input-button-wrapper-mixin";
import {
getRouterLinkDisplayTextFromCopy,
getRouterLinkRouteFromCopy,
splitCopyOnCMSPlaceHolder,
doesCopyContainTextLink,
} from "@/helpers/cms-content-helper";
export default {
name: "servicePackageRadio",
mixins: [inputButtonWrapperMixin],
components: {
baseInputButton,
textLink,
},
computed: {
arrayOfListItemsFromBodyText() {
return this.getArrayOfListItemsFromRawCmsCopy(this.buttonBodyCopy);
},
shouldDisplayStrikeThroughPrice() {
const displayPrice = this.buttonAuxillaryCopy.substring(
this.buttonAuxillaryCopy.indexOf("$")
);
return displayPrice != this.additionalButtonData.strikeThroughPrice;
},
totalAfterpayPrice() {
return parseFloat(this.buttonAuxillaryCopy.replace("$", ""));
},
},
methods: {
getRouterLinkDisplayTextFromCopy,
getRouterLinkRouteFromCopy,
splitCopyOnCMSPlaceHolder,
doesCopyContainTextLink,
stripUlTagFromCopy(copy) {
const regex = /(?:<ul(?:.*?)>)|(?:<\/ul>)/g;
return copy.replace(regex, "");
},
getArrayOfListItemsFromRawCmsCopy(copy) {
const withoutUlTags = this.stripUlTagFromCopy(copy);
return withoutUlTags
.split(/(?:<li(?:.*?)>)|(?:<\/li>)/g)
.filter((lineItem) => lineItem);
},
afterpayPrice() {
const decimalPrice = this.totalAfterpayPrice;
return "$" + (decimalPrice / 4).toFixed(2);
},
truncatedSinglePayment() {
const decimalPrice = this.totalAfterpayPrice;
return "$" + decimalPrice.toFixed(2);
},
hasPackageDiscount() {
return (
this.additionalButtonData.servicePackageDiscount && this.additionalButtonData.Text
);
},
},
};
</script>
<style lang="scss" scoped>
.package-main {
.package-wrapper {
margin: 1rem 0;
&:first-child {
margin: 1rem 0 0 0;
}
&:last-child {
margin: 1rem 0 0 0;
}
@include media-breakpoint-up(md) {
margin: 1rem 0.5rem 0 0.5rem;
display: flex;
flex-direction: column;
&:first-child {
margin: 1rem 0.5rem 0 0;
}
&:last-child {
margin: 1rem 0 0 0.5rem;
}
}
label {
display: block;
@include media-breakpoint-up(md) {
display: flex;
height: 100%;
}
}
input[type="radio"] {
opacity: 0;
position: absolute;
left: -9999px;
+ .package-label {
display: flex;
align-items: flex-start;
position: relative;
cursor: pointer;
width: 100%;
padding: 0.75rem;
border: 1px solid $gray-300;
box-shadow:
0px 4px 8px -4px rgba(0, 0, 0, 0.15),
0px 4px 24px -8px rgba(0, 0, 0, 0.2);
border-radius: 0.5rem;
overflow: hidden;
min-height: 52px;
max-height: 126px;
@include media-breakpoint-up(md) {
max-height: 500px;
padding-bottom: 4.75rem;
}
@include media-breakpoint-down(md) {
padding-bottom: 4rem;
}
transition: max-height 0.25s ease-in;
&.is-insurance {
padding-bottom: 3rem;
}
&.has-subheader {
& > .package-specs {
display: inline-flex;
@include media-breakpoint-up(md) {
& > div:first-of-type {
display: flex;
}
}
}
}
&.has-package-discount {
min-height: 170px;
}
&:before {
content: "";
position: relative;
top: 5px;
margin-right: 0.5rem;
border-radius: 50%;
border: 1px solid $gray-500;
width: 16px;
height: 16px;
min-width: 16px;
}
&:after {
content: "";
position: absolute;
left: 15px;
top: 20px;
border-radius: 50%;
width: 10px;
height: 10px;
min-width: 10px;
}
}
&:hover {
+ .package-label {
&:before {
border: 1px solid #8e9292;
box-shadow:
0px 0px 0px 4px #9fcee6,
0px 1px 4px rgba(0, 0, 0, 0.2);
}
}
}
&:checked {
+ .package-label {
.package-specs {
max-height: 1000px;
}
}
+ .package-label {
.package-specs {
.hide-when-closed {
display: block;
}
}
}
+ .package-label {
border: 1px solid $blue;
max-height: 500px;
.special-save-box {
background-color: $green-100;
}
}
+ .package-label {
&:before {
box-shadow: 0px 0px 0px 1px $blue;
}
}
+ .package-label {
&:after {
background: $blue;
}
}
}
&:focus {
+ .package-label {
&:before {
border: 2px solid $blue;
}
}
}
}
.package-footer {
color: $red;
font-weight: 600;
}
.package-specs {
display: flex;
flex-direction: column;
width: 100%;
transition: all 0.5s ease;
@include media-breakpoint-up(md) {
max-height: 500px;
}
.row {
align-items: center;
}
p {
font-weight: 600;
display: flex;
justify-content: space-between;
align-items: center;
white-space: nowrap;
&.sub-label {
color: $green;
padding: 0.25rem 0;
text-transform: uppercase;
font-size: 0.75rem;
margin-left: auto;
@include media-breakpoint-up(md) {
background-color: $green-100;
padding: 0.25rem 0.5rem;
border-radius: 0.75rem;
}
}
}
span {
&.service-package-discount {
color: $green;
text-align: end;
font-weight: 600;
font-size: 0.875rem;
display: flex;
justify-content: flex-end;
align-items: center;
align-self: flex-start;
div.strikethrough-discount-price {
text-decoration: line-through;
margin-right: 0.5rem;
font-weight: 400;
font-size: 0.75rem;
line-height: 20px;
color: $gray-550;
@include media-breakpoint-up(md) {
margin-right: 0.5rem;
font-size: 0.75rem;
}
}
div.discount-price {
color: $green;
}
@include media-breakpoint-up(md) {
position: absolute;
bottom: 0.75rem;
left: 0.5rem;
display: flex;
align-items: center;
justify-content: flex-start;
}
}
}
ul {
margin: 1rem 0 0 -5px;
padding: 0;
li {
font-size: 0.875rem;
line-height: 1.714;
color: $gray-550;
a {
line-height: 1.714;
padding: 0;
}
}
}
@keyframes slideaway {
from {
display: block;
}
to {
transform: translateY(40px);
opacity: 0;
}
}
.hide-when-closed {
display: none;
@include media-breakpoint-up(md) {
display: flex;
flex-direction: column;
}
}
}
.discount-text {
height: 2rem;
display: flex;
border-top-right-radius: 0.5rem;
border-top-left-radius: 0.5rem;
background-color: $green;
align-items: center;
justify-content: center;
width: 100%;
color: white;
text-transform: uppercase;
font-size: 0.875rem;
font-weight: 600;
}
.special-save-box {
background-color: $green-100;
display: flex;
flex-direction: row;
margin: 0.25rem auto 0 -1.25rem;
padding: 0.25rem 1rem;
border-radius: 0.25rem;
span {
color: $green;
font-size: 0.875rem;
font-weight: 600;
}
@include media-breakpoint-up(md) {
margin: 1rem auto 0 -1.25rem;
}
}
.pricing-info {
color: $green;
position: absolute;
bottom: 0.75rem;
left: 0;
width: calc(100% - 1.25rem);
margin-left: 1.25rem;
font-weight: 600;
line-height: 20px;
font-size: 1.25rem;
span.strikethrough-price {
text-decoration: line-through;
font-size: 0.75rem;
font-weight: 400;
color: $gray-550;
margin-left: 0.25rem;
}
span.afterpay-breakout {
color: $gray-550;
font-weight: initial;
font-size: 0.875rem;
& > span {
margin-left: 0.25rem;
}
& > div {
font-size: 0.625rem;
& > span:first-of-type {
font-size: 0.75rem;
font-weight: 600;
}
}
}
}
}
}
</style>