Merge branch 'develop' into feature/Digital/SSR-275

This commit is contained in:
Jeremy Zimmerman 2023-03-01 12:26:06 -05:00
commit 0629442570
7 changed files with 298 additions and 26 deletions

View file

@ -1,7 +1,8 @@
const dynamicStrings = {
GLOBAL_STATE: "globalState",
CUSTOM: "custom",
ROUTER_LINK: "routerLink:"
ROUTER_LINK: "routerLink:",
TEXT_LINK: "textLink:"
};
export { dynamicStrings };

View file

@ -43,6 +43,10 @@ const endpoints = {
url: "/parts/api/v1/parts/capability-questions",
method: "GET",
},
GetPartFromCapabilityAnswer: {
url: "/parts/api/v1/parts/part-from-capability-answer",
method: "POST",
},
GetVehicle: {
url: "/vehicle/api/v1/vehicle/lookup",
method: "GET",

View file

@ -181,10 +181,14 @@ function mapStringToState(str) {
return stringBuilder.trimStart();
}
export function doesCopyContainRouterLink(copy) {
export function doesCopyContainRouterLink(copy) {
return copy.includes(this.dynamicStrings.ROUTER_LINK);
}
export function doesCopyContainTextLink(copy) {
return copy.includes(this.dynamicStrings.TEXT_LINK);
}
export function splitCopyOnCMSPlaceHolder(copy) {
// splits copy on { ... } such as {routerlink: ...}
return copy.split(/{(.*?)}/g);

View file

@ -259,7 +259,7 @@ describe("capabilityQuestions.vue", () => {
expect(wrapper.vm.saveCapabilityQuestionAnswers).toHaveBeenCalled;
wrapper.unmount();
});
test("Should call GET_PARTS_OR_QUESTIONS API", async () => {
test("Should call GET_PART_FROM_CAPABILITY_QUESTION_ANSWER API", async () => {
// Arrange
const { wrapper } = setupMocks({});
@ -279,13 +279,12 @@ describe("capabilityQuestions.vue", () => {
],
},
];
useMainStore().getPartsOrQuestions = jest.fn(() => {
wrapper.vm.dispatchStoreAction = jest.fn(() => {
return {
data: {
partsOrQuestions: [],
},
data: [],
};
});
// Act
wrapper.vm.forwardButtonAction();
@ -293,7 +292,7 @@ describe("capabilityQuestions.vue", () => {
await nextTick();
//Assert
expect(wrapper.vm.getPartsOrQuestions).toHaveBeenCalled;
expect(wrapper.vm.getPartFromCapabilityQuestionAnswer).toHaveBeenCalled;
wrapper.unmount();
});

View file

@ -8,11 +8,6 @@
<siteHeader
cmsWidgetName="SiteHeaderWidget"
/>
<vehicleBanner
cmsWidgetName="VehicleBannerWidget"
:displayGenericVehicleImage="false"
class="mb-0"
/>
<div class="fade-on-route-transition sub-container make-tall px-5">
<textBlock
cmsWidgetName="verifyingCoverageStatement"
@ -20,21 +15,49 @@
justifyText="center"
class="mt-4 mb-4"
/>
<textBlock
cmsWidgetName="continueWithSchedulingCopy"
typeStyle="body"
class="mt-0"
<div
v-html="continueWithSchedulingBodyText"
class="mt-0"
/>
<textBlock
cmsWidgetName="whatHappensNextCopy"
class="mt-4 mb-2 fw-bold"
/>
<textBlock
cmsWidgetName="UnverifiedADASNextStepsWidget"
typeStyle="body"
/>
<a href="#!" data-bs-toggle="modal" data-bs-target="#RecalModal" aria-label="Modal window">Recalibration</a>
<modal cmsWidgetName="RecalModal" />
<div v-if="unverifiedADASNextSteps">
<ol>
<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="#!"
data-bs-toggle="modal"
data-bs-target="#RecalModal"
aria-label="Modal window" />
</span>
</template>
</li>
</ol>
</div>
<div
v-if="unverifiedNonADASNextSteps"
v-html="unverifiedNonADASNextStepsBodyText" >
</div>
<div
v-if="unverifiedNonADASRepair"
v-html="unverifiedNonADASRepairBodyText" >
</div>
<recalModal cmsWidgetName="RecalModal" />
<siteFooter
cmsWidgetName="SiteFooterWidget"
:isForwardActionDisabled="!meta.valid"
@ -57,10 +80,20 @@ import siteSubHeader from '@/iss-components/site-sub-header/site-sub-header.vue'
import vehicleBanner from '@/iss-components/vehicle-banner/vehicle-banner.vue';
import textBlock from "@/digital-components/text-block/text-block";
import modal from "@/digital-components/modal/modal";
import textLink from '@/ux-components/text-link/text-link.vue';
import recalModal from '@/layouts/coverage-statement/recal-modal/recal-modal.vue';
// Import Supporting Files
import { fetchCmsContentForPage } from '@/helpers/cms-content-helper';
import {
fetchCmsContentForPage,
doesCopyContainTextLink,
splitCopyOnCMSPlaceHolder,
getRouterLinkDisplayTextFromCopy,
getRouterLinkRouteFromCopy
} from '@/helpers/cms-content-helper';
import { settleAllPromises } from '@/helpers/layout-helper';
import { getDamageString } from '@/helpers/damage-helper.js';
import { useMainStore } from "@/store";
export default {
name: 'coverage-statement',
@ -73,11 +106,49 @@ export default {
vehicleBanner,
textBlock,
modal,
textLink,
recalModal
},
data() {
return {};
return {
isRepair: useMainStore().order.damage.isRepair,
unverifiedADASNextSteps: false,
unverifiedNonADASNextSteps: false,
unverifiedNonADASRepair: false,
};
},
created() {
// check if repair or replace, if repair, display NonADASRepair
if (this.isRepair) {
this.unverifiedNonADASRepair = true;
}
else {
this.displayReplaceCoverageStatement();
}
},
computed: {
continueWithSchedulingBodyText() {
return this.getCmsContent('continueWithSchedulingCopy', 'BodyText');
},
unverifiedADASNextStepsBodyText() {
return this.getCmsContent('UnverifiedADASNextStepsWidget', 'BodyText').replaceAll("{custom:damage}", getDamageString());
},
unverifiedNonADASNextStepsBodyText() {
return this.getCmsContent('UnverifiedNonADASNextStepsWidget', 'BodyText').replaceAll("{custom:damage}", getDamageString());
},
unverifiedNonADASRepairBodyText() {
return this.getCmsContent('UnverifiedNonADASRepairWidget', 'BodyText');
},
splitADASCoverageStatementCopy() {
// Splits content when brackets are found in text so that text can be looped through and text-link can be injected when needed
return this.splitCopyOnCMSPlaceHolder(this.unverifiedADASNextStepsBodyText);
},
arrayOfListItemsFromBodyText() {
return this.getArrayOfListItemsFromRawCmsCopy(this.unverifiedADASNextStepsBodyText);
}
},
async beforeRouteEnter(to, from, next) {
// Call APIs
const cmsContentPromise = fetchCmsContentForPage(to.query.issPage);
// Settle promises and get results
@ -95,6 +166,10 @@ export default {
});
},
methods: {
doesCopyContainTextLink,
splitCopyOnCMSPlaceHolder,
getRouterLinkDisplayTextFromCopy,
getRouterLinkRouteFromCopy,
arePagePrerequisiteValid() {
return true;
},
@ -104,9 +179,35 @@ export default {
*/
this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route);
},
displayReplaceCoverageStatement() {
// get array of parts for vehicle
let parts = useMainStore().order.lineItems.glassParts;
// loop through parts to check for ADAS
for (let part of parts) {
// if ADAS, display ADASNextSteps
if (part.requiresRecalibration) {
this.unverifiedADASNextSteps = true
}
// if non-ADAS, display NonADASNextSteps
else {
this.unverifiedNonADASNextSteps = true
}
}
},
stripOlTagFromCopy(copy) {
const regex = /(?:<ol(?:.*?)>)|(?:<\/ol>)/g;
return copy.replace(regex, "");
},
getArrayOfListItemsFromRawCmsCopy(copy) {
const withoutOlTags = this.stripOlTagFromCopy(copy);
return withoutOlTags
.split(/(?:<li(?:.*?)>)|(?:<\/li>)/g)
.filter((lineItem) => lineItem);
},
},
async forwardButtonAction() {},
resetDependentState() {},
},
};
</script>

View file

@ -0,0 +1,144 @@
<template>
<!-- Modal -->
<div
class="modal fade modal-component"
v-on="{ 'hidden.bs.modal': resetButtonStyle }"
:id="cmsWidgetName"
tabindex="-1"
aria-labelledby="ModalComponentLabel"
aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<button
type="button"
class="btn-close"
data-bs-dismiss="modal"
aria-label="Close"></button>
</div>
<div class="modal-body ps-4 pe-4 pt-4 pb-4">
<h5 class="mb-4 text-center" v-html="ModalHeadline"></h5>
<img :src="ModalImage" class="mw-100 d-flex mx-auto mb-4" alt="" />
<p class="fw-bold mb-2 subheader-text" v-html="ModalSubheadertext"></p>
<p class="mb-0" v-html="ModalBodyText"></p>
<p
class="my-4 caption modal-sub-body"
v-if="ModalSubBodyText"
v-html="ModalSubBodyText"></p>
</div>
<div class="modal-footer px-5 py-4">
<buttonMain
isPrimary
class="w-100"
ref="buttonMain"
suppressLoader
:buttonText="ModalCloseButtonText"
@click-event="buttonClick"
data-bs-dismiss="modal" />
</div>
</div>
</div>
</div>
</template>
<script>
import buttonMain from "@/ux-components/button-main/button-main";
export default {
name: "modal",
props: {
cmsWidgetName: String,
},
computed: {
ModalHeadline() {
return this.getCmsContent(this.cmsWidgetName, "HeaderText");
},
ModalSubheadertext() {
return this.getCmsContent(this.cmsWidgetName, "SubheaderText");
},
ModalBodyText() {
return this.getCmsContent(this.cmsWidgetName, "BodyText");
},
ModalSubBodyText() {
return this.getCmsContent(this.cmsWidgetName, "BodyText2");
},
ModalImage() {
return this.getCmsContent(this.cmsWidgetName, "Image");
},
ModalCloseButtonText() {
return this.getCmsContent(this.cmsWidgetName, "FooterText");
},
},
methods: {
resetButtonStyle() {
this.$refs.buttonMain.resetButtonStyle();
},
},
components: {
buttonMain,
},
};
</script>
<style lang="scss">
.modal {
top: auto;
bottom: 0;
h5,
strong,
.subheader-text {
color: $black;
}
.modal-header {
border-bottom: none;
.btn-close {
background-image: url("data:image/svg+xml,%3Csvg width='16' height='16' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M15.6874 1.82179L9.50889 8L15.6874 14.1782C16.1042 14.595 16.1042 15.2707 15.6874 15.6875C15.4843 15.8907 15.2146 16 14.9328 16C14.6514 16 14.3818 15.8906 14.1787 15.6875L8.00017 9.50934L1.82171 15.6875C1.6182 15.8907 1.34876 16 1.06708 16C0.785733 16 0.516056 15.8906 0.312965 15.6875C-0.10429 15.2706 -0.10429 14.5952 0.312797 14.1784L6.03276 8.45867L6.49146 8L0.312945 1.82177C-0.10429 1.40483 -0.10429 0.729418 0.312797 0.31262C0.728936 -0.104145 1.40489 -0.104051 1.82185 0.31262L7.54152 6.03202L8.00017 6.49065L14.1786 0.312472C14.5955 -0.104107 15.2707 -0.104201 15.6874 0.312452C16.1042 0.729289 16.1042 1.40496 15.6874 1.82179Z' fill='%231574A1'/%3E%3C/svg%3E%0A");
opacity: 1;
}
}
&.modal-component {
.modal-dialog {
max-width: 576px;
margin: 0 auto;
.modal-content {
margin: 0 auto;
box-shadow: 0px 16px 48px -16px rgba(0, 0, 0, 0.25);
border-radius: 1.5rem 1.5rem 0 0;
.modal-body {
.modal-sub-body {
color: $gray-600;
}
ul {
margin-bottom: 0;
}
p {
&:last-child {
margin-bottom: 0;
}
}
}
}
&.modal-dialog-centered {
align-items: flex-end;
min-height: 100%;
}
}
.modal-footer {
border-top: none;
background-color: $gray-100;
box-shadow: 0px -1px 0px rgba(179, 180, 181, 0.3);
button {
margin: 0;
}
}
}
}
body {
.modal-backdrop {
height: 100%;
&.show {
opacity: 0.4;
}
}
}
</style>

View file

@ -395,6 +395,25 @@ export const useMainStore = defineStore({
});
},
getPartFromCapabilityQuestionAnswer(glassLocation) {
const pageData =this.pageData(issPageValues.CAPABILITY_QUESTIONS);
const part = pageData.partsOrQuestions.find((x) => x.glassLocation === glassLocation)
.parts[0];
const capabilityQuestionAnswers = this.order.damage.capabilityQuestionAnswers;
const capabilityQuestionAnswersForPart = capabilityQuestionAnswers.find(
(x) => x.glassLocation === glassLocation
);
return globalMethods.callHttpClient({
method: endpoints.GetPartFromCapabilityAnswer.method,
endpoint: endpoints.GetPartFromCapabilityAnswer.url,
payload: {
part,
capabilityAnswerResults: capabilityQuestionAnswersForPart,
},
});
},
lookupVehicleByVin(vin) {
return globalMethods.callHttpClient({
method: endpoints.LookupVehicleByVin.method,