Merge branch 'develop' into feature/Digital/SSR-275
This commit is contained in:
commit
95f0507747
11 changed files with 189 additions and 107 deletions
|
|
@ -2,7 +2,7 @@ const dynamicStrings = {
|
|||
GLOBAL_STATE: "globalState",
|
||||
CUSTOM: "custom",
|
||||
ROUTER_LINK: "routerLink:",
|
||||
TEXT_LINK: "textLink:"
|
||||
MODAL_LINK: "modalLink"
|
||||
};
|
||||
|
||||
export { dynamicStrings };
|
||||
|
|
@ -98,6 +98,10 @@ const endpoints = {
|
|||
url: "/clientauth/api/v1/clientauth/validateClientTag",
|
||||
method: "GET",
|
||||
},
|
||||
IsVinbyAddressPermissible:{
|
||||
url:"/vehicle/api/v1/vehicle/is-vin-by-address-permissible",
|
||||
method:"Get",
|
||||
}
|
||||
};
|
||||
|
||||
export { endpoints };
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
const VIN_LOOKUP_METHODS = Object.freeze({
|
||||
manualVin: 'ManualVin',
|
||||
licensePlate: 'LicensePlate',
|
||||
homeAddress: 'HomeAddress',
|
||||
const vinLookupMethodSelections = Object.freeze({
|
||||
MANUALVIN: 'ManualVin',
|
||||
LICENSEPLATE: 'LicensePlate',
|
||||
HOMEADDRESS: 'HomeAddress',
|
||||
});
|
||||
|
||||
export default VIN_LOOKUP_METHODS;
|
||||
export {vinLookupMethodSelections};
|
||||
|
|
|
|||
|
|
@ -229,7 +229,7 @@ export default {
|
|||
|
||||
.overflow-scroll {
|
||||
// Height will be determined by overall height of content above list
|
||||
height: calc(100% - 314px);
|
||||
height: calc(100% - 383px);
|
||||
overflow-x: hidden !important;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -124,6 +124,9 @@ function processWidgetItemForReplacement(widgetModel, key) {
|
|||
if (widgetModel[key].includes(dynamicStrings.GLOBAL_STATE)) {
|
||||
widgetModel[key] = mapStringToState(widgetModel[key]);
|
||||
}
|
||||
if (widgetModel[key].includes(dynamicStrings.MODAL_LINK)) {
|
||||
widgetModel[key] = mapStringToModal(widgetModel[key]);
|
||||
}
|
||||
return widgetModel[key];
|
||||
}
|
||||
|
||||
|
|
@ -143,6 +146,18 @@ function processWidgetItemForReplacement(widgetModel, key) {
|
|||
return widgetModel[key];
|
||||
}
|
||||
|
||||
function mapStringToModal(str) {
|
||||
let startIndex = str.indexOf("{" + dynamicStrings.MODAL_LINK);
|
||||
let linkToReplace = str.substring(startIndex, str.length);
|
||||
linkToReplace = linkToReplace.substring(0, linkToReplace.indexOf("}") + 1);
|
||||
|
||||
let params = linkToReplace.substring((dynamicStrings.MODAL_LINK).length + 2, linkToReplace.length -1)
|
||||
let splitParams = params.split(",");
|
||||
let bodyText = '<a href="#!" data-bs-toggle="modal" data-bs-target="#' + splitParams[0] + '" aria-label="Modal window">' + splitParams[1] +'</a>'
|
||||
|
||||
return str.replace(linkToReplace, bodyText);
|
||||
}
|
||||
|
||||
// Function to convert a string, into a matching global state item.
|
||||
function mapStringToState(str) {
|
||||
// Pull all matches out of the string.
|
||||
|
|
@ -185,10 +200,6 @@ 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);
|
||||
|
|
|
|||
|
|
@ -18,45 +18,12 @@
|
|||
<div
|
||||
v-html="continueWithSchedulingBodyText"
|
||||
class="mt-0"
|
||||
/>
|
||||
></div>
|
||||
<textBlock
|
||||
cmsWidgetName="whatHappensNextCopy"
|
||||
class="mt-4 mb-2 fw-bold"
|
||||
/>
|
||||
<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>
|
||||
<div v-html="bodyText"></div>
|
||||
<recalModal cmsWidgetName="RecalModal" />
|
||||
<siteFooter
|
||||
cmsWidgetName="SiteFooterWidget"
|
||||
|
|
@ -109,43 +76,40 @@ export default {
|
|||
textLink,
|
||||
recalModal
|
||||
},
|
||||
data() {
|
||||
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: {
|
||||
bodyText() {
|
||||
if (useMainStore().order.damage.isRepair) {
|
||||
return this.unverifiedNonADASRepairBodyText;
|
||||
}
|
||||
else {
|
||||
let parts = useMainStore().order.lineItems.glassParts;
|
||||
|
||||
// if ADAS, display ADASNextSteps
|
||||
if (parts.filter(part => part.requiresRecalibration).length > 0) {
|
||||
return this.unverifiedADASNextStepsBodyText;
|
||||
}
|
||||
// if non-ADAS, display NonADASNextSteps
|
||||
else {
|
||||
return this.unverifiedNonADASNextStepsBodyText;
|
||||
}
|
||||
}
|
||||
},
|
||||
continueWithSchedulingBodyText() {
|
||||
return this.getCmsContent('continueWithSchedulingCopy', 'BodyText');
|
||||
},
|
||||
unverifiedADASNextStepsBodyText() {
|
||||
return this.getCmsContent('UnverifiedADASNextStepsWidget', 'BodyText').replaceAll("{custom:damage}", getDamageString());
|
||||
return this.getCmsContent('UnverifiedADASNextStepsWidget', 'BodyText').replaceAll("{custom:damage}", this.damageText);
|
||||
},
|
||||
unverifiedNonADASNextStepsBodyText() {
|
||||
return this.getCmsContent('UnverifiedNonADASNextStepsWidget', 'BodyText').replaceAll("{custom:damage}", getDamageString());
|
||||
return this.getCmsContent('UnverifiedNonADASNextStepsWidget', 'BodyText').replaceAll("{custom:damage}", this.damageText);
|
||||
},
|
||||
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);
|
||||
damageText() {
|
||||
var damageString = getDamageString();
|
||||
return damageString == "match" ? "" : damageString;
|
||||
},
|
||||
arrayOfListItemsFromBodyText() {
|
||||
return this.getArrayOfListItemsFromRawCmsCopy(this.unverifiedADASNextStepsBodyText);
|
||||
}
|
||||
},
|
||||
async beforeRouteEnter(to, from, next) {
|
||||
// Call APIs
|
||||
|
|
@ -179,32 +143,6 @@ 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() {},
|
||||
|
|
|
|||
69
src/layouts/provider-preference/provider-preference.vue
Normal file
69
src/layouts/provider-preference/provider-preference.vue
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
<template>
|
||||
<Form @submit="onSubmit" @invalidSubmit="onInvalidSubmit" v-slot="{ meta }">
|
||||
<div class="page-container-grouped-styles overflow-auto">
|
||||
<siteHeader cmsWidgetName="SiteHeaderWidget" />
|
||||
<siteSubHeader cmsWidgetName="SiteSubHeaderWidget" />
|
||||
<div class="fade-on-route-transition sub-container make-tall px-5">
|
||||
<p>Placeholder for provider-preference page</p>
|
||||
<siteFooter
|
||||
cmsWidgetName="SiteFooterWidget"
|
||||
:isForwardActionDisabled="!meta.valid"
|
||||
@backClicked="backButtonAction"
|
||||
@forwardClicked="forwardButtonAction"
|
||||
ref="siteFooter"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
</template>
|
||||
<script>
|
||||
// Import Supporting Files
|
||||
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||
import { settleAllPromises } from "@/helpers/layout-helper";
|
||||
// Import Component
|
||||
import baseFormMixin from "@/mixins/base-form-mixin";
|
||||
import { Form } from "vee-validate";
|
||||
import siteFooter from "@/iss-components/site-footer/site-footer.vue";
|
||||
import siteHeader from "@/iss-components/site-header/site-header.vue";
|
||||
import siteSubHeader from "@/iss-components/site-sub-header/site-sub-header.vue";
|
||||
export default {
|
||||
name: "provider-preference",
|
||||
mixins: [baseFormMixin],
|
||||
components: {
|
||||
siteFooter,
|
||||
siteHeader,
|
||||
siteSubHeader,
|
||||
Form,
|
||||
},
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
async beforeRouteEnter(to, from, next) {
|
||||
const cmsContentPromise = fetchCmsContentForPage(to.query.issPage);
|
||||
// Settle promises and get results
|
||||
const promiseResultMap = [
|
||||
{
|
||||
resultKey: "cmsContent",
|
||||
promise: cmsContentPromise,
|
||||
},
|
||||
];
|
||||
const resultMap = await settleAllPromises(promiseResultMap);
|
||||
next((vm) => {
|
||||
vm.setCmsContent(resultMap.cmsContent);
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
arePagePrerequisiteValid() {
|
||||
return true;
|
||||
},
|
||||
backButtonAction() {
|
||||
/**
|
||||
* this.navigationScenarios comes from base-mixin
|
||||
*/
|
||||
this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route);
|
||||
},
|
||||
async forwardButtonAction() {},
|
||||
resetDependentState() {},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -5,6 +5,7 @@ import { issPageValues } from '@/router/router-constants/issPage-values';
|
|||
import { queryStrings } from '@/constants/query-strings';
|
||||
import { GaActions } from "@/constants/analytics";
|
||||
import VehicleLookup from './vehicle-lookup.vue';
|
||||
import {vinLookupMethodSelections} from '@/constants/vin-lookup-methods';
|
||||
|
||||
const vinLookupMethodsMockData = {
|
||||
QuestionText: 'To find the right part, let’s get your VIN. Or we can look it up for you!',
|
||||
|
|
@ -47,6 +48,10 @@ function setupMocks() {
|
|||
const mockRouter = {
|
||||
navigate: jest.fn(),
|
||||
};
|
||||
|
||||
const mockIsVinbyAddressPermissibleToBeFalse={
|
||||
IsVinbyAddressPermissible:false,
|
||||
};
|
||||
|
||||
const wrapper = mount(VehicleLookup, {
|
||||
data() {
|
||||
|
|
@ -95,7 +100,7 @@ function setupMocks() {
|
|||
],
|
||||
mocks: {
|
||||
$route: mockRoute,
|
||||
$router: mockRouter,
|
||||
$router: mockRouter,
|
||||
},
|
||||
stubs: {
|
||||
SiteHeader: true,
|
||||
|
|
@ -105,10 +110,29 @@ function setupMocks() {
|
|||
},
|
||||
});
|
||||
|
||||
return { mockRoute, mockRouter, wrapper };
|
||||
return { mockRoute, mockRouter, wrapper,mockIsVinbyAddressPermissibleToBeFalse };
|
||||
}
|
||||
|
||||
describe('vehicle-lookup.vue', () => {
|
||||
test('IsVinbyAddressPermissible to be false', () => {
|
||||
const { wrapper,mockIsVinbyAddressPermissibleToBeFalse } = setupMocks();
|
||||
|
||||
const vinLookupMethodsWrapper = wrapper.findComponent({ name: 'vin-lookup-methods' });
|
||||
// Not using initializeComponent() because it doesn't trigger reactivity in test.
|
||||
const Answers = JSON.parse(JSON.stringify(vinLookupMethodsMockData.Answers));
|
||||
if(!mockIsVinbyAddressPermissibleToBeFalse.IsVinbyAddressPermissible){
|
||||
Answers.splice(Answers.indexOf(vinLookupMethodSelections.HOMEADDRESS),1);
|
||||
}
|
||||
vinLookupMethodsWrapper.setData({
|
||||
questionTextFromCms: vinLookupMethodsMockData.QuestionText,
|
||||
answersFromCms: Answers,
|
||||
});
|
||||
|
||||
const lookupByAddressOptionLabel = wrapper.find('[data-test-id="vin-lookup-methods-button-question"] label[for="VinLookupMethods-HomeAddress"]');
|
||||
expect(lookupByAddressOptionLabel.exists()).toBe(false);
|
||||
|
||||
});
|
||||
|
||||
test('"Continue" button is disabled when no VIN Lookup method is selected.', () => {
|
||||
const { wrapper } = setupMocks();
|
||||
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ import { fetchCmsContentForPage } from '@/helpers/cms-content-helper';
|
|||
import { settleAllPromises } from '@/helpers/layout-helper';
|
||||
import { Form } from 'vee-validate';
|
||||
import BaseFormMixin from '@/mixins/base-form-mixin';
|
||||
import VIN_LOOKUP_METHODS from '@/constants/vin-lookup-methods';
|
||||
import {vinLookupMethodSelections} from '@/constants/vin-lookup-methods';
|
||||
|
||||
// Import Component
|
||||
import SiteFooter from '@/iss-components/site-footer/site-footer.vue';
|
||||
|
|
@ -98,13 +98,13 @@ export default {
|
|||
},
|
||||
forwardButtonAction() {
|
||||
switch (this.selectedVinLookupMethod) {
|
||||
case VIN_LOOKUP_METHODS.manualVin:
|
||||
case vinLookupMethodSelections.MANUALVIN:
|
||||
this.$router.navigate(this.navigationScenarios.SELECTED_MANUAL_VIN, this.$route);
|
||||
break;
|
||||
case VIN_LOOKUP_METHODS.licensePlate:
|
||||
case vinLookupMethodSelections.LICENSEPLATE:
|
||||
this.$router.navigate(this.navigationScenarios.SELECTED_LICENSE_PLATE, this.$route);
|
||||
break;
|
||||
case VIN_LOOKUP_METHODS.homeAddress:
|
||||
case vinLookupMethodSelections.HOMEADDRESS:
|
||||
this.$router.navigate(this.navigationScenarios.SELECTED_HOME_ADDRESS, this.$route);
|
||||
break;
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -16,6 +16,9 @@
|
|||
import { defineRule } from 'vee-validate';
|
||||
import { required } from '@/helpers/validation-rules';
|
||||
import { errorMessages } from '@/constants/error-messages';
|
||||
import { useMainStore } from "@/store";
|
||||
import {vinLookupMethodSelections} from '@/constants/vin-lookup-methods';
|
||||
import { settleAllPromises } from "@/helpers/layout-helper";
|
||||
|
||||
// Import Component
|
||||
import ButtonQuestion from '@/digital-components/button-question/button-question.vue';
|
||||
|
|
@ -38,6 +41,7 @@ export default {
|
|||
required,
|
||||
},
|
||||
},
|
||||
|
||||
emits: ['update:modelValue'],
|
||||
components: {
|
||||
ButtonQuestion,
|
||||
|
|
@ -53,14 +57,31 @@ export default {
|
|||
},
|
||||
},
|
||||
methods: {
|
||||
initializeComponent() {
|
||||
async initializeComponent() {
|
||||
const isVinbyAddressPermissible = await this.getIsVinbyAddressPermissible();
|
||||
this.questionTextFromCms = this.getCmsContent(this.cmsWidgetName, 'QuestionText');
|
||||
|
||||
const answers = this.getCmsContent(this.cmsWidgetName, 'Answers');
|
||||
if (Array.isArray(answers)) {
|
||||
if(!isVinbyAddressPermissible){
|
||||
answers.splice(answers.indexOf(vinLookupMethodSelections.HOMEADDRESS),1);
|
||||
}
|
||||
this.answersFromCms = answers;
|
||||
}
|
||||
},
|
||||
async getIsVinbyAddressPermissible(){
|
||||
const isVinbyAddressPermissibleResponse = useMainStore().getIsVinbyAddressPermissible();
|
||||
// Settle promises and get results
|
||||
const promiseResultMap = [
|
||||
{
|
||||
resultKey: "isVinbyAddressPermissible",
|
||||
promise: isVinbyAddressPermissibleResponse,
|
||||
}
|
||||
];
|
||||
|
||||
const resultMap = await settleAllPromises(promiseResultMap);
|
||||
return resultMap.isVinbyAddressPermissible;
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ const getDefaultState = () => {
|
|||
triggeredSiteEntry: false,
|
||||
},
|
||||
issConfig: {
|
||||
clientName: "",
|
||||
clientName: "Generic Insurance", // this is the default and will be overriden by the client's name
|
||||
clientDisplayName: "",
|
||||
styleSheet: "",
|
||||
accountNumber: 0,
|
||||
|
|
@ -300,7 +300,22 @@ export const useMainStore = defineStore({
|
|||
payload: {},
|
||||
});
|
||||
},
|
||||
|
||||
getIsVinbyAddressPermissible(){
|
||||
try{
|
||||
const response = globalMethods.callHttpClient({
|
||||
method:endpoints.IsVinbyAddressPermissible.method,
|
||||
endpoint:`${endpoints.IsVinbyAddressPermissible.url}?state=${this.order.customer.address.state}`,
|
||||
payload: {},
|
||||
});
|
||||
return response;
|
||||
} catch (responseError) {
|
||||
return {
|
||||
error: {
|
||||
status: responseError.status,
|
||||
},
|
||||
};
|
||||
}
|
||||
},
|
||||
async lookupVinByPlate(licensePlate, licenseState) {
|
||||
try {
|
||||
const response = await globalMethods.callHttpClient({
|
||||
|
|
@ -1032,7 +1047,7 @@ export const useMainStore = defineStore({
|
|||
resetPartsAndDependencies() {
|
||||
this.resetGlassPartsState();
|
||||
this.updateSupportingItems(null);
|
||||
},
|
||||
},
|
||||
},
|
||||
persist: true
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue