CASH-151 add 'as low as' to quote page.
used an experiment version of service-package-radio
This commit is contained in:
CarlNation 2025-02-16 14:10:13 -05:00
parent 86812eefe2
commit 8123cae1a2
5 changed files with 472 additions and 4 deletions

View file

@ -18,6 +18,7 @@ const experimentSettings = {
INSURANCE_TAB_TO_DISPLAY_THRESHOLD_INTERNAL: "NextGen_InternalInsuranceTabDisplayThreshold",
INSURANCE_TAB_TO_DISPLAY_THRESHOLD_EXTERNAL: "NextGen_ExternalInsuranceTabDisplayThreshold",
DISPLAY_MSR: "DisplayMSR",
PRICING_BY_DAY: "DisplayPricingByDay",
};
const experimentTriggers = {

View file

@ -55,6 +55,7 @@ https://safelite.atlassian.net/wiki/spaces/DC/pages/76644418/Button+Question+Com
:additionalButtonData="answer.additionalButtonData"
:lastValuePushedToGa="lastValuePushedToGa"
:setLastValuePushedToGa="setLastValuePushedToGa"
:isInsuranceSelected="isInsuranceSelected"
:suppressError="suppressError"
@buttonEvent="handleButtonEvent"
v-model="selectedValues" />
@ -139,6 +140,7 @@ export default {
type: Boolean,
default: false,
},
isInsuranceSelected: Boolean,
},
setup(props) {
const propsClone = Object.assign({}, props);

View file

@ -0,0 +1,455 @@
<template>
<baseInputButton v-bind="$props" @buttonClicked="handleAnswerChange" v-model="selectedValue">
<div
class="package-label"
:class="[this.buttonLabelSubCopy ? 'has-subheader' : '',
this.isInsuranceSelected ? 'pricing-by-day-pkg-lbl-ins' : 'pricing-by-day-pkg-lbl']"
for="testradio">
<div class="package-specs">
<div v-if="this.additionalButtonData.servicePackageDiscount" class="row">
<div class="col md-6">
<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>
<span class="col md-6 service-package-discount-pricing-by-day">
<div>
<span v-if="!isInsuranceSelected" class="caption pricing-by-day">as low as</span>
<div class="strikethrough-discount-price"
v-if="shouldDisplayStrikeThroughPrice"
v-html="this.additionalButtonData.strikeThroughPrice"></div>
<div class="discount-price" v-html="getDisplayPrice"></div>
</div>
</span>
</div>
<div v-else>
<p class="m-0">
<span v-html="this.buttonLabel"></span>
<span class="pricing-info-pricing-by-day">
<span v-if="!isInsuranceSelected" class="caption pricing-by-day">as low as</span>
<span
class="strikethrough-price"
v-if="shouldDisplayStrikeThroughPrice"
v-html="this.additionalButtonData.strikeThroughPrice"></span>
<span v-html="getDisplayPrice"></span>
</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="
this.additionalButtonData.servicePackageDiscount &&
this.additionalButtonData.Text
"
class="special-save-box">
<span v-html="this.additionalButtonData.Text"></span>
</div>
</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";
import experimentMixin from "@/mixins/experiment-mixin";
import { experimentSettings } from "@/constants/experiments";
import store from "@/store";
export default {
name: "servicePackageRadio",
mixins: [inputButtonWrapperMixin],
props: {
isInsuranceSelected: Boolean,
},
components: {
baseInputButton,
textLink,
},
computed: {
arrayOfListItemsFromBodyText() {
return this.getArrayOfListItemsFromRawCmsCopy(this.buttonBodyCopy);
},
shouldDisplayStrikeThroughPrice() {
const displayPrice = this.buttonAuxillaryCopy.substring(
this.buttonAuxillaryCopy.indexOf("$")
);
return displayPrice != this.additionalButtonData.strikeThroughPrice;
},
getDisplayPrice() {
return this.buttonAuxillaryCopy.split(".")[0];
},
},
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);
},
},
};
</script>
<style lang="scss">
.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.pricing-by-day-pkg-lbl {
/* Copying a component for an experiment will cause duplication of css classes.
This can cause problems if you want change the value of something only for the experiment
version of the component. So added a more specific selector for the parts I want to change
If this experiment wins, move this to package-label ::before/::after below
*/
&:before {
top: 15px;
}
&:after {
top: 30px;
}
}
+ .package-label.pricing-by-day-pkg-lbl-ins {
&:before {
top: 5px;
}
&:after {
top: 20px;
}
}
+ .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: 3rem;
}
transition: max-height 0.25s ease-in;
&.has-subheader {
min-height: 72px;
}
&:before {
content: "";
position: relative;
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;
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 {
background-color: $blue-100;
border: 1px solid $blue;
max-height: 500px;
.special-save-box {
background-color: $green-200;
}
}
+ .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;
}
span.pricing-by-day {
color: #4d5151;
display: block;
white-space: nowrap;
}
.row {
align-items: center;
}
p {
font-weight: 600;
display: flex;
justify-content: space-between;
align-items: center;
white-space: nowrap;
span {
&.pricing-info-pricing-by-day {
text-align: end;
color: $green;
font-size: 0.875rem;
span.strikethrough-price {
text-decoration: line-through;
font-size: 0.75rem;
font-weight: 400;
color: $gray-550;
padding-right: 8px;
}
}
}
&.sub-label {
color: $green;
text-transform: uppercase;
font-size: 0.75rem;
}
}
span {
&.service-package-discount-pricing-by-day {
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 {
display:inline;
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;
display:inline;
}
}
}
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;
letter-spacing: 0.75px;
}
.special-save-box {
background-color: $green-100;
display: flex;
flex-direction: row;
margin: auto;
margin-left: -1.25rem;
padding: 0.5rem 1rem;
border-radius: 0.25rem;
margin-top: 0.5rem;
span {
color: $green;
font-size: 0.875rem;
font-weight: 600;
}
}
}
}
</style>

