recal ack modal WIP

This commit is contained in:
Katie Kroell 2026-03-25 14:56:54 -04:00
parent 29f727a342
commit 933449fbdf
5 changed files with 253 additions and 16 deletions

View file

@ -308,6 +308,7 @@ export default {
return (
glassParts !== null
&& !!glassParts.find((part) => part.requiresRecalibration)
&& !!glassParts.find((part) => part.canSafeliteRecalibrate)
);
},
totalServicePrice() {

View file

@ -0,0 +1,118 @@
<template>
<div id="recal-ack-modal-container">
<modal
:ref="ModalName"
:modalId="ModalName"
:footerButtonText="ModalCloseButtonText"
:isButtonDisabled="isButtonDisabled"
@footerButtonEvent="footerButtonClick"
:displayCloseButton="false"
:displayCloseLink="true">
<div class="form-test-invalid">
<h5>{{ ModalHeadline }}</h5>
<div>
<div
class="mb-4"
v-html="ModalBodyText"></div>
<recalAckToggle
class="mb-5"
cmsWidgetName="RecalAckModalToggle" />
<checkBox
ref="recalAcknowledgement"
v-model="acknowledged"
class="mb-2 mt-4"
:class="showAcknowledgementError && ' has-error'"
:validationRules="rules.optionRequired"
checkboxName="recalAcknowledgement"
buttonID="recalAcknowledgement"
:tabIndex="0"
:checkboxLabel="ModalSubBodyText"
:screenReaderOnlyText="ModalSubBodyText"
isRequired />
<div
v-if="showAcknowledgementError"
class="row form-test-error mt-1">
<p>{{ errorMessage }}</p>
</div>
</div>
</div>
</modal>
</div>
</template>
<script>
import modal from "@/digital-components/modal/modal";
import recalAckToggle from '@/layouts/schedule-page/recal-ack-modal/recal-ack-toggle/recal-ack-toggle.vue';
import checkBox from '@/ux-components/checkbox/checkbox.vue';
import globalRules from '@/constants/global-rules';
export default {
name: "recal-ack-modal",
components: {
modal,
checkBox,
recalAckToggle
},
props: {
cmsWidgetName: String,
ackError: String
},
data() {
return {
acknowledged: false,
showAcknowledgementError: false,
rules: {
optionRequired: globalRules.OPTION_REQUIRED
}
};
},
computed: {
ModalName() {
return this.cmsWidgetName;
},
ModalHeadline() {
return this.getCmsContent(this.cmsWidgetName, 'HeaderText');
},
ModalBodyText() {
return this.getCmsContent(this.cmsWidgetName, 'BodyText');
},
ModalSubBodyText() {
return this.getCmsContent(this.cmsWidgetName, 'BodyText2');
},
ModalCloseButtonText() {
return this.getCmsContent(this.cmsWidgetName, 'FooterText');
},
isButtonDisabled() {
return !this.acknowledged;
},
errorMessage() {
if (this.showAcknowledgementError) {
return this.ackError;
}
return '';
},
},
watch: {
acknowledged() {
this.showAcknowledgementError = false;
this.emit('recalAcknowledged', this.acknowledged);
}
},
methods: {
openModal() {
this.$refs[this.ModalName].openModal();
},
footerButtonClick() {
if (this.acknowledged) {
this.$refs[this.ModalName]?.closeModal();
} else {
this.showAcknowledgementError = true;
}
}
},
};
</script>
<style lang="scss">
</style>

View file

@ -0,0 +1,109 @@
<template>
<div class="recal-ack-toggle mt-2 mb-3">
<textLink
id="moreDetails"
class="d-inline-block"
linkType="text"
href="#!"
:text="textForToggle"
@click="handleClickToggle" />
<div
class="recal-ack-toggle d-inline-block"
:class="[isDetailVisible ? 'active' : '']"
@click="handleClickToggle"></div>
<Transition>
<div
v-show="isDetailVisible"
id="recal-ack-detail-wrapper"
class="mt-2">
<div
id="recal-ack-detail-content"
class="mb-2"
v-html="detailContent" />
</div>
</Transition>
</div>
</template>
<script>
import textLink from '@/ux-components/text-link/text-link.vue';
export default {
name: 'recal-ack-toggle',
components: {
textLink
},
props: {
cmsWidgetName: String
},
data() {
return {
isDetailVisible: false
};
},
computed: {
textForToggle() {
return this.getCmsContent(this.cmsWidgetName, 'HeaderText');
},
detailContent() {
return this.getCmsContent(this.cmsWidgetName, 'BodyText');
}
},
methods: {
handleClickToggle() {
this.isDetailVisible = !this.isDetailVisible;
}
}
};
</script>
<style lang="scss" scoped>
@import "@/styles/ux-variables-svg-strings.scss";
.recal-ack-toggle {
.recal-ack-toggle {
cursor: pointer;
&::after {
content: "";
transition: all 0.5s ease;
background-repeat: no-repeat;
margin-left: 0.5rem;
width: 16px;
height: 9px;
display: inline-block;
}
&.active::after {
transform: rotate(180deg);
}
a {
font-size: 0.875rem;
}
}
}
#recal-ack-detail-wrapper {
// START - Vue Transition
&.v-enter-active,
&.v-leave-active {
transition: opacity 250ms ease-in;
}
&.v-enter-from,
&.v-leave-to {
opacity: 0;
}
// END - Vue Transition
img {
width: 100%;
}
}
#recal-ack-detail-content {
font-size: 1rem;
p {
font-size: 1rem;
margin-bottom: 0;
}
ol {
margin-top: 0.5rem;
margin-bottom: 0;
}
}
</style>

