INSR-7760: More HOP updates, afterpay button handling

This commit is contained in:
Alex Humphries 2026-02-25 17:24:05 -05:00
parent bc0031b95b
commit 9d3ce7a90f
8 changed files with 182 additions and 35 deletions

View file

@ -32,6 +32,7 @@ body span {
body a { body a {
color: #0070D1; color: #0070D1;
text-decoration: none; text-decoration: none;
font-weight: 500;
} }
body a:hover { body a:hover {
text-decoration: underline; text-decoration: underline;
@ -175,13 +176,18 @@ body .has-error select {
border-color: #db0020; border-color: #db0020;
} }
body .has-error select { body .has-error select {
background: url(/icons/select-error.png) no-repeat 98% #ffffff; background-image: url(/icons/select-error.png);
background-size: 10px 17px;
appearance: none;
} }
body .has-error small { body .has-error small {
margin-top: 4px; margin-bottom: 0.3125rem;
color: #d4281c; color: #db0020;
}
body .has-success input,
body .has-success select {
border-color: #0c7e47;
}
body .has-success select {
background-image: url(/icons/select-error.png);
} }
body .cc-error-container #alert-message { body .cc-error-container #alert-message {
background-color: #fcbfbb; background-color: #fcbfbb;

View file

@ -88,7 +88,7 @@
} }
.buy-backdrop { .buy-backdrop {
background-color: rgba(0, 0, 0, 0.3) !important; background-color: rgba(0, 0, 0, 0.8) !important;
} }
.buy-backdrop #afterpay__iframe-checkout-container { .buy-backdrop #afterpay__iframe-checkout-container {
width: 100%; width: 100%;
@ -97,10 +97,6 @@
display: none; display: none;
} }
.buy-container-closer {
display: none;
}
@keyframes rotate { @keyframes rotate {
0% { 0% {
transform: rotate(0); transform: rotate(0);

View file

@ -36,6 +36,7 @@ body {
a { a {
color: #0070D1; color: #0070D1;
text-decoration: none; text-decoration: none;
font-weight: 500;
&:hover { &:hover {
text-decoration: underline; text-decoration: underline;
color: #167cac; color: #167cac;
@ -200,13 +201,22 @@ body {
border-color: #db0020; border-color: #db0020;
} }
select { select {
background: url(/icons/select-error.png) no-repeat 98% #ffffff;; background-image: url(/icons/select-error.png);
background-size: 10px 17px;
appearance: none;
} }
small { small {
margin-top: 4px; margin-bottom: .3125rem;
color: #d4281c; color: #db0020;
}
}
.has-success {
input,
select {
border-color: #0c7e47;
}
select {
background-image: url(/icons/select-error.png);
} }
} }

View file

@ -93,7 +93,7 @@
// AfterPay Styles // AfterPay Styles
.buy-backdrop { .buy-backdrop {
background-color: rgba(0,0,0,.3)!important; background-color: rgba(0,0,0,.8)!important;
#afterpay__iframe-checkout-container { #afterpay__iframe-checkout-container {
width: 100%; width: 100%;
.style_closeWrapper__lIHJe { .style_closeWrapper__lIHJe {
@ -102,10 +102,6 @@
} }
} }
.buy-container-closer {
display: none;
}
// Modal Spinner // Modal Spinner
@keyframes rotate { @keyframes rotate {

View file

@ -10,18 +10,22 @@
<alert <alert
v-if="displayPayInAdvanceCreditCardAlert" v-if="displayPayInAdvanceCreditCardAlert"
name="payInAdvanceCreditCardErrorAlert" name="payInAdvanceCreditCardErrorAlert"
class="my-4" class="pay-in-advance-alert"
cmsWidgetName="PayInAdvanceCreditCardErrorAlertWidget" cmsWidgetName="PayInAdvanceCreditCardErrorAlertWidget"
alertClass="alert-danger" alertClass="alert-danger"
:isDismissible="false" :isDismissible="false"
:isCollapsible="true"
:preventDefaultOnClick="true"
@textLinkClicked="paymentFailedPayLater" /> @textLinkClicked="paymentFailedPayLater" />
<alert <alert
v-if="displayPayInAdvanceAfterPayAlert" v-if="displayPayInAdvanceAfterPayAlert"
name="payInAdvanceAfterPayErrorAlert" name="payInAdvanceAfterPayErrorAlert"
class="my-4" class="pay-in-advance-alert"
cmsWidgetName="PayInAdvanceAfterpayErrorAlertWidget" cmsWidgetName="PayInAdvanceAfterpayErrorAlertWidget"
alertClass="alert-danger" alertClass="alert-danger"
:isDismissible="false" :isDismissible="false"
:isCollapsible="true"
:preventDefaultOnClick="true"
@textLinkClicked="paymentFailedPayLater" /> @textLinkClicked="paymentFailedPayLater" />
<iframe <iframe
id="card-frame" id="card-frame"
@ -31,18 +35,44 @@
name="card-frame" name="card-frame"
seamless seamless
scrolling="no" scrolling="no"
allow="payment *; microphone *; geolocation *;"> allow="payment *; microphone *; geolocation *; loopback-network *;">
</iframe> </iframe>
</div> </div>
<div class="col-lg-4 col-md-10 col-xs-12"> <div class="col-lg-4 col-md-10 col-xs-12">
<div> <div class="order-summary-panel">
<div class="cart-title">{{ cartTitle }}</div>
<cartDropdown <cartDropdown
ref="cart" ref="cart"
class="cart-dropdown-component"
:readOnly="true" :readOnly="true"
:showAsPaid="false" :showAsPaid="false"
recyclingModalCmsWidgetName="RecycleModal" recyclingModalCmsWidgetName="RecycleModal"
servicePackageTitleWidgetName="ServicePackageTitle" /> servicePackageTitleWidgetName="ServicePackageTitle" />
</div> </div>
<div class="d-none d-lg-block">
<buttonMain
v-if="!afterpayModalOpen"
class="submit-payment-button"
variant="success"
:preventDefaultOnClick="true"
:buttonText="submitPaymentText"
@clickEvent="submitPaymentAction"
/>
</div>
<div
v-if="!afterpayModalOpen"
class="switch-payment-container">
<div class="switch-payment-text">{{ switchPaymentText }}</div>
<button
class="afterpay-button"
@click.prevent="switchToAfterPay">
{{ afterpayButtonText }}
<img
class="afterpay-logo"
:src="afterpayLogo"
alt="Afterpay Logo" />
</button>
</div>
</div> </div>
</div> </div>
<div class="col-xs-12"> <div class="col-xs-12">
@ -409,6 +439,9 @@ import cartDropdown from '@/iss-components/cart-dropdown/cart-dropdown.vue';
import { formatAmountInDollars } from '@/helpers/text-helper'; import { formatAmountInDollars } from '@/helpers/text-helper';
import showIssLoadingModal from '@/helpers/loading-modal-helper'; import showIssLoadingModal from '@/helpers/loading-modal-helper';
import { supportsApplePay } from '@/helpers/browser-helper'; import { supportsApplePay } from '@/helpers/browser-helper';
import buttonMain from '@/ux-components/button-main/button-main.vue';
import { submitWorkOrder } from '@/helpers/order-helper';
import submitType from '@/constants/submit-type';
export default { export default {
name: 'payment-page', name: 'payment-page',
@ -417,7 +450,8 @@ export default {
siteHeader, siteHeader,
siteFooter, siteFooter,
Form, Form,
alert alert,
buttonMain
}, },
directives: { directives: {
resize: { resize: {
@ -434,7 +468,8 @@ export default {
mixins: [BaseFormMixin], mixins: [BaseFormMixin],
async beforeRouteEnter(to, from, next) { async beforeRouteEnter(to, from, next) {
const paymentType = useMainStore().order.payment.paymentMethod; const paymentType = useMainStore().order.payment.paymentMethod;
if (paymentType === paymentMethods.PAYPAL) { const alertToDisplay = to.query[queryStrings.DISPLAY_PAY_IN_ADVANCE_ALERT];
if (paymentType === paymentMethods.PAYPAL || alertToDisplay) {
useMainStore().savePaymentMethodChoice(paymentMethods.CREDIT_CARD); useMainStore().savePaymentMethodChoice(paymentMethods.CREDIT_CARD);
} }
@ -499,7 +534,8 @@ export default {
totalAmount: this.getAmountDue(), totalAmount: this.getAmountDue(),
displayAmount: this.getDisplayAmountDue(), displayAmount: this.getDisplayAmountDue(),
payInAdvanceLineItems: '', payInAdvanceLineItems: '',
shouldBlockInteraction: false shouldBlockInteraction: false,
afterpayModalOpen: false
}; };
}, },
computed: { computed: {
@ -537,6 +573,21 @@ export default {
} }
return ''; return '';
}, },
cartTitle() {
return this.getCmsContent('OrderSummaryTextWidget', widgetFields.TEXT_BLOCK_WIDGET.TEXT);
},
switchPaymentText() {
return this.getCmsContent('SwitchPaymentTextWidget', widgetFields.TEXT_BLOCK_WIDGET.TEXT);
},
submitPaymentText() {
return this.getCmsContent('SiteFooterWidget', widgetFields.FOOTER_WIDGET.FORWARD_BUTTON_TEXT);
},
afterpayButtonText() {
return this.getCmsContent('AfterpayButtonWidget', widgetFields.CONTENT_GROUP_WIDGET.HEADER_TEXT);
},
afterpayLogo() {
return this.getCmsContent('AfterpayButtonWidget', widgetFields.CONTENT_GROUP_WIDGET.IMAGE);
},
isPaypal() { isPaypal() {
return this.paymentType === paymentMethods.PAYPAL; return this.paymentType === paymentMethods.PAYPAL;
}, },
@ -720,6 +771,7 @@ export default {
} }
if (this.isAfterPay) { if (this.isAfterPay) {
this.afterpayModalOpen = true;
iframe.contentWindow.postMessage('afterpay', '*'); iframe.contentWindow.postMessage('afterpay', '*');
} else { } else {
iframe.contentWindow.postMessage('CreditCardChosen', '*'); iframe.contentWindow.postMessage('CreditCardChosen', '*');
@ -730,12 +782,19 @@ export default {
}); });
}, },
handleIFrameContentWindowMessage(event) { handleIFrameContentWindowMessage(event) {
console.log('Received message from iframe:', event.data);
const iframe = this.$refs.paymentFrame;
if (typeof event.data !== 'string') { if (typeof event.data !== 'string') {
return; return;
} }
if (event.data.includes('afterpayClosed')) { if (event.data.includes('afterpayClosed')) {
this.backButtonAction(); if (this.isAfterPay) {
this.backButtonAction();
} else {
iframe.contentWindow.postMessage('CreditCardChosen', '*');
this.afterpayModalOpen = false;
}
} }
if (useMainStore().order.payment.paymentMethod !== paymentMethods.PayNow) { if (useMainStore().order.payment.paymentMethod !== paymentMethods.PayNow) {
@ -776,7 +835,10 @@ export default {
this.$route.query[queryStrings.DISPLAY_PAY_IN_ADVANCE_ALERT] === payMethod this.$route.query[queryStrings.DISPLAY_PAY_IN_ADVANCE_ALERT] === payMethod
); );
}, },
paymentFailedPayLater() { async paymentFailedPayLater() {
this.showIssLoadingModal(true);
this.mainStore.savePaymentMethodChoice(paymentMethods.PAY_AT_TIME_OF_SERVICE);
await submitWorkOrder({ submitType: submitType.SAFELITE });
this.$router.navigate( this.$router.navigate(
this.navigationScenarios.CLICKED_FORWARD, this.navigationScenarios.CLICKED_FORWARD,
this.$route this.$route
@ -792,6 +854,15 @@ export default {
this.$route this.$route
); );
}, },
submitPaymentAction() {
const iframe = this.$refs.paymentFrame;
iframe.contentWindow.postMessage('submitPayment', '*');
},
switchToAfterPay() {
const iframe = this.$refs.paymentFrame;
this.afterpayModalOpen = true;
iframe.contentWindow.postMessage('afterpay-switch', '*');
},
showIssLoadingModal showIssLoadingModal
} }
}; };
@ -808,8 +879,66 @@ export default {
} }
} }
.order-summary-panel {
margin-bottom: 1.375rem;
}
.cart-title {
font-size: 1rem;
font-weight: 500;
margin-top: 1.25rem;
margin-bottom: .625rem;
}
.cart-dropdown-component:deep(*) {
font-size: .8125rem;
}
.submit-payment-button {
width: 100%;
font-weight: $font-weight-normal;
}
.switch-payment-container {
padding-top: 1.25rem
}
.switch-payment-text {
padding-top: 1rem;
padding-bottom: .625rem;
border-top: 1px solid #f0f1f2
}
.footer-component:deep(.footer) { .footer-component:deep(.footer) {
margin: 0; margin: 1.25rem 0 0 0;
}
.afterpay-button {
width: 100%;
height: 2.875rem;
border: solid 1px #cacbcc;
border-radius: 1.375rem;
background-color: white;
box-shadow: 0 1px 5px 0 rgba(0, 0, 0, 0.2);
color: $darker-gray;
font-weight: 400;
margin-bottom: 1.25rem;
padding: .5rem 1rem;
&:focus {
background: $background-color-selected;
}
&:active {
box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
}
}
.pay-in-advance-alert {
width: 80%;
@media (max-width: 35rem) {
width: 100%;
}
} }
} }

View file

@ -106,6 +106,7 @@
:text="getRouterLinkDisplayTextFromCopy(copy)" :text="getRouterLinkDisplayTextFromCopy(copy)"
href="#!" href="#!"
:data-bs-target="'#' + getRouterLinkRouteFromCopy(copy)" :data-bs-target="'#' + getRouterLinkRouteFromCopy(copy)"
:preventDefaultOnClick="preventDefaultOnClick"
@clickEvent="$emit('textLinkClicked', getRouterLinkRouteFromCopy(copy))" /> @clickEvent="$emit('textLinkClicked', getRouterLinkRouteFromCopy(copy))" />
</span> </span>
<span <span
@ -172,7 +173,8 @@ export default {
startCollapsed: { startCollapsed: {
type: Boolean, type: Boolean,
default: false default: false
} },
preventDefaultOnClick: { type: Boolean, required: false, default: false }
}, },
emits: ['textLinkClicked'], emits: ['textLinkClicked'],
data() { data() {

View file

@ -21,7 +21,8 @@ export default {
isDisabled: Boolean, isDisabled: Boolean,
isOutline: Boolean, isOutline: Boolean,
pushToGA: Boolean, pushToGA: Boolean,
canOverride: { type: Boolean, required: false, default: true } canOverride: { type: Boolean, required: false, default: true },
preventDefaultOnClick: { type: Boolean, required: false, default: false }
}, },
emits: ['click-event'], emits: ['click-event'],
computed: { computed: {
@ -34,8 +35,11 @@ export default {
} }
}, },
methods: { methods: {
clicked() { clicked(e) {
if (!this.isDisabled) { if (!this.isDisabled) {
if (this.preventDefaultOnClick) {
e.preventDefault();
}
if (this.pushToGA) { if (this.pushToGA) {
this.pushEventToGA( this.pushEventToGA(
this.$route.query[this.queryStrings.ISS_PAGE], this.$route.query[this.queryStrings.ISS_PAGE],

View file

@ -43,11 +43,15 @@ export default {
text: String, text: String,
href: { href: {
type: String type: String
} },
preventDefaultOnClick: { type: Boolean, required: false, default: false }
}, },
emits: ['click-event'], emits: ['click-event'],
methods: { methods: {
handleClick() { handleClick(e) {
if (this.preventDefaultOnClick) {
e.preventDefault();
}
this.pushEventToGA( this.pushEventToGA(
this.$route.query[this.queryStrings.ISS_PAGE], this.$route.query[this.queryStrings.ISS_PAGE],
this.GaActions.CLICKED, this.GaActions.CLICKED,