View file

@ -387,8 +387,7 @@ export default {
// prettier-ignore
{
const log = getQuerystringParameter(queryStrings.LOG);
if (log === "true") {
if (store.getters.applicationUser.loggingOption) {
console.log("------------- quote.vue thresholds start -----------------");
console.log(new Date() + " isExternalParameter: " + isExternalParameter);
console.log(new Date() + " internalThreshold: " + internalThreshold);

View file

@ -4,11 +4,12 @@
:answers="servicePackageAnswers"
:groupName="groupName"
buttonTypeString="servicePackageRadio"
:buttonTypeObject="servicePackageRadio"
:buttonTypeObject="hasPricingByDay() ? servicePackageRadioExperiment : servicePackageRadio"
v-model="selectedPackageName"
:validationRules="validationRules"
:isRequired="isRequired"
:logDisplayedValuesEvent="true" />
:logDisplayedValuesEvent="true"
:isInsuranceSelected="isInsuranceSelected" />
</template>
<script>
@ -16,6 +17,7 @@ import buttonQuestion from "@/digital-components/button-question/button-question
import baseMixin from "@/mixins/base-mixin.js";
import { processIfStatements } from "@/helpers/cms-content-helper";
import servicePackageRadio from "./service-package-radio/service-package-radio";
import servicePackageRadioExperiment from "@/experiment-components/service-package-radio";
import { partTypeStrings } from "@/constants/part-type-strings";
import { packageNames } from "@/constants/package-names";
import {
@ -58,6 +60,7 @@ export default {
data() {
return {
servicePackageRadio: servicePackageRadio,
servicePackageRadioExperiment: servicePackageRadioExperiment,
selectedPackageName: null,
};
},
@ -223,6 +226,14 @@ export default {
},
methods: {
processIfStatements,
hasPricingByDay() {
return (
experimentMixin.methods.hasSettingEqualTo(
experimentSettings.PRICING_BY_DAY,
"true"
) && !this.isInsuranceSelected
);
},
getHeaderTextFromCms(cmsWidgetName) {
return this.getCmsContent(cmsWidgetName, "HeaderText");
},