View file

@ -46,7 +46,7 @@
:customSelectableDatesCallback="getAvailableDatesMethod"
@dateSelected="dateSelectedFromPicker"
@timeSlotSelected="timeSlotSelectedFromPicker" />
<modal
<!-- <modal
ref="recalAckModal"
class="recal-ack-modal"
:headerText="recalModalContent.headerText"
@ -58,7 +58,7 @@
<div
class="recal-ack-modal-body"
v-html="recalModalContent.bodyText"></div>
</modal>
</modal> -->
</div>
</div>
<div class="site-footer-container iss-heritage-content-container-width">
@ -73,6 +73,11 @@
</div>
</div>
</div>
<recalAckModal
ref="recalAckModal"
cmsWidgetName="RecalAckModalWidget"
:ackError="ackError"
/>
</Form>
</template>
<script>
@ -84,6 +89,7 @@ import datePicker from '@/digital-components/date-picker/date-picker.vue';
import serviceLocation from '@/layouts/schedule-page/service-location/service-location.vue';
import siteFooter from '@/iss-components/site-footer/site-footer.vue';
import modal from '@/digital-components/modal/modal.vue';
import recalAckModal from '@/layouts/schedule-page/recal-ack-modal/recal-ack-modal.vue';
// Supporting files
import {
@ -110,7 +116,8 @@ import {
import { Form } from 'vee-validate';
import BaseFormMixin from '@/mixins/base-form-mixin.js';
import { useMainStore } from '@/store';
import { containsRecalParts } from '@/helpers/recal-helper';
import { containsRecalParts, anyPartWithRequiresRecalFlag } from '@/helpers/recal-helper';
import errorMessages from '@/constants/error-messages';
// Define constants
const TIME_SLOTS_CALL_DAYS_LIMIT = 15;
@ -235,7 +242,7 @@ export default {
serviceLocation,
siteFooter,
Form,
modal
recalAckModal
},
mixins: [BaseFormMixin],
async beforeRouteEnter(to, from, next) {
@ -410,7 +417,7 @@ export default {
supportingItems() {
return useMainStore().lineItems.supportingItems;
},
displayRecalAckModalwidget() {
displayRecalAckModalWidget() {
// if any of the glass parts contain an item with the requiresRecalibration flag set to true
// and the order does not contain a recalibration part, show the recalibration acknowledgement modal
const containsRecalPart = containsRecalParts(useMainStore().order.lineItems);
@ -421,16 +428,9 @@ export default {
}
return false;
},
recalModalContent() {
const widgetName = 'RecalAckModalWidget';
return {
headerText: this.getCmsContent(widgetName, widgetFields.CONTENT_GROUP_WIDGET.HEADER_TEXT),
subHeaderText: this.getCmsContent(widgetName, widgetFields.CONTENT_GROUP_WIDGET.SUBHEADER_TEXT),
bodyText: this.getCmsContent(widgetName, widgetFields.CONTENT_GROUP_WIDGET.BODY_TEXT),
bodyText2: this.getCmsContent(widgetName, widgetFields.CONTENT_GROUP_WIDGET.BODY_TEXT_2),
footerButtonText: this.getCmsContent(widgetName, widgetFields.CONTENT_GROUP_WIDGET.FOOTER_TEXT)
};
},
ackError() {
return errorMessages.ACKNOWLEDGEMENT_REQUIRED;
}
},
watch: {
inShopDatesData(newData) {
@ -761,6 +761,12 @@ export default {
this.showDatePickerError = false;
},
async forwardButtonAction() {
if (this.displayRecalAckModal) {
showIssLoadingModal(false);
this.$refs.recalAckModal.openModal();
return;
}
if (!this.isFormValid) {
this.showDatePickerError = true;
return;
@ -777,6 +783,9 @@ export default {
this.navigationScenarios.CLICKED_FORWARD,
this.$route
);
},
updateRecalAcknowledged(isAcknowledged) {
this.displayRecalAckModal = !isAcknowledged;
}
}
};

View file

@ -1,4 +1,4 @@
process.env.VUE_APP_CONSUMER_CF_DISTRO = 'https://digitalapi.dev.safelite.io';
process.env.VUE_APP_CONSUMER_CF_DISTRO = 'https://digitalapi.test.safelite.io';
process.env.VUE_APP_CURRENT_ENVIRONMENT = 'Localhost';
process.env.VUE_APP_CUSTOMER_PORTAL_URL = 'https://myaccountdev.safelite.com/';
process.env.VUE_APP_GOOGLE_PLACES_API_KEY = 'AIzaSyCuLhQcDdZTTb4JzpUFms1OCch2dk5lHF0';