Merging from develop
This commit is contained in:
commit
14f6174a76
47 changed files with 1189 additions and 370 deletions
|
|
@ -79,4 +79,44 @@ stages:
|
||||||
__VUE_APP_CONSUMER_API_GATEWAY__: $(__VUE_APP_CONSUMER_API_GATEWAY__)
|
__VUE_APP_CONSUMER_API_GATEWAY__: $(__VUE_APP_CONSUMER_API_GATEWAY__)
|
||||||
__VUE_APP_GOOGLE_PLACES_API_KEY__: $(__VUE_APP_GOOGLE_PLACES_API_KEY__)
|
__VUE_APP_GOOGLE_PLACES_API_KEY__: $(__VUE_APP_GOOGLE_PLACES_API_KEY__)
|
||||||
__VUE_APP_HERITAGE_FUNNEL__: $(__VUE_APP_HERITAGE_FUNNEL__)
|
__VUE_APP_HERITAGE_FUNNEL__: $(__VUE_APP_HERITAGE_FUNNEL__)
|
||||||
|
indexDeployVariables:
|
||||||
|
__VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY__: $(__VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY__)
|
||||||
|
__VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC__: $(__VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC__)
|
||||||
|
cfDistributionId: $(cfDistributionId)
|
||||||
|
|
||||||
|
|
||||||
|
# QA Build/Deploy
|
||||||
|
- stage: Qa
|
||||||
|
variables:
|
||||||
|
- group: FixMyGlassQa
|
||||||
|
jobs:
|
||||||
|
- deployment: qaBuildDeployment
|
||||||
|
displayName: Build and Deploy FMG - QA
|
||||||
|
environment: digitalCloud-qa
|
||||||
|
container: node
|
||||||
|
workspace:
|
||||||
|
clean: all
|
||||||
|
strategy:
|
||||||
|
runOnce:
|
||||||
|
deploy:
|
||||||
|
steps:
|
||||||
|
- checkout: self
|
||||||
|
clean: true
|
||||||
|
- template: templates/digital/step-build-vue.yml@AzureDevOps
|
||||||
|
parameters:
|
||||||
|
buildOutputDir: dist
|
||||||
|
- template: templates/digital/step-deploy-vue.yml@AzureDevOps
|
||||||
|
parameters:
|
||||||
|
artifactName: vueDist
|
||||||
|
awsProfile: $(qaDeploymentProfile)
|
||||||
|
outputPath: /fmg/
|
||||||
|
deployBuckets:
|
||||||
|
safelite-qa-fmg-us-east-1:
|
||||||
|
clearFolder: true
|
||||||
|
deployFolder: ''
|
||||||
|
region: us-east-1
|
||||||
|
appDeployVariables:
|
||||||
|
__VUE_APP_CONSUMER_API_GATEWAY__: $(__VUE_APP_CONSUMER_API_GATEWAY__)
|
||||||
|
__VUE_APP_GOOGLE_PLACES_API_KEY__: $(__VUE_APP_GOOGLE_PLACES_API_KEY__)
|
||||||
|
__VUE_APP_HERITAGE_FUNNEL__: $(__VUE_APP_HERITAGE_FUNNEL__)
|
||||||
cfDistributionId: $(cfDistributionId)
|
cfDistributionId: $(cfDistributionId)
|
||||||
|
|
@ -17,11 +17,9 @@ module.exports = {
|
||||||
"!src/layouts/vin-lookup/vin-lookup.vue",
|
"!src/layouts/vin-lookup/vin-lookup.vue",
|
||||||
"!src/layouts/vehicle-damage/windshield-damage-type-question/windshield-damage-type-question.vue",
|
"!src/layouts/vehicle-damage/windshield-damage-type-question/windshield-damage-type-question.vue",
|
||||||
"!src/layouts/vehicle-damage/windshield-options/windshield-options.vue",
|
"!src/layouts/vehicle-damage/windshield-options/windshield-options.vue",
|
||||||
"!src/layouts/address-poc/address-poc.vue",
|
|
||||||
"!src/layouts/nested-radio-poc/nested-radio.vue",
|
|
||||||
"!src/layouts/button-question-examples/**/*.vue",
|
|
||||||
"!src/layouts/part-questions/**/*.vue",
|
"!src/layouts/part-questions/**/*.vue",
|
||||||
"!src/layouts/reveal/**/*.vue",
|
"!src/layouts/reveal/**/*.vue",
|
||||||
|
"!src/layouts/estimate/**/*.vue",
|
||||||
// REMOVE THESE AFTER WRITING UNIT TESTS
|
// REMOVE THESE AFTER WRITING UNIT TESTS
|
||||||
|
|
||||||
"!src/layouts/address-lookup/customer-questions/customer-questions.vue",
|
"!src/layouts/address-lookup/customer-questions/customer-questions.vue",
|
||||||
|
|
@ -31,7 +29,7 @@ module.exports = {
|
||||||
testMatch: ["**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)"],
|
testMatch: ["**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)"],
|
||||||
coverageThreshold: {
|
coverageThreshold: {
|
||||||
global: {
|
global: {
|
||||||
statements: 87,
|
statements: 86,
|
||||||
// Got the go ahead from Mark to temporarily lower this. Taking out initialize component made the year,make,model and style coverage drop a bit. Once unit tests for license plate lookup, vin lookup and address lookup are in the coverage should go back up to 90
|
// Got the go ahead from Mark to temporarily lower this. Taking out initialize component made the year,make,model and style coverage drop a bit. Once unit tests for license plate lookup, vin lookup and address lookup are in the coverage should go back up to 90
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en-US">
|
<html lang="en-US">
|
||||||
<head>
|
<head>
|
||||||
|
<script>
|
||||||
|
window.dataLayer = [{}];
|
||||||
|
</script>
|
||||||
|
<script><%= process.env.VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY %></script>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||||
|
|
@ -11,9 +15,17 @@
|
||||||
<title><%= htmlWebpackPlugin.options.title %></title>
|
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
|
<!-- Google Tag Manager -->
|
||||||
|
<noscript>
|
||||||
|
<iframe src="<%= process.env.VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC %>"
|
||||||
|
height="0" width="0" style="display:none;visibility:hidden"></iframe>
|
||||||
|
</noscript>
|
||||||
|
|
||||||
<noscript>
|
<noscript>
|
||||||
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||||
</noscript>
|
</noscript>
|
||||||
|
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
<!-- built files will be auto injected -->
|
<!-- built files will be auto injected -->
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,9 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="w-100 d-flex justify-content-center">
|
<div class="w-100 d-flex justify-content-center">
|
||||||
<fieldset class="w-100" :class="getFieldSetClasses" :role="isMultiSelect ? 'group' : 'radiogroup'" :aria-labelledby="groupName ? groupName + '-radio-group' : ''">
|
<fieldset class="w-100" :class="getFieldSetClasses" :role="isMultiSelect ? 'group' : 'radiogroup'" :aria-labelledby="groupName ? groupName + '-radio-group' : ''">
|
||||||
<legend class="sr-only">{{groupName}}</legend>
|
<legend class="sr-only" :data-focus-target="groupName" tabindex="-1">
|
||||||
|
{{(isMultiSelect && answers && answers.length > 1) ? 'Select one or more options below.' : 'Select an option below.' }}
|
||||||
|
</legend>
|
||||||
<div :class="getComponentWrapperClasses">
|
<div :class="getComponentWrapperClasses">
|
||||||
<component
|
<component
|
||||||
:is="buttonType"
|
:is="buttonType"
|
||||||
|
|
@ -33,6 +35,7 @@
|
||||||
:selectedValues="selectedValues"
|
:selectedValues="selectedValues"
|
||||||
data-test="button"
|
data-test="button"
|
||||||
:validationRules="validationRules"
|
:validationRules="validationRules"
|
||||||
|
:class="[suppressError ? 'alertError' : '']"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
|
||||||
|
|
@ -22,13 +22,13 @@
|
||||||
isPrimary
|
isPrimary
|
||||||
:buttonText="buttonText"
|
:buttonText="buttonText"
|
||||||
loaderColor="white"
|
loaderColor="white"
|
||||||
:class="isDisabled && 'form-test-invalid'"
|
:class="isForwardActionDisabled && 'form-test-invalid'"
|
||||||
:aria-disabled="isDisabled"
|
:aria-disabled="isForwardActionDisabled"
|
||||||
:isDisabled="isDisabled"
|
:isDisabled="isForwardActionDisabled"
|
||||||
@click-event="buttonClick"
|
@click-event="buttonClick"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-auto link-col py-1 text-break">
|
<div v-if="!isBackButtonHidden" class="col-auto link-col py-1 text-break">
|
||||||
<textLink
|
<textLink
|
||||||
linkType="navigation"
|
linkType="navigation"
|
||||||
:text="backLink"
|
:text="backLink"
|
||||||
|
|
@ -48,8 +48,10 @@ import buttonMain from "@/ux-components/button-main/button-main";
|
||||||
export default {
|
export default {
|
||||||
name: "funnelFooter",
|
name: "funnelFooter",
|
||||||
props: {
|
props: {
|
||||||
isDisabled: Boolean,
|
isForwardActionDisabled: Boolean,
|
||||||
|
isBackButtonHidden: {type: Boolean, default: false},
|
||||||
cmsWidgetName: String,
|
cmsWidgetName: String,
|
||||||
|
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
textLink,
|
textLink,
|
||||||
|
|
@ -70,6 +72,9 @@ export default {
|
||||||
beforeUnmount() {
|
beforeUnmount() {
|
||||||
window.removeEventListener('resize', this.onResize);
|
window.removeEventListener('resize', this.onResize);
|
||||||
},
|
},
|
||||||
|
unmounted() {
|
||||||
|
document.onkeydown = null;
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
backLink(){
|
backLink(){
|
||||||
return this.getCmsContent(this.cmsWidgetName, 'BackButtonText');
|
return this.getCmsContent(this.cmsWidgetName, 'BackButtonText');
|
||||||
|
|
@ -87,8 +92,15 @@ export default {
|
||||||
},
|
},
|
||||||
removeLoader(){
|
removeLoader(){
|
||||||
this.$refs.buttonMain.removeLoader();
|
this.$refs.buttonMain.removeLoader();
|
||||||
|
document.onkeydown = function (e) {
|
||||||
|
return true;
|
||||||
|
};
|
||||||
},
|
},
|
||||||
buttonClick() {
|
buttonClick() {
|
||||||
|
//prevent keyboard input after button click
|
||||||
|
document.onkeydown = function (e) {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
this.$emit("ForwardClicked");
|
this.$emit("ForwardClicked");
|
||||||
},
|
},
|
||||||
linkClick() {
|
linkClick() {
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,25 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="textbox-question" :class="hasError ? 'has-error' : ''">
|
<div class="textbox-question" :class="(errors && errors.length) || hasError ? 'has-error' : ''">
|
||||||
<label :for="inputId" :aria-label="questionText" class="form-label" v-html="labelText"></label>
|
<label :for="inputId" :aria-label="questionText" class="form-label" v-html="labelText"></label>
|
||||||
<input v-model="value"
|
<input v-model="value"
|
||||||
v-maska="mask"
|
v-maska="mask"
|
||||||
:type="type"
|
:type="type"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
:ref="inputId"
|
:ref="inputId"
|
||||||
:id="inputId"
|
:id="inputId"
|
||||||
:name="inputId"
|
:name="inputId"
|
||||||
:placeholder="placeholderText"
|
:placeholder="placeholderText"
|
||||||
:aria-disabled="isDisabled"
|
:aria-disabled="isDisabled"
|
||||||
:disabled="isDisabled"
|
:disabled="isDisabled"
|
||||||
:aria-required="isRequired"
|
:aria-required="isRequired"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
:class="[hasIcon ? 'has-icon' : '', iconRight ? 'icon-right' : '']"
|
:class="[hasIcon ? 'has-icon' : '', iconRight ? 'icon-right' : '']"
|
||||||
:validationRules="validationRules"
|
:validationRules="validationRules"
|
||||||
@input="handleChange"
|
@input="handleChange"
|
||||||
@blur="handleBlur" />
|
@blur="handleBlur" />
|
||||||
<div v-show="errorMessage" class="row mt-2 form-test-error">
|
<div v-show="errorMessage" class="row mt-2 form-test-error">
|
||||||
<span role="alert">{{ errorMessage }}</span>
|
<span role="alert">{{ errorMessage }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -56,7 +56,7 @@ export default {
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const fieldOptions = {
|
const fieldOptions = {
|
||||||
type: "text",
|
type: "text",
|
||||||
value: props.modelValue,
|
value: props.modelValue,
|
||||||
};
|
};
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
|
@ -64,15 +64,17 @@ export default {
|
||||||
handleBlur,
|
handleBlur,
|
||||||
handleChange,
|
handleChange,
|
||||||
meta,
|
meta,
|
||||||
validate
|
validate,
|
||||||
|
errors,
|
||||||
} = useField(props.inputId, props.validationRules, fieldOptions);
|
} = useField(props.inputId, props.validationRules, fieldOptions);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
errorMessage,
|
errorMessage,
|
||||||
handleBlur,
|
handleBlur,
|
||||||
handleChange,
|
handleChange,
|
||||||
validate,
|
validate,
|
||||||
meta,
|
meta,
|
||||||
|
errors,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|
@ -86,7 +88,7 @@ export default {
|
||||||
set: function(newValue) {
|
set: function(newValue) {
|
||||||
this.$emit("update:modelValue", newValue);
|
this.$emit("update:modelValue", newValue);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
labelText: {
|
labelText: {
|
||||||
get: function () {
|
get: function () {
|
||||||
var thisQuestionText = "";
|
var thisQuestionText = "";
|
||||||
|
|
@ -102,7 +104,7 @@ export default {
|
||||||
|
|
||||||
return thisQuestionText;
|
return thisQuestionText;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
value(newValue) {
|
value(newValue) {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,10 @@
|
||||||
const cookieNames = {
|
const cookieNames = {
|
||||||
FUNNEL_SESSION_INFO: "FunnelSessionInfo",
|
FUNNEL_SESSION_INFO: "FunnelSessionInfo",
|
||||||
|
|
||||||
|
// Existing Safelite.com cookies
|
||||||
|
DXDEV: "dxdev",
|
||||||
|
SESSION_ID: "sid",
|
||||||
|
SESSION_KEY: "skey"
|
||||||
};
|
};
|
||||||
|
|
||||||
export { cookieNames };
|
export { cookieNames };
|
||||||
|
|
|
||||||
7
src/constants/dynamic-strings.js
Normal file
7
src/constants/dynamic-strings.js
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
const dynamicStrings = {
|
||||||
|
GLOBAL_STATE: "globalState",
|
||||||
|
CUSTOM: "custom",
|
||||||
|
ROUTER_LINK: "routerLink"
|
||||||
|
};
|
||||||
|
|
||||||
|
export { dynamicStrings };
|
||||||
|
|
@ -67,6 +67,10 @@ const endpoints = {
|
||||||
url: "/location/api/v1/location/zip",
|
url: "/location/api/v1/location/zip",
|
||||||
method: "GET",
|
method: "GET",
|
||||||
},
|
},
|
||||||
|
LogExperimentExposureIfAssigned:{
|
||||||
|
url: "/analytics/api/v1/analytics/log-experiment-exposure",
|
||||||
|
method: "POST",
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export { endpoints };
|
export { endpoints };
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,8 @@ const errorMessages = {
|
||||||
EMAIL_ADDRESS_FORMAT: "Please enter a valid email address",
|
EMAIL_ADDRESS_FORMAT: "Please enter a valid email address",
|
||||||
SERVICE_ZIP_REQUIRED: "Please enter your Service ZIP",
|
SERVICE_ZIP_REQUIRED: "Please enter your Service ZIP",
|
||||||
SERVICE_ZIP_FORMAT: "Please enter a valid Service ZIP",
|
SERVICE_ZIP_FORMAT: "Please enter a valid Service ZIP",
|
||||||
|
VIN_REQUIRED: "Please enter your VIN",
|
||||||
|
VIN_FORMAT: "Please enter a valid VIN",
|
||||||
};
|
};
|
||||||
|
|
||||||
export { errorMessages };
|
export { errorMessages };
|
||||||
|
|
|
||||||
6
src/constants/experiments.js
Normal file
6
src/constants/experiments.js
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
const experimentUniverses = {
|
||||||
|
CONCEPT_FUNNEL: 'ConceptFunnel'
|
||||||
|
};
|
||||||
|
|
||||||
|
export { experimentUniverses };
|
||||||
|
|
||||||
|
|
@ -18,6 +18,7 @@ const storeActions = {
|
||||||
LOAD_ORDER: "loadOrder",
|
LOAD_ORDER: "loadOrder",
|
||||||
SET_REFERRAL_INFORMATION: "setReferralInformation",
|
SET_REFERRAL_INFORMATION: "setReferralInformation",
|
||||||
VALIDATE_ZIP: "validateZip",
|
VALIDATE_ZIP: "validateZip",
|
||||||
|
LOG_EXPERIMENT_EXPOSURE: "logExperimentExposure",
|
||||||
|
|
||||||
// DEPENDENCY MUTATIONS
|
// DEPENDENCY MUTATIONS
|
||||||
RESET_VEHICLE_STATE_AND_DEPENDENCIES: "resetVehicleAndDependencies",
|
RESET_VEHICLE_STATE_AND_DEPENDENCIES: "resetVehicleAndDependencies",
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(error) => {
|
(error) => {
|
||||||
|
console.error(error);
|
||||||
return reject(error.response);
|
return reject(error.response);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { storeActions } from "@/constants/store-actions.js";
|
import { storeActions } from "@/constants/store-actions.js";
|
||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
|
import { dynamicStrings } from "../constants/dynamic-strings";
|
||||||
|
|
||||||
export function fetchCmsContentForPage(fmgPage) {
|
export function fetchCmsContentForPage(fmgPage) {
|
||||||
return store
|
return store
|
||||||
|
|
@ -40,12 +41,15 @@ export function fetchCmsContentForPage(fmgPage) {
|
||||||
function mapStringToState(str) {
|
function mapStringToState(str) {
|
||||||
// Pull all matches out of the string.
|
// Pull all matches out of the string.
|
||||||
const regexExp = new RegExp("{(.*?):(.*?)}", "g");
|
const regexExp = new RegExp("{(.*?):(.*?)}", "g");
|
||||||
const matches = [...str.matchAll(regexExp)];
|
const regexMatches = [...str.matchAll(regexExp)];
|
||||||
|
const globalStateMatches = regexMatches.filter(match => {
|
||||||
|
return match[1] === dynamicStrings.GLOBAL_STATE;
|
||||||
|
})
|
||||||
|
|
||||||
// Our final string value that will be built from the matches.
|
// Our final string value that will be built from the matches.
|
||||||
let stringBuilder = "";
|
let stringBuilder = "";
|
||||||
|
|
||||||
for (const match of matches) {
|
for (const match of globalStateMatches) {
|
||||||
// Reset store state for each match.
|
// Reset store state for each match.
|
||||||
let storeState = store.state;
|
let storeState = store.state;
|
||||||
|
|
||||||
|
|
@ -60,7 +64,7 @@ function mapStringToState(str) {
|
||||||
const stringWithReplacement = str.replace(match[0], storeState);
|
const stringWithReplacement = str.replace(match[0], storeState);
|
||||||
|
|
||||||
// If we still have values we need to substitute, call this function again.
|
// If we still have values we need to substitute, call this function again.
|
||||||
if (stringWithReplacement.includes("{globalState:")) {
|
if (stringWithReplacement.includes(dynamicStrings.GLOBAL_STATE)) {
|
||||||
return mapStringToState(stringWithReplacement);
|
return mapStringToState(stringWithReplacement);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -96,7 +100,7 @@ function findAndReplaceGlobalStateValues(widgetModel, widgetName) {
|
||||||
function processWidgetItemForReplacement(widgetModel, key) {
|
function processWidgetItemForReplacement(widgetModel, key) {
|
||||||
// If we have a string, and it needs to be replaced.
|
// If we have a string, and it needs to be replaced.
|
||||||
if (typeof widgetModel[key] === "string") {
|
if (typeof widgetModel[key] === "string") {
|
||||||
if (widgetModel[key].includes("{globalState:")) {
|
if (widgetModel[key].includes(dynamicStrings.GLOBAL_STATE)) {
|
||||||
widgetModel[key] = mapStringToState(widgetModel[key]);
|
widgetModel[key] = mapStringToState(widgetModel[key]);
|
||||||
}
|
}
|
||||||
return widgetModel[key];
|
return widgetModel[key];
|
||||||
|
|
@ -116,4 +120,4 @@ function processWidgetItemForReplacement(widgetModel, key) {
|
||||||
|
|
||||||
// If we have something else like a number, boolean, etc. just return it
|
// If we have something else like a number, boolean, etc. just return it
|
||||||
return widgetModel[key];
|
return widgetModel[key];
|
||||||
}
|
}
|
||||||
22
src/helpers/damage-helper.js
Normal file
22
src/helpers/damage-helper.js
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
import store from "@/store";
|
||||||
|
|
||||||
|
export function getDamageString() {
|
||||||
|
return store.getters.damage.glassToReplace.length > 1 ? "match" : store.getters.damage.glassToReplace[0].location;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function compareGlassOptions(newOptions, currentOptions){
|
||||||
|
const optionsMap = {
|
||||||
|
Windshield: "windshieldOptions",
|
||||||
|
Driver: "driverSideOptions",
|
||||||
|
Passenger: "passengerSideOptions",
|
||||||
|
Rear: "backGlassOptions"
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const option of currentOptions){
|
||||||
|
if(!newOptions[optionsMap[option.location]].availableReplacementOptions.includes(option.name)){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
37
src/helpers/damage-helper.spec.js
Normal file
37
src/helpers/damage-helper.spec.js
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
import {getDamageString, compareGlassOptions} from "./damage-helper";
|
||||||
|
|
||||||
|
jest.mock("@/store", () => ({
|
||||||
|
getters: {damage: {
|
||||||
|
glassToReplace: [{location: "TEST"}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe("damage-helper.js", () => {
|
||||||
|
it("Should return damage getter info", () => {
|
||||||
|
const damage = getDamageString();
|
||||||
|
expect(damage).toEqual("TEST")
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("damage-helper.js", () => {
|
||||||
|
it("Should return false if no mismatches between each array", () => {
|
||||||
|
const newOptions = {
|
||||||
|
windshieldOptions: {availableReplacementOptions: ["windshield"]}
|
||||||
|
}
|
||||||
|
const currentOptions = [{location: "Windshield", name: "windshield"}];
|
||||||
|
const misMatch = compareGlassOptions(newOptions, currentOptions);
|
||||||
|
expect(misMatch).toEqual(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("damage-helper.js", () => {
|
||||||
|
it("Should return true if there are any mismatches between arrays", () => {
|
||||||
|
const newOptions = {
|
||||||
|
windshieldOptions: {availableReplacementOptions: ["window"]}
|
||||||
|
}
|
||||||
|
const currentOptions = [{location: "Windshield", name: "windshield"}];
|
||||||
|
const misMatch = compareGlassOptions(newOptions, currentOptions);
|
||||||
|
expect(misMatch).toEqual(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -18,19 +18,20 @@ export function updateOrCreateFunnelCookie() {
|
||||||
ReferralNumber: store.getters.order.referralNumber,
|
ReferralNumber: store.getters.order.referralNumber,
|
||||||
ReferralDate: store.getters.order.referralDate,
|
ReferralDate: store.getters.order.referralDate,
|
||||||
ReferralCorrelationId: store.getters.order.referralCorrelationId,
|
ReferralCorrelationId: store.getters.order.referralCorrelationId,
|
||||||
|
ReferralParentAccountNumber: store.getters.order.parentAccountNumber,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Gets the current instance of the funnel cookie.
|
Gets the current instance of the funnel cookie.
|
||||||
Returns null if cookie isn't valid JSON.
|
Returns null if cookie isn't valid JSON.
|
||||||
*/
|
*/
|
||||||
export function getFunnelCookie() {
|
export function getFunnelCookie() {
|
||||||
const cookieJson = document.cookie
|
const cookieJson = document.cookie
|
||||||
?.split("; ")
|
?.split("; ")
|
||||||
?.find(row => row.startsWith(`${cookieNames.FUNNEL_SESSION_INFO}=`))
|
?.find(row => row.startsWith(`${cookieNames.FUNNEL_SESSION_INFO}=`))
|
||||||
?.split("=")[1];
|
?.split("=")[1];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return JSON.parse(cookieJson);
|
return JSON.parse(cookieJson);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
@ -45,6 +46,55 @@ export function deleteFunnelCookie() {
|
||||||
document.cookie = `${cookieNames.FUNNEL_SESSION_INFO}=; Max-Age=0; path=${applicationConfig.COOKIE_PATH}; ${getCookieDomainValue()}`;
|
document.cookie = `${cookieNames.FUNNEL_SESSION_INFO}=; Max-Age=0; path=${applicationConfig.COOKIE_PATH}; ${getCookieDomainValue()}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Gets cookie domain value. Localhost will be empty "".
|
||||||
|
*/
|
||||||
|
export function getCookieDomainValue() {
|
||||||
|
return location.hostname.includes("localhost") ? "" : `domain=${getDomainWithoutSubdomain()};`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Gets value of dxdev cookie, and then extracts "did" value from it.
|
||||||
|
Returns empty string if cookie not found or "did" string not present.
|
||||||
|
*/
|
||||||
|
export function getDeviceIdValue(){
|
||||||
|
// Sometimes these cookie contains more than the device ID.
|
||||||
|
const cookieValue = getCookieValueByName(cookieNames.DXDEV);
|
||||||
|
const cookieValuesSplit = cookieValue.split('=');
|
||||||
|
|
||||||
|
// If this is the only value, just use that.
|
||||||
|
if(cookieValuesSplit.length === 2 && cookieValuesSplit[0] === 'did'){
|
||||||
|
return cookieValuesSplit[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
const cookieValueMatch = cookieValue.match("^did=[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}");
|
||||||
|
if(cookieValueMatch){
|
||||||
|
return cookieValueMatch[0].split('=')[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Gets value of skey cookie, returns 0 if not found.
|
||||||
|
*/
|
||||||
|
export function getSessionKeyValue(){
|
||||||
|
const cookieValue = getCookieValueByName(cookieNames.SESSION_KEY);
|
||||||
|
|
||||||
|
if(cookieValue){
|
||||||
|
return cookieValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
===========================
|
||||||
|
= PRIVATE FUNCTIONS =
|
||||||
|
===========================
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Used to set properties on the funnel cookie.
|
Used to set properties on the funnel cookie.
|
||||||
Takes an object with properties to set. Will overwrite existing properties.
|
Takes an object with properties to set. Will overwrite existing properties.
|
||||||
|
|
@ -65,10 +115,9 @@ function setFunnelCookieProperties(properties) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getCookieDomainValue() {
|
/*
|
||||||
return location.hostname.includes("localhost") ? "" : `domain=${getDomainWithoutSubdomain()};`;
|
Gets current domain without the subdomain for cookie.
|
||||||
}
|
*/
|
||||||
|
|
||||||
function getDomainWithoutSubdomain() {
|
function getDomainWithoutSubdomain() {
|
||||||
let url = location.hostname;
|
let url = location.hostname;
|
||||||
if (url.includes("localhost")) {
|
if (url.includes("localhost")) {
|
||||||
|
|
@ -82,3 +131,16 @@ function getDomainWithoutSubdomain() {
|
||||||
.slice(-(urlParts.length === 4 ? 3 : 2))
|
.slice(-(urlParts.length === 4 ? 3 : 2))
|
||||||
.join('.')}`;
|
.join('.')}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Gets cookie value by name, returns empty string if not found.
|
||||||
|
*/
|
||||||
|
function getCookieValueByName(name) {
|
||||||
|
const value = "; " + document.cookie;
|
||||||
|
const parts = value.split("; " + name + "=");
|
||||||
|
|
||||||
|
if (parts.length === 2) {
|
||||||
|
return parts.pop().split(";").shift();
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import {getFunnelCookie} from "@/helpers/heritage-integration/cookie-helper.js";
|
import {getFunnelCookie, getDeviceIdValue, getSessionKeyValue} from "@/helpers/heritage-integration/cookie-helper.js";
|
||||||
import { removeAllTestCookies, setupCookies } from "@/helpers/unit-test-helper";
|
import { removeAllTestCookies, setupCookies } from "@/helpers/unit-test-helper";
|
||||||
|
|
||||||
describe("cookies", () => {
|
describe("cookies", () => {
|
||||||
|
|
@ -100,5 +100,31 @@ describe("cookies", () => {
|
||||||
expect(actualCookieValue).toBeNull();
|
expect(actualCookieValue).toBeNull();
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("getDeviceIdValue", () => {
|
||||||
|
test("getDeviceIdValue, should return GUID", () => {
|
||||||
|
// Arrange
|
||||||
|
setupCookies({});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const result = getDeviceIdValue();
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
expect(result).toBe('21b9b94a-ec23-42c1-aaac-e2ae4e4dbffe');
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
test("getSessionKeyValue, should return session key int", () => {
|
||||||
|
// Arrange
|
||||||
|
setupCookies({});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const result = getSessionKeyValue();
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
expect(result).toBe('12345');
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -2,55 +2,38 @@ import { queryStrings } from "@/constants/query-strings";
|
||||||
import { externalUrls } from "@/router/router-constants/externalUrl-values";
|
import { externalUrls } from "@/router/router-constants/externalUrl-values";
|
||||||
import { lazyLoadComponent } from "@/router/dynamic-routing/component-loader.js";
|
import { lazyLoadComponent } from "@/router/dynamic-routing/component-loader.js";
|
||||||
import { saveOrder } from "@/helpers/heritage-integration/order-helper.js";
|
import { saveOrder } from "@/helpers/heritage-integration/order-helper.js";
|
||||||
|
import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
|
||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
import router from "@/router";
|
import router from "@/router";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
If the user has visited the funnel before this method will determine the bets place to
|
If the user has visited the funnel before this method will determine the bets place to
|
||||||
drop them so they don't start at the beginning again. This method will return 'heritage' if
|
drop them so they don't start at the beginning again. This method will return 'heritage' if
|
||||||
the user has an existing order and they come back in from the Safelite.com CTA.
|
the user has an existing order and they come back in from the Safelite.com CTA.
|
||||||
*/
|
*/
|
||||||
export async function getPageToRouteExistingOrderTo(toRoute = {}, existingHeritageOrder = false) {
|
export async function getPageToRouteExistingOrderTo(toRoute = {}, existingHeritageOrder = false) {
|
||||||
|
|
||||||
// If the user is coming in via the Safelite.Com CTA
|
// If the user is coming in via the Safelite.Com CTA
|
||||||
if (toRoute.query[queryStrings.START_TYPE] === 'fmg') {
|
if (toRoute.query[queryStrings.START_TYPE] === 'fmg') {
|
||||||
|
|
||||||
// If they have an existing order, return 'heritage' for the page name.
|
// If they have an existing order, return 'heritage' for the page name.
|
||||||
if (existingHeritageOrder) {
|
if (existingHeritageOrder) {
|
||||||
return 'heritage';
|
return 'heritage';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return await getLatestPageForRedirection();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If navigating to a specific page, and that page is not part of the vin pages.
|
||||||
// If this is a non-CTA navigation, determine where to send the user based on page prerequisites.
|
// Return that page, so that it can navigate like normal.
|
||||||
// This also works if a user has a 'fmg' start_type query string but no current order.
|
if (toRoute.query[queryStrings.FMG_PAGE] !== undefined && !isVinRelatedPage(toRoute)) {
|
||||||
// That shouldn't happen, but it's possible.
|
return overrideYmmsDirectionIfNeeded(toRoute);
|
||||||
const vehicleMakeComponent = (await lazyLoadComponent('vehicle-make')()).default;
|
|
||||||
const vehicleModelComponent = (await lazyLoadComponent('vehicle-model')()).default;
|
|
||||||
const vehicleStyleComponent = (await lazyLoadComponent('vehicle-style')()).default;
|
|
||||||
const vehicleDamageComponent = (await lazyLoadComponent('vehicle-damage')()).default;
|
|
||||||
|
|
||||||
|
|
||||||
if (!vehicleMakeComponent.methods.arePagePrerequisitesValid()) {
|
|
||||||
return "vehicle-year";
|
|
||||||
} else if (!vehicleModelComponent.methods.arePagePrerequisitesValid()) {
|
|
||||||
return "vehicle-make";
|
|
||||||
} else if (!vehicleStyleComponent.methods.arePagePrerequisitesValid()) {
|
|
||||||
return "vehicle-model";
|
|
||||||
} else if (!vehicleDamageComponent.methods.arePagePrerequisitesValid()) {
|
|
||||||
return "vehicle-style";
|
|
||||||
} else if (store.getters.damage.isRepair == null || !store.getters.vehicle.carId) {
|
|
||||||
return 'vehicle-damage'
|
|
||||||
} else {
|
|
||||||
if (store.getters.vehicle.vin) {
|
|
||||||
return 'vehicle-damage';
|
|
||||||
//return "vin-lookup"; (uncomment)
|
|
||||||
} else {
|
|
||||||
return 'vehicle-damage';
|
|
||||||
//return "estimate" (uncomment)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If this is not a direct link to a page using fmgPage, not from Safelite.com CTA or this is a vin related page.
|
||||||
|
// Get the latest page for redirection.
|
||||||
|
const latestPageRoute = await getLatestPageForRedirection();
|
||||||
|
|
||||||
|
return latestPageRoute;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -65,10 +48,81 @@ export async function navigateToHeritageFunnel() {
|
||||||
externalUrls.HERITAGE_FUNNEL,
|
externalUrls.HERITAGE_FUNNEL,
|
||||||
{
|
{
|
||||||
corid: store.getters.order.referralCorrelationId,
|
corid: store.getters.order.referralCorrelationId,
|
||||||
src: "concept-funnel",
|
src: "concept-funnel"
|
||||||
// TODO CSR-28, remove this
|
|
||||||
cns: "all",
|
|
||||||
experiments: "RemoveServiceAreaPage=ServAreaRemoval_V7=NoShowPackages_CONTROL=true,ConceptFunnel=ConceptFunnel_V1=ConceptFunnel_TEST=true"
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Logic for getting the last "valid" page a user visited.
|
||||||
|
*/
|
||||||
|
async function getLatestPageForRedirection() {
|
||||||
|
// If this is a non-CTA navigation, determine where to send the user based on page prerequisites.
|
||||||
|
// This also works if a user has a 'fmg' start_type query string but no current order.
|
||||||
|
// That shouldn't happen, but it's possible.
|
||||||
|
const vehicleMakeComponent = (await lazyLoadComponent('vehicle-make')()).default;
|
||||||
|
const vehicleModelComponent = (await lazyLoadComponent('vehicle-model')()).default;
|
||||||
|
const vehicleStyleComponent = (await lazyLoadComponent('vehicle-style')()).default;
|
||||||
|
const vehicleDamageComponent = (await lazyLoadComponent('vehicle-damage')()).default;
|
||||||
|
|
||||||
|
|
||||||
|
if (!vehicleMakeComponent.methods.arePagePrerequisitesValid()) {
|
||||||
|
return fmgPageValues.VEHICLE_YEAR;
|
||||||
|
} else if (!vehicleModelComponent.methods.arePagePrerequisitesValid()) {
|
||||||
|
return fmgPageValues.VEHICLE_MAKE;
|
||||||
|
} else if (!vehicleStyleComponent.methods.arePagePrerequisitesValid()) {
|
||||||
|
return fmgPageValues.VEHICLE_MODEL;
|
||||||
|
} else if (!vehicleDamageComponent.methods.arePagePrerequisitesValid()) {
|
||||||
|
return fmgPageValues.VEHICLE_STYLE;
|
||||||
|
} else if (store.getters.damage.isRepair == null || !store.getters.vehicle.carId) {
|
||||||
|
return fmgPageValues.VEHICLE_DAMAGE;
|
||||||
|
} else {
|
||||||
|
if (store.getters.vehicle.vin) {
|
||||||
|
return fmgPageValues.VIN_LOOKUP;
|
||||||
|
} else {
|
||||||
|
return fmgPageValues.ESTIMATE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Overrides functionality to go to the YMMS pages in certain cases.
|
||||||
|
If this is not one of the cases, it returns the 'to' fmgPage value.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* istanbul ignore next */
|
||||||
|
function overrideYmmsDirectionIfNeeded(toRoute) {
|
||||||
|
const fmgPageValue = toRoute.query[queryStrings.FMG_PAGE];
|
||||||
|
|
||||||
|
if (store.getters.payment.insuranceCoverage.isVerified) {
|
||||||
|
switch (fmgPageValue) {
|
||||||
|
case fmgPageValues.VEHICLE_YEAR:
|
||||||
|
case fmgPageValues.VEHICLE_MAKE:
|
||||||
|
case fmgPageValues.VEHICLE_MODEL:
|
||||||
|
case fmgPageValues.VEHICLE_STYLE:
|
||||||
|
{
|
||||||
|
return fmgPageValues.VEHICLE_DAMAGE;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
return fmgPageValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return fmgPageValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Determine if the page is a vin related page.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* istanbul ignore next */
|
||||||
|
function isVinRelatedPage(toRoute) {
|
||||||
|
const fmgPageValue = toRoute.query[queryStrings.FMG_PAGE];
|
||||||
|
|
||||||
|
return fmgPageValue === fmgPageValues.VIN_LOOKUP ||
|
||||||
|
fmgPageValue === fmgPageValues.LICENSE_PLATE_LOOKUP ||
|
||||||
|
fmgPageValue === fmgPageValues.ADDRESS_LOOKUP ||
|
||||||
|
fmgPageValue === fmgPageValues.ADDRESS_VEHICLES ||
|
||||||
|
fmgPageValue === fmgPageValues.ESTIMATE;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -228,8 +228,7 @@ describe("getPageToRouteExistingOrderTo", () => {
|
||||||
const result = await getPageToRouteExistingOrderTo(toRoute, false);
|
const result = await getPageToRouteExistingOrderTo(toRoute, false);
|
||||||
|
|
||||||
//Assert
|
//Assert
|
||||||
//expect(result).toBe('vin-lookup');
|
expect(result).toBe('vin-lookup');
|
||||||
expect(result).toBe('vehicle-damage');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("getPageToRouteExistingOrderTo, should return estimate", async () => {
|
test("getPageToRouteExistingOrderTo, should return estimate", async () => {
|
||||||
|
|
@ -284,8 +283,7 @@ describe("getPageToRouteExistingOrderTo", () => {
|
||||||
const result = await getPageToRouteExistingOrderTo(toRoute, false);
|
const result = await getPageToRouteExistingOrderTo(toRoute, false);
|
||||||
|
|
||||||
//Assert
|
//Assert
|
||||||
//expect(result).toBe('estimate');
|
expect(result).toBe('estimate');
|
||||||
expect(result).toBe('vehicle-damage');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("getPageToRouteExistingOrderTo, existing order, should return heritage", async () => {
|
test("getPageToRouteExistingOrderTo, existing order, should return heritage", async () => {
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ export async function loadOrderIfPresent() {
|
||||||
|
|
||||||
|
|
||||||
// Load referral if there is a cookie, and it doesn't indicate it needs a state reset.
|
// Load referral if there is a cookie, and it doesn't indicate it needs a state reset.
|
||||||
return (await loadOrder(funnelCookie.ReferralNumber, funnelCookie.ReferralDate, funnelCookie.ReferralCorrelationId)).data;
|
return (await loadOrder(funnelCookie.ReferralNumber, funnelCookie.ReferralDate, funnelCookie.ReferralCorrelationId, funnelCookie.ReferralParentAccountNumber)).data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -54,12 +54,13 @@ export async function saveOrder() {
|
||||||
Calls API to load order given the referral number, referralDate, and referralCorrelationId
|
Calls API to load order given the referral number, referralDate, and referralCorrelationId
|
||||||
and returns the response.
|
and returns the response.
|
||||||
*/
|
*/
|
||||||
async function loadOrder(referralNumber, referralDate, referralCorrelationId) {
|
async function loadOrder(referralNumber, referralDate, referralCorrelationId, accountNumber) {
|
||||||
const response = await baseMixin.methods.dispatchNonBlockingStoreAction(storeActions.LOAD_ORDER,
|
const response = await baseMixin.methods.dispatchNonBlockingStoreAction(storeActions.LOAD_ORDER,
|
||||||
{
|
{
|
||||||
referralNumber: referralNumber.toString(),
|
referralNumber: referralNumber.toString(),
|
||||||
referralDate: referralDate,
|
referralDate: referralDate,
|
||||||
referralCorrelationId: referralCorrelationId
|
referralCorrelationId: referralCorrelationId,
|
||||||
|
accountNumber: accountNumber?.toString()
|
||||||
}, false);
|
}, false);
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,10 @@ export function settleAllPromises(promiseResultMap) {
|
||||||
// when returned, so other promises do. Map the results to the object
|
// when returned, so other promises do. Map the results to the object
|
||||||
// so that the object is the return data.
|
// so that the object is the return data.
|
||||||
|
|
||||||
if (results[i].value.data === undefined) {
|
if (results[i]?.value?.data === undefined) {
|
||||||
resultMap[promiseName] = results[i].value;
|
resultMap[promiseName] = results[i]?.value;
|
||||||
} else {
|
} else {
|
||||||
resultMap[promiseName] = results[i].value.data;
|
resultMap[promiseName] = results[i]?.value?.data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,9 @@ export const cookies = {
|
||||||
[cookieNames.FUNNEL_SESSION_INFO]: `{"ReferralNumber":"1566818","ReferralDate":"2022-03-15T10:56:24.597","ReferralCorrelationId":"404d2b04-f86e-45c3-b373-127b6217b060","ShouldResetState":false,"DidHeritageFunnelUpdateLast":true}`,
|
[cookieNames.FUNNEL_SESSION_INFO]: `{"ReferralNumber":"1566818","ReferralDate":"2022-03-15T10:56:24.597","ReferralCorrelationId":"404d2b04-f86e-45c3-b373-127b6217b060","ShouldResetState":false,"DidHeritageFunnelUpdateLast":true}`,
|
||||||
"UNIQUE_SESSION_ID": "33756020-b58e-4ec7-b8b8-3f1576719c40",
|
"UNIQUE_SESSION_ID": "33756020-b58e-4ec7-b8b8-3f1576719c40",
|
||||||
"anotherCookie": "{}",
|
"anotherCookie": "{}",
|
||||||
"someOtherCookie": "{}"
|
"someOtherCookie": "{}",
|
||||||
|
"dxdev": "did=21b9b94a-ec23-42c1-aaac-e2ae4e4dbffe",
|
||||||
|
"skey": "12345"
|
||||||
};
|
};
|
||||||
|
|
||||||
export function removeAllTestCookies() {
|
export function removeAllTestCookies() {
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,7 @@
|
||||||
ref="funnelFooter"
|
ref="funnelFooter"
|
||||||
:isDisabled="!meta.valid"
|
:isDisabled="!meta.valid"
|
||||||
@ForwardClicked="forwardButtonAction"
|
@ForwardClicked="forwardButtonAction"
|
||||||
|
:isForwardActionDisabled="!meta.valid"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
13
src/layouts/estimate/estimate.vue
Normal file
13
src/layouts/estimate/estimate.vue
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
<template>
|
||||||
|
<p> Estimate </p>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "estimate",
|
||||||
|
methods: {
|
||||||
|
arePagePrerequisitesValid() {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
@ -44,7 +44,7 @@
|
||||||
/>
|
/>
|
||||||
<funnel-footer
|
<funnel-footer
|
||||||
ref="funnelFooter"
|
ref="funnelFooter"
|
||||||
:isDisabled="!meta.valid"
|
:isForwardActionDisabled="!meta.valid"
|
||||||
@back-clicked="backButtonAction"
|
@back-clicked="backButtonAction"
|
||||||
@ForwardClicked="forwardButtonAction"
|
@ForwardClicked="forwardButtonAction"
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,8 @@
|
||||||
</div>
|
</div>
|
||||||
<alert
|
<alert
|
||||||
class="my-3"
|
class="my-3"
|
||||||
cmsWidgetName="NoServiceZipWidget"
|
:manualHeadline="NoServiceZipHeader"
|
||||||
|
:manualCopy="NoServiceZipBody"
|
||||||
v-if="newServiceZipRequired"
|
v-if="newServiceZipRequired"
|
||||||
alertClass="alert-danger"
|
alertClass="alert-danger"
|
||||||
/>
|
/>
|
||||||
|
|
@ -38,7 +39,8 @@
|
||||||
/>
|
/>
|
||||||
<alert
|
<alert
|
||||||
class="my-3"
|
class="my-3"
|
||||||
cmsWidgetName="MatchedDifferentVehicleAlertWidget"
|
:manualHeadline="MatchedDifferentVehicleAlertHeader"
|
||||||
|
:manualCopy="MatchedDifferentVehicleAlertBody"
|
||||||
v-if="vinDoesNotMatchCarId"
|
v-if="vinDoesNotMatchCarId"
|
||||||
alertClass="alert-warning"
|
alertClass="alert-warning"
|
||||||
/>
|
/>
|
||||||
|
|
@ -50,7 +52,7 @@
|
||||||
<funnelFooter
|
<funnelFooter
|
||||||
ref="funnelFooter"
|
ref="funnelFooter"
|
||||||
cmsWidgetName="FunnelFooterWidget"
|
cmsWidgetName="FunnelFooterWidget"
|
||||||
:isDisabled="!meta.valid"
|
:isForwardActionDisabled="!meta.valid"
|
||||||
@back-clicked="backButtonAction"
|
@back-clicked="backButtonAction"
|
||||||
@ForwardClicked="forwardButtonAction"
|
@ForwardClicked="forwardButtonAction"
|
||||||
/>
|
/>
|
||||||
|
|
@ -75,8 +77,10 @@ import baseMixin from "@/mixins/base-mixin.js";
|
||||||
import { storeActions } from "@/constants/store-actions";
|
import { storeActions } from "@/constants/store-actions";
|
||||||
import { storeMutations } from "@/constants/store-mutations";
|
import { storeMutations } from "@/constants/store-mutations";
|
||||||
import { errorMessages } from "@/constants/error-messages";
|
import { errorMessages } from "@/constants/error-messages";
|
||||||
|
import { getDamageString, compareGlassOptions } from "@/helpers/damage-helper";
|
||||||
import { required, regex } from "@/helpers/validation-rules";
|
import { required, regex } from "@/helpers/validation-rules";
|
||||||
import { Form, defineRule } from "vee-validate";
|
import { Form, defineRule } from "vee-validate";
|
||||||
|
import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
|
||||||
|
|
||||||
// DEFINE VALIDATION RULES
|
// DEFINE VALIDATION RULES
|
||||||
defineRule("license-plate-required", required(errorMessages.LICENSE_PLATE_REQUIRED));
|
defineRule("license-plate-required", required(errorMessages.LICENSE_PLATE_REQUIRED));
|
||||||
|
|
@ -118,8 +122,32 @@ export default {
|
||||||
zip: '',
|
zip: '',
|
||||||
email: '',
|
email: '',
|
||||||
serviceZip: '',
|
serviceZip: '',
|
||||||
|
carIdEntered: '',
|
||||||
|
customAlertData: {},
|
||||||
|
newCarId: false,
|
||||||
|
glassOptionsMismatch: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
MatchedDifferentVehicleAlertHeader(){
|
||||||
|
let text = this.getCmsContent("MatchedDifferentVehicleAlertWidget", "HeadlineText").replaceAll("{custom:damage}", getDamageString());
|
||||||
|
|
||||||
|
return text;
|
||||||
|
},
|
||||||
|
MatchedDifferentVehicleAlertBody(){
|
||||||
|
let text = this.getCmsContent("MatchedDifferentVehicleAlertWidget", "BodyText").replaceAll("{custom:damage}", getDamageString()).replaceAll("{custom:plateLookupYear}", this.customAlertData?.vehicleInfo?.year).replaceAll("{custom:plateLookupMake}", this.customAlertData?.vehicleInfo?.make).replaceAll("{custom:plateLookupModel}", this.customAlertData?.vehicleInfo?.model);
|
||||||
|
|
||||||
|
return text;
|
||||||
|
},
|
||||||
|
NoServiceZipHeader(){
|
||||||
|
let text = this.getCmsContent("NoServiceZipWidget", "HeadlineText").replaceAll("{custom:zip}", this.zip);
|
||||||
|
|
||||||
|
return text;
|
||||||
|
},
|
||||||
|
NoServiceZipBody(){
|
||||||
|
return this.getCmsContent("NoServiceZipWidget", "BodyText");
|
||||||
|
}
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
arePagePrerequisitesValid() {
|
arePagePrerequisitesValid() {
|
||||||
return store.getters.vehicle.carId !== null;
|
return store.getters.vehicle.carId !== null;
|
||||||
|
|
@ -137,43 +165,58 @@ export default {
|
||||||
async forwardButtonAction() {
|
async forwardButtonAction() {
|
||||||
const zipValidation = this.serviceZip ? await this.validateZip(this.serviceZip) : await this.validateZip(this.zip);
|
const zipValidation = this.serviceZip ? await this.validateZip(this.serviceZip) : await this.validateZip(this.zip);
|
||||||
if (!zipValidation.data.isServiceable) {
|
if (!zipValidation.data.isServiceable) {
|
||||||
|
this.customAlertData.zip = this.zip;
|
||||||
this.$refs.funnelFooter.removeLoader();
|
this.$refs.funnelFooter.removeLoader();
|
||||||
|
this.vinDoesNotMatchCarId = false;
|
||||||
|
this.vinNotValid = false;
|
||||||
this.newServiceZipRequired = true;
|
this.newServiceZipRequired = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const vinLookup = await this.lookupVin(this.licensePlate, zipValidation.data.state).catch(() => {
|
const vinLookup = await this.lookupVin(this.licensePlate, zipValidation.data.state).catch(() => {
|
||||||
this.$refs.funnelFooter.removeLoader();
|
this.$refs.funnelFooter.removeLoader();
|
||||||
|
this.vinDoesNotMatchCarId = false;
|
||||||
this.vinNotValid = true;
|
this.vinNotValid = true;
|
||||||
return;
|
return;
|
||||||
});
|
});
|
||||||
if (vinLookup.data.vehicle.carId !== store.getters.vehicle.carId) {
|
|
||||||
|
if ((vinLookup.data.vehicle.carId !== store.getters.vehicle.carId) && (vinLookup.data.vehicle.carId !== this.carIdEntered)) {
|
||||||
|
this.carIdEntered = vinLookup.data.vehicle.carId;
|
||||||
|
this.customAlertData.vehicleInfo = vinLookup.data.vehicle;
|
||||||
|
this.newCarId = true;
|
||||||
|
const glassOptions = await baseMixin.methods.dispatchNonBlockingStoreAction(
|
||||||
|
storeActions.GET_DAMAGE_OPTIONS,
|
||||||
|
{ carId: vinLookup.data.vehicle.carId }
|
||||||
|
);
|
||||||
|
this.glassOptionsMismatch = compareGlassOptions(glassOptions.data, store.getters.damage.glassToReplace);
|
||||||
this.$refs.funnelFooter.updateButtonText(`Continue with ${vinLookup.data.vin} ${vinLookup.data.vehicle.year} ${vinLookup.data.vehicle.make} ${vinLookup.data.vehicle.model}`);
|
this.$refs.funnelFooter.updateButtonText(`Continue with ${vinLookup.data.vin} ${vinLookup.data.vehicle.year} ${vinLookup.data.vehicle.make} ${vinLookup.data.vehicle.model}`);
|
||||||
this.$refs.funnelFooter.removeLoader();
|
this.$refs.funnelFooter.removeLoader();
|
||||||
|
this.vinNotValid = false;
|
||||||
this.vinDoesNotMatchCarId = true;
|
this.vinDoesNotMatchCarId = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.updateCustomerInfo(vinLookup.data.vin, vinLookup.data.vehicle, zipValidation.data.state);
|
||||||
|
|
||||||
const partsData = await baseMixin.methods.dispatchNonBlockingStoreAction(
|
const partsData = await baseMixin.methods.dispatchNonBlockingStoreAction(
|
||||||
this.storeActions.GET_PARTS_OR_QUESTIONS,
|
this.storeActions.GET_PARTS_OR_QUESTIONS,
|
||||||
{
|
{
|
||||||
carId: store.getters.vehicle.carId,
|
carId: vinLookup.data.vehicle.carId,
|
||||||
glassArray: store.getters.damage.glassToReplace,
|
glassArray: store.getters.damage.glassToReplace ? store.getters.damage.glassToReplace : [],
|
||||||
zipCode: this.zip,
|
zipCode: this.serviceZip ? this.serviceZip : this.zip,
|
||||||
vin: vinLookup.vin
|
vin: vinLookup.data.vin
|
||||||
},
|
},
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
this.navigateForward(partsData);
|
this.navigateForward(partsData);
|
||||||
},
|
},
|
||||||
navigateForward(partsData){
|
navigateForward(partsData){
|
||||||
if(partsData.data.partsOrQuestions[0].partQuestions && partsData.data.partsOrQuestions[0].partQuestions.length > 0){
|
if(this.newCarId && this.glassOptionsMismatch){
|
||||||
this.$router.navigateAfterSave(this.navigationScenarios.CONTINUING_WITH_PARTS_QUESTION, this.$route, {}, {}, partsData.data);
|
this.$router.navigateAfterSave(this.navigationScenarios.CLICKED_FORWARD, this.$route, {}, { displayVehicleChangeAlert: true }, partsData.data);
|
||||||
return;
|
return;
|
||||||
} else if((!partsData.data.partsOrQuestions[0].partQuestions || partsData.data.partsOrQuestions[0].partQuestions.length < 1) && partsData.data.partsOrQuestions[0].parts.length > 1) {
|
|
||||||
this.$router.navigateAfterSave(this.navigationScenarios.CONTINUING_WITH_MULTIPLE_PARTS, this.$route, {}, {}, partsData.data);
|
|
||||||
return;
|
|
||||||
} else {
|
} else {
|
||||||
this.$router.navigate(this.navigationScenarios.CONTINUING_WITH_SINGLE_PART, this.$route);
|
navigateToHeritageFunnel();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
validateZip(zip) {
|
validateZip(zip) {
|
||||||
|
|
@ -188,27 +231,32 @@ export default {
|
||||||
{ licensePlate: plate, licenseState: state }
|
{ licensePlate: plate, licenseState: state }
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
updateStore() {
|
updateCustomerInfo(vin, vehicleInfo, registrationState) {
|
||||||
// if(vehicleDamage){
|
if(this.newCarId && this.glassOptionsMismatch){
|
||||||
// store.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
|
store.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
|
||||||
// }
|
}
|
||||||
store.commit(storeMutations.UPDATE_VEHICLE_VIN, null);
|
store.commit(storeMutations.UPDATE_VEHICLE_VIN, vin);
|
||||||
store.commit(storeMutations.UPDATE_YEAR, null);
|
store.commit(storeMutations.UPDATE_YEAR, vehicleInfo.year);
|
||||||
store.commit(storeMutations.UPDATE_MAKE, null);
|
store.commit(storeMutations.UPDATE_MAKE, vehicleInfo.make);
|
||||||
store.commit(storeMutations.UPDATE_MODEL, null);
|
store.commit(storeMutations.UPDATE_MODEL, vehicleInfo.model);
|
||||||
store.commit(storeMutations.UPDATE_STYLE, null);
|
store.commit(storeMutations.UPDATE_STYLE, vehicleInfo.style);
|
||||||
store.commit(storeMutations.UPDATE_CAR_ID, null);
|
store.commit(storeMutations.UPDATE_CAR_ID, vehicleInfo.carId);
|
||||||
store.commit(storeMutations.UPDATE_VEHICLE_CATEGORY, null);
|
store.commit(storeMutations.UPDATE_VEHICLE_CATEGORY, vehicleInfo.category);
|
||||||
store.commit(storeMutations.UPDATE_VEHICLE_IMAGE_URL, null);
|
store.commit(storeMutations.UPDATE_VEHICLE_IMAGE_URL, vehicleInfo.imageUrl);
|
||||||
store.commit(storeMutations.UPDATE_VEHICLE_IMAGE_VIF_NUMBER, null);
|
store.commit(storeMutations.UPDATE_VEHICLE_IMAGE_VIF_NUMBER, vehicleInfo.imageVifNumber);
|
||||||
store.commit(storeMutations.UPDATE_VEHICLE_IMAGE_COLOR, null);
|
store.commit(storeMutations.UPDATE_VEHICLE_IMAGE_COLOR, vehicleInfo.imageColor);
|
||||||
store.commit(storeMutations.UPDATE_REGISTRATION_LICENSE_PLATE, null);
|
store.commit(storeMutations.UPDATE_REGISTRATION_LICENSE_PLATE, this.licensePlate);
|
||||||
store.commit(storeMutations.UPDATE_REGISTRATION_STATE, null);
|
store.commit(storeMutations.UPDATE_REGISTRATION_STATE, registrationState);
|
||||||
store.commit(storeMutations.UPDATE_REGISTRATION_ZIP_CODE, null);
|
store.commit(storeMutations.UPDATE_REGISTRATION_ZIP_CODE, this.zip);
|
||||||
store.commit(storeMutations.UPDATE_SERVICE_LOCATION_ZIP, null);
|
store.commit(storeMutations.UPDATE_SERVICE_LOCATION_ZIP, this.serviceZip);
|
||||||
store.commit(storeMutations.UPDATE_CUSTOMER_EMAIL_ADDRESS, null);
|
store.commit(storeMutations.UPDATE_CUSTOMER_EMAIL_ADDRESS, this.email);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
licensePlate() {
|
||||||
|
this.$refs.funnelFooter.updateButtonText(this.getCmsContent("FunnelFooterWidget", "ForwardButtonText"));
|
||||||
|
}
|
||||||
|
},
|
||||||
components: {
|
components: {
|
||||||
Form,
|
Form,
|
||||||
funnelHeader,
|
funnelHeader,
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="container-fluid shadow rounded-3 p-2 position-relative make-tall">
|
<div class="container-fluid shadow rounded-3 p-2 position-relative make-tall">
|
||||||
<funnelHeader ref="funnelHeader" />
|
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
|
||||||
<vehicleBanner ref="vehicleBanner" :displayGenericVehicleImage="false" />
|
<vehicleBanner cmsWidgetName="VehicleBannerWidget" :displayGenericVehicleImage=false />
|
||||||
<funnelSubHeader ref="funnelSubHeader" />
|
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" />
|
||||||
<h1>Part Questions Page Placeholder</h1>
|
<h1>Part Questions Page Placeholder</h1>
|
||||||
<funnelFooter ref="funnelFooter" @back-clicked="backButtonAction" />
|
<funnel-footer
|
||||||
|
cmsWidgetName="FunnelFooterWidget" @back-clicked="backButtonAction"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -49,18 +51,7 @@ export default {
|
||||||
|
|
||||||
// Call the "next" function to complete the transition to this page.
|
// Call the "next" function to complete the transition to this page.
|
||||||
next((vm) => {
|
next((vm) => {
|
||||||
vm.$refs.funnelHeader.initializeComponent(
|
vm.setCmsContent(resultMap.cmsContent);
|
||||||
resultMap.cmsContent.FunnelHeaderWidget
|
|
||||||
);
|
|
||||||
vm.$refs.vehicleBanner.initializeComponent(
|
|
||||||
resultMap.cmsContent.VehicleBannerWidget
|
|
||||||
);
|
|
||||||
vm.$refs.funnelSubHeader.initializeComponent(
|
|
||||||
resultMap.cmsContent.FunnelSubHeaderWidget
|
|
||||||
);
|
|
||||||
vm.$refs.funnelFooter.initializeComponent(
|
|
||||||
resultMap.cmsContent.FunnelFooterWidget
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,11 @@ jest.mock("@/store", () => ({
|
||||||
vehicle: {
|
vehicle: {
|
||||||
carId: "C00000000",
|
carId: "C00000000",
|
||||||
image: "test.jpg",
|
image: "test.jpg",
|
||||||
|
payment: {
|
||||||
|
insuranceCoverage: {
|
||||||
|
isVerified: false
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
eventBusItem: jest.fn(),
|
eventBusItem: jest.fn(),
|
||||||
damage: {
|
damage: {
|
||||||
|
|
@ -134,6 +139,7 @@ describe("vehicle-damage.vue", () => {
|
||||||
store: {
|
store: {
|
||||||
getters: {
|
getters: {
|
||||||
vehicle: {},
|
vehicle: {},
|
||||||
|
payment: { insuranceCoverage: { isVerified: false } },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -201,6 +207,7 @@ describe("vehicle-damage.vue", () => {
|
||||||
store: {
|
store: {
|
||||||
getters: {
|
getters: {
|
||||||
vehicle: {},
|
vehicle: {},
|
||||||
|
payment: { insuranceCoverage: { isVerified: false } },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -291,6 +298,7 @@ describe("vehicle-damage.vue", () => {
|
||||||
store: {
|
store: {
|
||||||
getters: {
|
getters: {
|
||||||
vehicle: {},
|
vehicle: {},
|
||||||
|
payment: { insuranceCoverage: { isVerified: false } },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -336,6 +344,7 @@ describe("vehicle-damage.vue", () => {
|
||||||
store: {
|
store: {
|
||||||
getters: {
|
getters: {
|
||||||
vehicle: {},
|
vehicle: {},
|
||||||
|
payment: { insuranceCoverage: { isVerified: false } },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -734,6 +743,7 @@ function setupMocks({
|
||||||
store: {
|
store: {
|
||||||
getters: {
|
getters: {
|
||||||
vehicle: {},
|
vehicle: {},
|
||||||
|
payment: { insuranceCoverage: { isVerified: false } },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,8 @@
|
||||||
/>
|
/>
|
||||||
<funnel-footer
|
<funnel-footer
|
||||||
cmsWidgetName="FunnelFooterWidget"
|
cmsWidgetName="FunnelFooterWidget"
|
||||||
:isDisabled="!meta.valid"
|
:isForwardActionDisabled="!meta.valid"
|
||||||
|
:isBackButtonHidden=shouldHideBackButton
|
||||||
@back-clicked="backButtonAction"
|
@back-clicked="backButtonAction"
|
||||||
@ForwardClicked="forwardButtonAction"
|
@ForwardClicked="forwardButtonAction"
|
||||||
/>
|
/>
|
||||||
|
|
@ -145,9 +146,11 @@ export default {
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
|
||||||
resetDependentState() {
|
resetDependentState() {
|
||||||
store.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES);
|
store.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES);
|
||||||
},
|
},
|
||||||
|
|
||||||
backButtonAction() {
|
backButtonAction() {
|
||||||
// route to move backwards
|
// route to move backwards
|
||||||
this.$router.navigate(
|
this.$router.navigate(
|
||||||
|
|
@ -155,19 +158,20 @@ export default {
|
||||||
this.$route
|
this.$route
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
getDamageLocationsFromStore() {
|
getDamageLocationsFromStore() {
|
||||||
var glassSelections = [];
|
var glassSelections = [];
|
||||||
|
|
||||||
if (store.getters.damage.glassToReplace.some(glass => { return glass.location === damageLocationsSelected.WINDSHIELD }) ||
|
if (store.getters.damage.glassToReplace?.some(glass => { return glass.location === damageLocationsSelected.WINDSHIELD }) ||
|
||||||
store.getters.damage.isRepair) {
|
store.getters.damage.isRepair) {
|
||||||
glassSelections.push(damageLocationsSelected.WINDSHIELD);
|
glassSelections.push(damageLocationsSelected.WINDSHIELD);
|
||||||
}
|
}
|
||||||
if (store.getters.damage.glassToReplace.some(glass => { return glass.location === damageLocationsSelected.DRIVER ||
|
if (store.getters.damage.glassToReplace?.some(glass => { return glass.location === damageLocationsSelected.DRIVER ||
|
||||||
glass.location === damageLocationsSelected.PASSENGER })) {
|
glass.location === damageLocationsSelected.PASSENGER })) {
|
||||||
glassSelections.push(damageLocationsSelected.SIDEDOOR);
|
glassSelections.push(damageLocationsSelected.SIDEDOOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (store.getters.damage.glassToReplace.some(glass => { return glass.location === damageLocationsSelected.REAR })) {
|
if (store.getters.damage.glassToReplace?.some(glass => { return glass.location === damageLocationsSelected.REAR })) {
|
||||||
glassSelections.push(damageLocationsSelected.REARWINDOW);
|
glassSelections.push(damageLocationsSelected.REARWINDOW);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -180,19 +184,19 @@ export default {
|
||||||
if (store.getters.damage.isRepair === undefined) return windshieldOptions;
|
if (store.getters.damage.isRepair === undefined) return windshieldOptions;
|
||||||
|
|
||||||
if (!store.getters.damage.isRepair) {
|
if (!store.getters.damage.isRepair) {
|
||||||
if (store.getters.damage.glassToReplace.some(glass => { return glass.location === damageLocationsSelected.WINDSHIELD &&
|
if (store.getters.damage.glassToReplace?.some(glass => { return glass.location === damageLocationsSelected.WINDSHIELD &&
|
||||||
glass.name === damageLocationsSelected.SINGLE })) {
|
glass.name === damageLocationsSelected.SINGLE })) {
|
||||||
windShieldOptions.selectedWindshieldDamageType.push(damageLocationsSelected.REPLACE);
|
windShieldOptions.selectedWindshieldDamageType.push(damageLocationsSelected.REPLACE);
|
||||||
windShieldOptions.selectedWindshieldReplaceOptions.push(damageLocationsSelected.SINGLE);
|
windShieldOptions.selectedWindshieldReplaceOptions.push(damageLocationsSelected.SINGLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (store.getters.damage.glassToReplace.some(glass => { return glass.location === damageLocationsSelected.WINDSHIELD &&
|
if (store.getters.damage.glassToReplace?.some(glass => { return glass.location === damageLocationsSelected.WINDSHIELD &&
|
||||||
glass.name === damageLocationsSelected.DRIVER })) {
|
glass.name === damageLocationsSelected.DRIVER })) {
|
||||||
windShieldOptions.selectedWindshieldDamageType.push(damageLocationsSelected.REPLACE);
|
windShieldOptions.selectedWindshieldDamageType.push(damageLocationsSelected.REPLACE);
|
||||||
windShieldOptions.selectedWindshieldReplaceOptions.push(damageLocationsSelected.DRIVER);
|
windShieldOptions.selectedWindshieldReplaceOptions.push(damageLocationsSelected.DRIVER);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (store.getters.damage.glassToReplace.some(glass => { return glass.location === damageLocationsSelected.WINDSHIELD &&
|
if (store.getters.damage.glassToReplace?.some(glass => { return glass.location === damageLocationsSelected.WINDSHIELD &&
|
||||||
glass.name === damageLocationsSelected.PASSENGER })) {
|
glass.name === damageLocationsSelected.PASSENGER })) {
|
||||||
windShieldOptions.selectedWindshieldDamageType.push(damageLocationsSelected.REPLACE);
|
windShieldOptions.selectedWindshieldDamageType.push(damageLocationsSelected.REPLACE);
|
||||||
windShieldOptions.selectedWindshieldReplaceOptions.push(damageLocationsSelected.PASSENGER);
|
windShieldOptions.selectedWindshieldReplaceOptions.push(damageLocationsSelected.PASSENGER);
|
||||||
|
|
@ -210,11 +214,11 @@ export default {
|
||||||
|
|
||||||
getDoorSidesFromStore() {
|
getDoorSidesFromStore() {
|
||||||
var doorSides = [];
|
var doorSides = [];
|
||||||
if (store.getters.damage.glassToReplace.some(glass => { return glass.location === damageLocationsSelected.DRIVER })){
|
if (store.getters.damage.glassToReplace?.some(glass => { return glass.location === damageLocationsSelected.DRIVER })){
|
||||||
doorSides.push(damageLocationsSelected.DRIVERSIDE);
|
doorSides.push(damageLocationsSelected.DRIVERSIDE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (store.getters.damage.glassToReplace.some(glass => { return glass.location === damageLocationsSelected.PASSENGER })){
|
if (store.getters.damage.glassToReplace?.some(glass => { return glass.location === damageLocationsSelected.PASSENGER })){
|
||||||
doorSides.push(damageLocationsSelected.PASSENGERSIDE);
|
doorSides.push(damageLocationsSelected.PASSENGERSIDE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -224,7 +228,7 @@ export default {
|
||||||
getDriverSideReplaceOptionsFromStore() {
|
getDriverSideReplaceOptionsFromStore() {
|
||||||
var driverSideReplaceOptions = [];
|
var driverSideReplaceOptions = [];
|
||||||
|
|
||||||
store.getters.damage.glassToReplace.forEach(glass => {
|
store.getters.damage.glassToReplace?.forEach(glass => {
|
||||||
if (glass.location === damageLocationsSelected.DRIVER){
|
if (glass.location === damageLocationsSelected.DRIVER){
|
||||||
driverSideReplaceOptions.push(glass.name);
|
driverSideReplaceOptions.push(glass.name);
|
||||||
}
|
}
|
||||||
|
|
@ -236,7 +240,7 @@ export default {
|
||||||
getPassengerSideReplaceOptionsFromStore() {
|
getPassengerSideReplaceOptionsFromStore() {
|
||||||
var passengerSideReplaceOptions = [];
|
var passengerSideReplaceOptions = [];
|
||||||
|
|
||||||
store.getters.damage.glassToReplace.forEach(glass => {
|
store.getters.damage.glassToReplace?.forEach(glass => {
|
||||||
if (glass.location === damageLocationsSelected.PASSENGER){
|
if (glass.location === damageLocationsSelected.PASSENGER){
|
||||||
passengerSideReplaceOptions.push(glass.name);
|
passengerSideReplaceOptions.push(glass.name);
|
||||||
}
|
}
|
||||||
|
|
@ -248,7 +252,7 @@ export default {
|
||||||
getRearReplaceOptionsFromStore(){
|
getRearReplaceOptionsFromStore(){
|
||||||
var rearReplaceOptions = [];
|
var rearReplaceOptions = [];
|
||||||
|
|
||||||
store.getters.damage.glassToReplace.forEach(glass => {
|
store.getters.damage.glassToReplace?.forEach(glass => {
|
||||||
if (glass.location === damageLocationsSelected.REAR){
|
if (glass.location === damageLocationsSelected.REAR){
|
||||||
rearReplaceOptions.push(glass.name);
|
rearReplaceOptions.push(glass.name);
|
||||||
}
|
}
|
||||||
|
|
@ -275,13 +279,20 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
navigateForward(partsData){
|
navigateForward(partsData){
|
||||||
// CSR-98 TEMP
|
|
||||||
const vehicleYearsToShowHeritageFunnel = [ "2001", "2002", "2010" ];
|
// Temporary easter egg to navigate to heritage funnel.
|
||||||
|
const vehicleYearsToShowHeritageFunnel = [ 2001, 2002, 2010, 2016 ];
|
||||||
if (vehicleYearsToShowHeritageFunnel.includes(store.getters.vehicle.year)) {
|
if (vehicleYearsToShowHeritageFunnel.includes(store.getters.vehicle.year)) {
|
||||||
navigateToHeritageFunnel();
|
navigateToHeritageFunnel();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If vin already exists, navigate directly to vin-lookup
|
||||||
|
if(this.$store.getters.vehicle.vin){
|
||||||
|
this.$router.navigate(this.navigationScenarios.CLICKED_FORWARD_WITH_VIN, this.$route);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
//found problem questions
|
//found problem questions
|
||||||
if (partsData.data.partsOrQuestions.some(pq => pq.partQuestions != null && pq.partQuestions.length > 0)){
|
if (partsData.data.partsOrQuestions.some(pq => pq.partQuestions != null && pq.partQuestions.length > 0)){
|
||||||
this.$router.navigateAfterSave(this.navigationScenarios.SELECTED_DAMAGE_WITH_PART_QUESTIONS, this.$route, {}, {}, partsData.data);
|
this.$router.navigateAfterSave(this.navigationScenarios.SELECTED_DAMAGE_WITH_PART_QUESTIONS, this.$route, {}, {}, partsData.data);
|
||||||
|
|
@ -382,7 +393,7 @@ export default {
|
||||||
return this.isWindshieldDamageLocation && this.selectedDamageLocations.length > 1 && this.isWindshieldRepair;
|
return this.isWindshieldDamageLocation && this.selectedDamageLocations.length > 1 && this.isWindshieldRepair;
|
||||||
},
|
},
|
||||||
hasSplitSingleConflict() {
|
hasSplitSingleConflict() {
|
||||||
if (!this.selectedWindshieldOptions.selectedWindshieldReplaceOptions) return false;
|
if (!this.selectedDamageLocations || !this.selectedDamageLocations.includes("Windshield") || !this.selectedWindshieldOptions.selectedWindshieldDamageType || !this.selectedWindshieldOptions.selectedWindshieldDamageType.includes("Replace") || !this.selectedWindshieldOptions.selectedWindshieldReplaceOptions) return false;
|
||||||
|
|
||||||
return this.selectedWindshieldOptions.selectedWindshieldReplaceOptions.some(selectedSingleWindshield =>
|
return this.selectedWindshieldOptions.selectedWindshieldReplaceOptions.some(selectedSingleWindshield =>
|
||||||
{
|
{
|
||||||
|
|
@ -398,6 +409,9 @@ export default {
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
shouldHideBackButton(){
|
||||||
|
return this.$store.getters.payment.insuranceCoverage.isVerified;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
|
|
|
||||||
|
|
@ -17,11 +17,16 @@ import yearQuestion from "@/layouts/vehicle-year/year-question/year-question";
|
||||||
import funnelHeader from "@/common-components/funnel-header/funnel-header";
|
import funnelHeader from "@/common-components/funnel-header/funnel-header";
|
||||||
import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner";
|
import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner";
|
||||||
import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header";
|
import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header";
|
||||||
|
|
||||||
// Supporting files
|
// Supporting files
|
||||||
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||||
import { settleAllPromises } from "@/helpers/layout-helper";
|
import { settleAllPromises } from "@/helpers/layout-helper";
|
||||||
import { storeMutations } from "@/constants/store-mutations";
|
import { storeMutations } from "@/constants/store-mutations";
|
||||||
import { storeActions } from "@/constants/store-actions";
|
import { storeActions } from "@/constants/store-actions";
|
||||||
|
import { experimentUniverses } from "@/constants/experiments";
|
||||||
|
import { getDeviceIdValue, getSessionIdValue, getSessionKeyValue } from "@/helpers/heritage-integration/cookie-helper";
|
||||||
|
|
||||||
|
import baseMixin from "@/mixins/base-mixin";
|
||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
@ -36,8 +41,16 @@ export default {
|
||||||
async beforeRouteEnter(to, from, next) {
|
async beforeRouteEnter(to, from, next) {
|
||||||
// Call APIs
|
// Call APIs
|
||||||
const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage);
|
const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage);
|
||||||
const yearQuestionInitialDataPromise =
|
const yearQuestionInitialDataPromise = yearQuestion.methods.loadInitialData();
|
||||||
yearQuestion.methods.loadInitialData();
|
|
||||||
|
// Log experiment exposure
|
||||||
|
const logExperimentExposurePromise = baseMixin.methods.dispatchNonBlockingStoreAction(storeActions.LOG_EXPERIMENT_EXPOSURE,
|
||||||
|
{
|
||||||
|
userId: getDeviceIdValue(),
|
||||||
|
sessionKey: getSessionKeyValue(),
|
||||||
|
pageName: to.query.fmgPage,
|
||||||
|
universeName: experimentUniverses.CONCEPT_FUNNEL
|
||||||
|
}, false);
|
||||||
|
|
||||||
// Settle promises and get results
|
// Settle promises and get results
|
||||||
const promiseResultMap = [
|
const promiseResultMap = [
|
||||||
|
|
@ -49,6 +62,10 @@ export default {
|
||||||
resultKey: "yearQuestionInitialData",
|
resultKey: "yearQuestionInitialData",
|
||||||
promise: yearQuestionInitialDataPromise,
|
promise: yearQuestionInitialDataPromise,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
resultKey: "logExperimentExposure",
|
||||||
|
promise: logExperimentExposurePromise,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
let resultMap = await settleAllPromises(promiseResultMap);
|
let resultMap = await settleAllPromises(promiseResultMap);
|
||||||
|
|
@ -64,8 +81,8 @@ export default {
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
selectedYear(year) {
|
selectedYear(year) {
|
||||||
|
const parsedYear = parseInt(year);
|
||||||
this.$store.commit(this.storeMutations.UPDATE_YEAR, year);
|
this.$store.commit(this.storeMutations.UPDATE_YEAR, parsedYear);
|
||||||
this.$router.navigateAfterSave(
|
this.$router.navigateAfterSave(
|
||||||
this.navigationScenarios.SELECTED_YEAR,
|
this.navigationScenarios.SELECTED_YEAR,
|
||||||
this.$route
|
this.$route
|
||||||
|
|
|
||||||
|
|
@ -1,41 +1,272 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="container-fluid shadow rounded-3 px-5 position-relative make-tall">
|
<Form
|
||||||
<funnelHeader ref="funnelHeader" />
|
@submit="onSubmit"
|
||||||
<vehicleBanner ref="vehicleBanner" />
|
@invalid-submit="onInvalidSubmit"
|
||||||
<funnelSubHeader ref="funnelSubHeader" />
|
ref="theForm"
|
||||||
<vinInformation
|
v-slot="{ meta }"
|
||||||
class="mb-5"
|
>
|
||||||
/>
|
<div class="container-fluid shadow rounded-3 p-2 position-relative make-tall px-5">
|
||||||
<funnel-footer
|
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
|
||||||
ref="funnelFooter"
|
<vehicleBanner cmsWidgetName="VehicleBannerWidget" :displayGenericVehicleImage="false" />
|
||||||
:isDisabled="!meta.valid"
|
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" />
|
||||||
@back-clicked="backButtonAction"
|
<div class="row my-2">
|
||||||
@ForwardClicked="forwardButtonAction"
|
<div class="col">
|
||||||
/>
|
<textboxQuestion cmsWidgetName="VinNumber" v-model="vin" inputId="vin" disableAutoFill validationRules="vin-required|vin-format" :isDisabled=isVinFieldReadOnly />
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row my-2">
|
||||||
|
<div class="col">
|
||||||
|
<vinInformation />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row my-2">
|
||||||
|
<div class="col">
|
||||||
|
<textboxQuestion cmsWidgetName="ServiceZIP" v-model="zip" inputId="zip" mask="#####" disableAutoFill validationRules="zip-required" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row my-2">
|
||||||
|
<div class="col">
|
||||||
|
<textboxQuestion cmsWidgetName="EmailAddress" v-model="email" inputId="email" disableAutoFill validationRules="email-address-required|email-address-format" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<alert
|
||||||
|
class="my-3"
|
||||||
|
v-model="customAlertData"
|
||||||
|
v-if="matchedDifferentVehicle"
|
||||||
|
alertClass="alert-danger"
|
||||||
|
cmsWidgetName="MatchedDifferentVehicle"
|
||||||
|
/>
|
||||||
|
<alert
|
||||||
|
class="my-3"
|
||||||
|
v-model="customAlertData"
|
||||||
|
v-if="noMatchAlert"
|
||||||
|
alertClass="alert-warning"
|
||||||
|
cmsWidgetName="NoMatchAlertWidget"
|
||||||
|
/>
|
||||||
|
<alert
|
||||||
|
class="my-3"
|
||||||
|
v-model="customAlertData"
|
||||||
|
v-if="noServiceZip"
|
||||||
|
alertClass="alert-warning"
|
||||||
|
cmsWidgetName="NoServiceZipWidget"
|
||||||
|
/>
|
||||||
|
<alert
|
||||||
|
class="my-3"
|
||||||
|
v-model="customAlertData"
|
||||||
|
v-if="vinFound"
|
||||||
|
alertClass="alert-warning"
|
||||||
|
cmsWidgetName="VinFoundWidget"
|
||||||
|
/>
|
||||||
|
<alert
|
||||||
|
class="my-3"
|
||||||
|
v-model="customAlertData"
|
||||||
|
v-if="vinFoundReadOnly"
|
||||||
|
alertClass="alert-warning"
|
||||||
|
cmsWidgetName="VinFoundReadOnlyWidget"
|
||||||
|
/>
|
||||||
|
<alert
|
||||||
|
class="my-3"
|
||||||
|
v-model="customAlertData"
|
||||||
|
v-if="foundWindshieldAlert"
|
||||||
|
alertClass="alert-warning"
|
||||||
|
cmsWidgetName="FoundWindshieldAlert"
|
||||||
|
/>
|
||||||
|
<alert
|
||||||
|
class="my-3"
|
||||||
|
v-model="customAlertData"
|
||||||
|
v-if="vinNotFound"
|
||||||
|
alertClass="alert-warning"
|
||||||
|
cmsWidgetName="VinNotFound"
|
||||||
|
/>
|
||||||
|
<alert
|
||||||
|
class="my-3"
|
||||||
|
v-model="customAlertData"
|
||||||
|
v-if="perfectMatchNewVinAlert"
|
||||||
|
alertClass="alert-warning"
|
||||||
|
cmsWidgetName="PerfectMatchNewVinAlert"
|
||||||
|
/>
|
||||||
|
<funnelFooter
|
||||||
|
cmsWidgetName="FunnelFooterWidget"
|
||||||
|
ref="funnelFooter"
|
||||||
|
:isDisabled="!meta.valid"
|
||||||
|
@back-clicked="backButtonAction"
|
||||||
|
@ForwardClicked="forwardButtonAction"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// Components
|
// Components
|
||||||
import funnelHeader from "@/common-components/funnel-header/funnel-header";
|
import funnelHeader from "@/common-components/funnel-header/funnel-header";
|
||||||
|
import funnelFooter from "@/common-components/funnel-footer/funnel-footer";
|
||||||
import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner";
|
import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner";
|
||||||
import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header";
|
import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header";
|
||||||
import funnelFooter from "@/common-components/funnel-footer/funnel-footer";
|
import alert from "@/ux-components/alert/alert";
|
||||||
import textboxQuestion from "@/common-components/textbox-question/textbox-question";
|
import textboxQuestion from "@/common-components/textbox-question/textbox-question";
|
||||||
import vinInformation from "@/layouts/vin-lookup/vin-information/vin-information";
|
import vinInformation from "@/layouts/vin-lookup/vin-information/vin-information";
|
||||||
|
|
||||||
|
// Supporting files
|
||||||
|
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||||
|
import { settleAllPromises } from "@/helpers/layout-helper";
|
||||||
|
import store from "@/store";
|
||||||
|
import baseMixin from "@/mixins/base-mixin.js";
|
||||||
|
import { storeActions } from "@/constants/store-actions";
|
||||||
|
import { storeMutations } from "@/constants/store-mutations";
|
||||||
|
import { errorMessages } from "@/constants/error-messages";
|
||||||
|
import { required, regex } from "@/helpers/validation-rules";
|
||||||
|
import { Form, defineRule } from "vee-validate";
|
||||||
|
|
||||||
|
// DEFINE VALIDATION RULES
|
||||||
|
defineRule("zip-required", required(errorMessages.ZIP_REQUIRED));
|
||||||
|
defineRule("email-address-required", required(errorMessages.EMAIL_ADDRESS_REQUIRED));
|
||||||
|
defineRule("email-address-format", regex(/^([a-zA-Z0-9_\-.+]+)@([a-zA-Z0-9_\-.]+).([a-zA-Z]{2,})$/, errorMessages.EMAIL_ADDRESS_FORMAT));
|
||||||
|
defineRule("vin-required", required(errorMessages.VIN_REQUIRED));
|
||||||
|
defineRule("vin-format", regex(/^[A-HJ-NPR-Z0-9]{17}$/, errorMessages.VIN_FORMAT));
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "vin-lookup",
|
name: "vin-lookup",
|
||||||
|
async beforeRouteEnter(to, from, next) {
|
||||||
|
// Call APIs
|
||||||
|
const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage);
|
||||||
|
|
||||||
|
|
||||||
|
// Settle promises and get results
|
||||||
|
const promiseResultMap = [
|
||||||
|
{
|
||||||
|
resultKey: "cmsContent",
|
||||||
|
promise: cmsContentPromise,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const resultMap = await settleAllPromises(promiseResultMap);
|
||||||
|
|
||||||
|
// Call the "next" function to complete the transition to this page.
|
||||||
|
next((vm) => {
|
||||||
|
vm.setCmsContent(resultMap.cmsContent);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
validationRules: String,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
matchedDifferentVehicle: false,
|
||||||
|
noMatchAlert: false,
|
||||||
|
noServiceZip: false,
|
||||||
|
vinFound: false,
|
||||||
|
vinFoundReadOnly: false,
|
||||||
|
foundWindshieldAlert: false,
|
||||||
|
vinNotFound: false,
|
||||||
|
perfectMatchNewVinAlert: false,
|
||||||
|
vin: '',
|
||||||
|
zip: '',
|
||||||
|
email: '',
|
||||||
|
customAlertData: {},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
arePagePrerequisitesValid() {
|
||||||
|
return store.getters.vehicle.carId !== null;
|
||||||
|
},
|
||||||
|
resetDependentState() {
|
||||||
|
store.commit(storeMutations.UPDATE_REGISTRATION_ADDRESS, null);
|
||||||
|
store.commit(storeMutations.UPDATE_REGISTRATION_CITY, null);
|
||||||
|
store.commit(storeMutations.UPDATE_REGISTRATION_FIRST_NAME, null);
|
||||||
|
store.commit(storeMutations.UPDATE_REGISTRATION_LAST_NAME, null);
|
||||||
|
store.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES);
|
||||||
|
},
|
||||||
|
backButtonAction() {
|
||||||
|
this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route);
|
||||||
|
},
|
||||||
|
async forwardButtonAction() {
|
||||||
|
const zipValidation = await this.validateZip(this.zip);
|
||||||
|
if (!zipValidation.data.isServiceable) {
|
||||||
|
this.customAlertData.zip = this.zip;
|
||||||
|
this.$refs.funnelFooter.removeLoader();
|
||||||
|
this.noServiceZip = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const vinLookup = await this.lookupVin(this.vin).catch(() => {
|
||||||
|
this.$refs.funnelFooter.removeLoader();
|
||||||
|
this.noMatchAlert = true;
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
if (vinLookup.data.carId !== store.getters.vehicle.carId) {
|
||||||
|
this.customAlertData.vehicleInfo = vinLookup.data.vehicle;
|
||||||
|
this.$refs.funnelFooter.removeLoader();
|
||||||
|
this.foundWindshieldAlert = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const carInfo = this.vinDoesNotMatchCarId ? vinLookup.data : store.getters.vehicle;
|
||||||
|
this.updateStore(carInfo)
|
||||||
|
const partsData = await baseMixin.methods.dispatchNonBlockingStoreAction(
|
||||||
|
this.storeActions.GET_PARTS_OR_QUESTIONS,
|
||||||
|
{
|
||||||
|
carId: store.getters.vehicle.carId,
|
||||||
|
glassArray: store.getters.damage.glassToReplace,
|
||||||
|
zipCode: this.zip,
|
||||||
|
vin: vinLookup.vin
|
||||||
|
},
|
||||||
|
false
|
||||||
|
);
|
||||||
|
this.navigateForward(partsData);
|
||||||
|
},
|
||||||
|
navigateForward(partsData){
|
||||||
|
if(partsData.data.partsOrQuestions[0].partQuestions && partsData.data.partsOrQuestions[0].partQuestions.length > 0){
|
||||||
|
this.$router.navigateAfterSave(this.navigationScenarios.CONTINUING_WITH_PARTS_QUESTION, this.$route, {}, {}, partsData.data);
|
||||||
|
return;
|
||||||
|
} else if((!partsData.data.partsOrQuestions[0].partQuestions || partsData.data.partsOrQuestions[0].partQuestions.length < 1) && partsData.data.partsOrQuestions[0].parts.length > 1) {
|
||||||
|
this.$router.navigateAfterSave(this.navigationScenarios.CONTINUING_WITH_MULTIPLE_PARTS, this.$route, {}, {}, partsData.data);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
this.$router.navigate(this.navigationScenarios.CONTINUING_WITH_SINGLE_PART, this.$route);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
validateZip(zip) {
|
||||||
|
return baseMixin.methods.dispatchNonBlockingStoreAction(
|
||||||
|
storeActions.VALIDATE_ZIP,
|
||||||
|
{ zip }
|
||||||
|
);
|
||||||
|
},
|
||||||
|
lookupVin(vin) {
|
||||||
|
return baseMixin.methods.dispatchNonBlockingStoreAction(
|
||||||
|
storeActions.LOOKUP_VEHICLE_BY_VIN,
|
||||||
|
{ vin }
|
||||||
|
);
|
||||||
|
},
|
||||||
|
updateStore(carInfo) {
|
||||||
|
// if(vehicleDamage){
|
||||||
|
// store.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
|
||||||
|
// }
|
||||||
|
store.commit(storeMutations.UPDATE_VEHICLE_VIN, this.vin);
|
||||||
|
store.commit(storeMutations.UPDATE_YEAR, carInfo.year);
|
||||||
|
store.commit(storeMutations.UPDATE_MAKE, carInfo.make);
|
||||||
|
store.commit(storeMutations.UPDATE_MODEL, carInfo.model);
|
||||||
|
store.commit(storeMutations.UPDATE_STYLE, carInfo.style);
|
||||||
|
store.commit(storeMutations.UPDATE_CAR_ID, carInfo.carId);
|
||||||
|
store.commit(storeMutations.UPDATE_VEHICLE_CATEGORY, carInfo.category);
|
||||||
|
store.commit(storeMutations.UPDATE_VEHICLE_IMAGE_URL, carInfo.imageUrl);
|
||||||
|
store.commit(storeMutations.UPDATE_VEHICLE_IMAGE_VIF_NUMBER, carInfo.imageVifNumber);
|
||||||
|
store.commit(storeMutations.UPDATE_VEHICLE_IMAGE_COLOR, carInfo.imageColor);
|
||||||
|
store.commit(storeMutations.UPDATE_SERVICE_LOCATION_ZIP, this.zip);
|
||||||
|
store.commit(storeMutations.UPDATE_CUSTOMER_EMAIL_ADDRESS, this.email);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
isVinFieldReadOnly(){
|
||||||
|
return this.$store.getters.payment.insuranceCoverage.isVerified;
|
||||||
|
}
|
||||||
|
},
|
||||||
components: {
|
components: {
|
||||||
|
Form,
|
||||||
funnelHeader,
|
funnelHeader,
|
||||||
vehicleBanner,
|
vehicleBanner,
|
||||||
funnelSubHeader,
|
funnelSubHeader,
|
||||||
vinInformation,
|
textboxQuestion,
|
||||||
|
alert,
|
||||||
funnelFooter,
|
funnelFooter,
|
||||||
|
vinInformation,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import { storeActions } from "@/constants/store-actions.js";
|
||||||
import { storeMutations } from "@/constants/store-mutations.js";
|
import { storeMutations } from "@/constants/store-mutations.js";
|
||||||
import { navigationScenarios } from "@/router/router-constants/navigation-scenarios";
|
import { navigationScenarios } from "@/router/router-constants/navigation-scenarios";
|
||||||
import { vehicleCategories } from "@/constants/vehicle-categories.js";
|
import { vehicleCategories } from "@/constants/vehicle-categories.js";
|
||||||
|
import { queryStrings } from "@/constants/query-strings";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
|
|
@ -11,10 +12,10 @@ export default {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
setCmsContent(cmsContent){
|
setCmsContent(cmsContent) {
|
||||||
this.$root.cmsContentByWidget = cmsContent;
|
this.$root.cmsContentByWidget = cmsContent;
|
||||||
},
|
},
|
||||||
getCmsContent(widgetName, fieldName){
|
getCmsContent(widgetName, fieldName) {
|
||||||
return this.$root.cmsContentByWidget?.[widgetName]?.[fieldName] ? this.$root.cmsContentByWidget[widgetName][fieldName] : '';
|
return this.$root.cmsContentByWidget?.[widgetName]?.[fieldName] ? this.$root.cmsContentByWidget[widgetName][fieldName] : '';
|
||||||
},
|
},
|
||||||
dispatchNonBlockingStoreAction(type, payload, encodePayload = true) {
|
dispatchNonBlockingStoreAction(type, payload, encodePayload = true) {
|
||||||
|
|
@ -25,10 +26,10 @@ export default {
|
||||||
|
|
||||||
return store.dispatch(type, payload);
|
return store.dispatch(type, payload);
|
||||||
},
|
},
|
||||||
savePageDataToStore(page, data){
|
savePageDataToStore(page, data) {
|
||||||
store.commit(storeMutations.UPDATE_PAGE_DATA, { page: page, data: data });
|
store.commit(storeMutations.UPDATE_PAGE_DATA, { page: page, data: data });
|
||||||
},
|
},
|
||||||
onSubmit() {}, // DO NOT REMOVE; needed to prevent default form submit behavior
|
onSubmit() { }, // DO NOT REMOVE; needed to prevent default form submit behavior
|
||||||
onInvalidSubmit({ values, errors, results }) {
|
onInvalidSubmit({ values, errors, results }) {
|
||||||
// identify the first error field and put focus on it
|
// identify the first error field and put focus on it
|
||||||
// get error names array
|
// get error names array
|
||||||
|
|
@ -40,6 +41,28 @@ export default {
|
||||||
el && el.focus();
|
el && el.focus();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
pushEventToGA(category, action, label, value, pageName) {
|
||||||
|
const eventToBePushed = {
|
||||||
|
'event': 'ga_event',
|
||||||
|
'category': category,
|
||||||
|
'action': action,
|
||||||
|
'label': label,
|
||||||
|
'value': value,
|
||||||
|
'path': `/fmg/?${queryStrings.FMG_PAGE}=${pageName}`
|
||||||
|
}
|
||||||
|
pushToDataLayerIfDefined(eventToBePushed);
|
||||||
|
},
|
||||||
|
|
||||||
|
pushPageViewToGA(pageName) {
|
||||||
|
const pageViewEvent = {
|
||||||
|
'event': 'logPageview',
|
||||||
|
'pagePath': `/fmg/?${queryStrings.FMG_PAGE}=${pageName}`,
|
||||||
|
'pageTitle': pageName
|
||||||
|
};
|
||||||
|
pushToDataLayerIfDefined(pageViewEvent);
|
||||||
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
storeActions() {
|
storeActions() {
|
||||||
|
|
@ -65,3 +88,9 @@ function encodeUriData(payload) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function pushToDataLayerIfDefined(data) {
|
||||||
|
if (window.dataLayer !== undefined) {
|
||||||
|
window.dataLayer.push(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,13 @@ import { storeActions } from "@/constants/store-actions";
|
||||||
import { lazyLoadComponent } from "@/router/dynamic-routing/component-loader.js";
|
import { lazyLoadComponent } from "@/router/dynamic-routing/component-loader.js";
|
||||||
import { routingTable } from "@/router/router-constants/routing-table.js";
|
import { routingTable } from "@/router/router-constants/routing-table.js";
|
||||||
import { globalEvents, globalEventTypes } from "@/constants/events";
|
import { globalEvents, globalEventTypes } from "@/constants/events";
|
||||||
|
import { queryStrings } from "@/constants/query-strings";
|
||||||
|
|
||||||
// Heritage integration
|
// Heritage integration
|
||||||
import { isSavedSessionStillActive } from "@/helpers/heritage-integration/session-helper";
|
import { isSavedSessionStillActive } from "@/helpers/heritage-integration/session-helper";
|
||||||
import { updateOrCreateFunnelCookie,getFunnelCookie } from "@/helpers/heritage-integration/cookie-helper";
|
import { updateOrCreateFunnelCookie, getFunnelCookie } from "@/helpers/heritage-integration/cookie-helper";
|
||||||
import { loadOrderIfPresent, saveOrder } from "@/helpers/heritage-integration/order-helper";
|
import { loadOrderIfPresent, saveOrder } from "@/helpers/heritage-integration/order-helper";
|
||||||
import { getPageToRouteExistingOrderTo, navigateToHeritageFunnel} from "@/helpers/heritage-integration/navigation-helper";
|
import { getPageToRouteExistingOrderTo, navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
|
||||||
|
|
||||||
import baseMixin from "@/mixins/base-mixin";
|
import baseMixin from "@/mixins/base-mixin";
|
||||||
import eventBus from "@/helpers/event-bus/event-bus";
|
import eventBus from "@/helpers/event-bus/event-bus";
|
||||||
|
|
@ -35,84 +36,79 @@ const routes = [
|
||||||
name: "root",
|
name: "root",
|
||||||
async beforeEnter(to, from, next) {
|
async beforeEnter(to, from, next) {
|
||||||
// If we have no query string, or we don't have the FmgPage query string.
|
// If we have no query string, or we don't have the FmgPage query string.
|
||||||
if (to.query.fmgPage === undefined) {
|
try {
|
||||||
await GoToFunnelStartOn404(next);
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
|
|
||||||
// If the saved session has timed out, clear the session, execute 404 logic.
|
// If the saved session has timed out, clear the session, execute 404 logic.
|
||||||
if (getFunnelCookie() !== null && !isSavedSessionStillActive()) {
|
if (getFunnelCookie() !== null && !isSavedSessionStillActive()) {
|
||||||
await GoToFunnelStartOn404(next);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// On entering the funnel "fresh", read cookie information, decide what to do next.
|
|
||||||
if (from.redirectedFrom === undefined) {
|
|
||||||
const loadOrderResponse = await loadOrderIfPresent();
|
|
||||||
const pageToRedirectTo = await getPageToRouteExistingOrderTo(to, loadOrderResponse);
|
|
||||||
|
|
||||||
// If getPageToRouteExistingOrderTo determines that the return user needs to
|
|
||||||
// go back to heritage funnel, send them there and stop our current navigation.
|
|
||||||
if (pageToRedirectTo === 'heritage') {
|
|
||||||
await navigateToHeritageFunnel();
|
|
||||||
return next(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assign our fmgPage so it will load normally like the other pages.
|
|
||||||
to.query.fmgPage = pageToRedirectTo;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process funnel cookie.
|
|
||||||
updateOrCreateFunnelCookie();
|
|
||||||
|
|
||||||
// If we already have our route, go to it.
|
|
||||||
if (router.hasRoute(to.query.fmgPage)) {
|
|
||||||
// Since our route is already in scope, we can grab the component from it and call the arePagePrerequisitesValid function.
|
|
||||||
let component = router.getRoutes().filter((x) => x.name === to.query.fmgPage)[0].components;
|
|
||||||
|
|
||||||
// If the component hasn't been loaded fully, load it before we check prerequisites.
|
|
||||||
if (component.default.methods === undefined) {
|
|
||||||
component = await component.default();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!arePagePrerequisitesValid(component)) {
|
|
||||||
await GoToFunnelStartOn404(next);
|
|
||||||
}
|
|
||||||
|
|
||||||
return next({ name: to.query.fmgPage, query: to.query, params: to.params });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get route info for the given url. Names will have a 1:1 relationship with names in the Cms.
|
|
||||||
const routeData = await GetRouteInfoFromPageName(to.query.fmgPage);
|
|
||||||
|
|
||||||
// Add our dynamic route.
|
|
||||||
router.addRoute({
|
|
||||||
path: routeData[0].path, // Always the same path, because we control it with query strings.
|
|
||||||
name: routeData[0].name,
|
|
||||||
component: routeData[0].component,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Call the next components arePagePrerequisitesValid method before load.
|
|
||||||
// If it returns false, use the 404 logic.
|
|
||||||
const nextComponent = await router.getRoutes().filter((x) => x.name === routeData[0].name)[0].components.default();
|
|
||||||
|
|
||||||
if (!arePagePrerequisitesValid(nextComponent)) {
|
|
||||||
await GoToFunnelStartOn404(next);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assign current query string parameters, as well as our fmgPage one.
|
|
||||||
next({
|
|
||||||
name: routeData[0].name,
|
|
||||||
query: Object.assign(to.query, { fmgPage: routeData[0].name }),
|
|
||||||
params: to.params
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
|
|
||||||
// If we don't have a route, go to our 404 page.
|
|
||||||
await GoToFunnelStartOn404(next);
|
await GoToFunnelStartOn404(next);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// On entering the funnel "fresh", read cookie information, decide what to do next.
|
||||||
|
if (from.redirectedFrom === undefined) {
|
||||||
|
const loadOrderResponse = await loadOrderIfPresent();
|
||||||
|
const pageToRedirectTo = await getPageToRouteExistingOrderTo(to, loadOrderResponse);
|
||||||
|
|
||||||
|
// If getPageToRouteExistingOrderTo determines that the return user needs to
|
||||||
|
// go back to heritage funnel, send them there and stop our current navigation.
|
||||||
|
if (pageToRedirectTo === 'heritage') {
|
||||||
|
await navigateToHeritageFunnel();
|
||||||
|
return next(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Assign our fmgPage so it will load normally like the other pages.
|
||||||
|
to.query.fmgPage = pageToRedirectTo;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process funnel cookie.
|
||||||
|
updateOrCreateFunnelCookie();
|
||||||
|
|
||||||
|
// If we already have our route, go to it.
|
||||||
|
if (router.hasRoute(to.query.fmgPage)) {
|
||||||
|
// Since our route is already in scope, we can grab the component from it and call the arePagePrerequisitesValid function.
|
||||||
|
let component = router.getRoutes().filter((x) => x.name === to.query.fmgPage)[0].components;
|
||||||
|
|
||||||
|
// If the component hasn't been loaded fully, load it before we check prerequisites.
|
||||||
|
if (component.default.methods === undefined) {
|
||||||
|
component = await component.default();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!arePagePrerequisitesValid(component)) {
|
||||||
|
await GoToFunnelStartOn404(next);
|
||||||
|
}
|
||||||
|
|
||||||
|
return next({ name: to.query.fmgPage, query: to.query, params: to.params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get route info for the given url. Names will have a 1:1 relationship with names in the Cms.
|
||||||
|
const routeData = await GetRouteInfoFromPageName(to.query.fmgPage);
|
||||||
|
|
||||||
|
// Add our dynamic route.
|
||||||
|
router.addRoute({
|
||||||
|
path: routeData[0].path, // Always the same path, because we control it with query strings.
|
||||||
|
name: routeData[0].name,
|
||||||
|
component: routeData[0].component,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Call the next components arePagePrerequisitesValid method before load.
|
||||||
|
// If it returns false, use the 404 logic.
|
||||||
|
const nextComponent = await router.getRoutes().filter((x) => x.name === routeData[0].name)[0].components.default();
|
||||||
|
|
||||||
|
if (!arePagePrerequisitesValid(nextComponent)) {
|
||||||
|
await GoToFunnelStartOn404(next);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign current query string parameters, as well as our fmgPage one.
|
||||||
|
next({
|
||||||
|
name: routeData[0].name,
|
||||||
|
query: Object.assign(to.query, { fmgPage: routeData[0].name }),
|
||||||
|
params: to.params
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
|
||||||
|
// If we don't have a route, go to our 404 page.
|
||||||
|
await GoToFunnelStartOn404(next);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -125,6 +121,10 @@ const router = createRouter({
|
||||||
|
|
||||||
//---------------------------------------------------------- Router Functions ----------------------------------------------------------
|
//---------------------------------------------------------- Router Functions ----------------------------------------------------------
|
||||||
|
|
||||||
|
router.afterEach((to, from) => {
|
||||||
|
baseMixin.methods.pushPageViewToGA(to.query[queryStrings.FMG_PAGE]);
|
||||||
|
});
|
||||||
|
|
||||||
router.navigate = (scenario, currentRoute, optionalQuery = {}, optionalParams = {}, optionalPageData = {}) => {
|
router.navigate = (scenario, currentRoute, optionalQuery = {}, optionalParams = {}, optionalPageData = {}) => {
|
||||||
navigate(scenario, currentRoute, false, optionalQuery, optionalParams, optionalPageData);
|
navigate(scenario, currentRoute, false, optionalQuery, optionalParams, optionalPageData);
|
||||||
}
|
}
|
||||||
|
|
@ -205,6 +205,13 @@ function navigateToUrl(url, optionalQuery = {}) {
|
||||||
externalUrl.searchParams.append(queryKey, optionalQuery[queryKey]);
|
externalUrl.searchParams.append(queryKey, optionalQuery[queryKey]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
// TEMP CODE FOR TESTING WITH SPECIFIC EXPERIMENTS //
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
if (externalUrl.search.indexOf("corid=") != -1)
|
||||||
|
externalUrl.search = externalUrl.search + '&experiments=CollectEmailOnQuote=CollectEmailOnQuote_V1=YesCollectEmail_TEST1=true,RemoveServiceAreaPage=ServAreaRemoval_V7=ServAreaNoRemove_V7_TEST=true,VINeducationV2=VINeducation_V2=NoShowVINmodalV2_CONTROL=true,ServicePackages=ServicePackages_V1=NoShowPackages_CONTROL=true,PhotoUploadRedesign=PhotoUploadRedesign_V1=CurrentPhotoUpload_CONTROL=true,ScheduleDetailsServiceType=ScheduleBeforeServiceType_V1=ServTypeThenSched_CONTROL=true';
|
||||||
|
///////////// END TEMP CODE /////////////////////////
|
||||||
|
|
||||||
window.location.assign(externalUrl);
|
window.location.assign(externalUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ const fmgPageValues = {
|
||||||
LICENSE_PLATE_LOOKUP: "license-plate-lookup",
|
LICENSE_PLATE_LOOKUP: "license-plate-lookup",
|
||||||
REVEAL: "reveal",
|
REVEAL: "reveal",
|
||||||
ESTIMATE: "estimate",
|
ESTIMATE: "estimate",
|
||||||
|
ADDRESS_VEHICLES: "address-vehicles",
|
||||||
};
|
};
|
||||||
|
|
||||||
export { fmgPageValues };
|
export { fmgPageValues };
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,9 @@ const navigationScenarios = {
|
||||||
SELECTED_MAKE: "SELECTED_MAKE",
|
SELECTED_MAKE: "SELECTED_MAKE",
|
||||||
SELECTED_STYLE: "SELECTED_STYLE",
|
SELECTED_STYLE: "SELECTED_STYLE",
|
||||||
CLICKED_BACK: "CLICKED_BACK",
|
CLICKED_BACK: "CLICKED_BACK",
|
||||||
|
CLICKED_BACK_WITH_VIN: "CLICKED_BACK_WITH_VIN",
|
||||||
CLICKED_FORWARD: "CLICKED_FORWARD",
|
CLICKED_FORWARD: "CLICKED_FORWARD",
|
||||||
|
CLICKED_FORWARD_WITH_VIN: "CLICKED_FORWARD_WITH_VIN",
|
||||||
SELECTED_PARTS: "SELECTED_PARTS",
|
SELECTED_PARTS: "SELECTED_PARTS",
|
||||||
SELECTED_DAMAGE_WITH_SINGLE_PART: "SELECTED_DAMAGE_WITH_SINGLE_PART",
|
SELECTED_DAMAGE_WITH_SINGLE_PART: "SELECTED_DAMAGE_WITH_SINGLE_PART",
|
||||||
SELECTED_DAMAGE_WITH_MULTIPLE_PARTS: "SELECTED_DAMAGE_WITH_MULTIPLE_PARTS",
|
SELECTED_DAMAGE_WITH_MULTIPLE_PARTS: "SELECTED_DAMAGE_WITH_MULTIPLE_PARTS",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
|
import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
|
||||||
import { navigationScenarios } from "@/router/router-constants/navigation-scenarios";
|
import { navigationScenarios } from "@/router/router-constants/navigation-scenarios";
|
||||||
import { applicationConfig } from "@/constants/application-config";
|
|
||||||
|
|
||||||
const routingTable = [
|
const routingTable = [
|
||||||
{
|
{
|
||||||
|
|
@ -59,6 +58,10 @@ const routingTable = [
|
||||||
destinationFmgPageValue: fmgPageValues.VEHICLE_STYLE,
|
destinationFmgPageValue: fmgPageValues.VEHICLE_STYLE,
|
||||||
},
|
},
|
||||||
// UNDO THESE CHANGES BEFORE MERGING TO develop
|
// UNDO THESE CHANGES BEFORE MERGING TO develop
|
||||||
|
{
|
||||||
|
scenario: navigationScenarios.CLICKED_FORWARD_WITH_VIN,
|
||||||
|
destinationFmgPageValue: fmgPageValues.VIN_LOOKUP,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
scenario: navigationScenarios.SELECTED_DAMAGE_WITH_SINGLE_PART,
|
scenario: navigationScenarios.SELECTED_DAMAGE_WITH_SINGLE_PART,
|
||||||
destinationFmgPageValue: fmgPageValues.ADDRESS_LOOKUP
|
destinationFmgPageValue: fmgPageValues.ADDRESS_LOOKUP
|
||||||
|
|
@ -109,6 +112,10 @@ const routingTable = [
|
||||||
maps: [
|
maps: [
|
||||||
{
|
{
|
||||||
scenario: navigationScenarios.CLICKED_BACK,
|
scenario: navigationScenarios.CLICKED_BACK,
|
||||||
|
destinationFmgPageValue: fmgPageValues.REVEAL,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
scenario: navigationScenarios.CLICKED_BACK_WITH_VIN,
|
||||||
destinationFmgPageValue: fmgPageValues.VEHICLE_DAMAGE,
|
destinationFmgPageValue: fmgPageValues.VEHICLE_DAMAGE,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -136,6 +143,10 @@ const routingTable = [
|
||||||
scenario: navigationScenarios.CONTINUING_WITH_SINGLE_PART,
|
scenario: navigationScenarios.CONTINUING_WITH_SINGLE_PART,
|
||||||
destinationFmgPageValue: fmgPageValues.REVEAL,
|
destinationFmgPageValue: fmgPageValues.REVEAL,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
scenario: navigationScenarios.CLICKED_FORWARD,
|
||||||
|
destinationFmgPageValue: fmgPageValues.VEHICLE_DAMAGE,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -45,10 +45,16 @@ const getDefaultState = () => {
|
||||||
glassParts: null,
|
glassParts: null,
|
||||||
otherParts: null
|
otherParts: null
|
||||||
},
|
},
|
||||||
|
payment:{
|
||||||
|
isInsurance: null,
|
||||||
|
insuranceCoverage: {
|
||||||
|
isVerified: null
|
||||||
|
}
|
||||||
|
},
|
||||||
referralNumber: null,
|
referralNumber: null,
|
||||||
referralDate: null,
|
referralDate: null,
|
||||||
referralCorrelationId: null,
|
referralCorrelationId: null,
|
||||||
parentAccountNumber: null,
|
parentAccountNumber: 0,
|
||||||
},
|
},
|
||||||
applicationUser: {
|
applicationUser: {
|
||||||
eventBus: [],
|
eventBus: [],
|
||||||
|
|
@ -93,7 +99,7 @@ export const mutations = {
|
||||||
updateVehicleVin(state, vin) {
|
updateVehicleVin(state, vin) {
|
||||||
state.order.vehicle.vin = vin;
|
state.order.vehicle.vin = vin;
|
||||||
},
|
},
|
||||||
updateIsRepair(state, isRepair){
|
updateIsRepair(state, isRepair) {
|
||||||
state.order.damage.isRepair = isRepair;
|
state.order.damage.isRepair = isRepair;
|
||||||
},
|
},
|
||||||
updateNumberOfChips(state, numberOfChips) {
|
updateNumberOfChips(state, numberOfChips) {
|
||||||
|
|
@ -120,6 +126,27 @@ export const mutations = {
|
||||||
updateParentAcctNumber(state, parentAcctNumber) {
|
updateParentAcctNumber(state, parentAcctNumber) {
|
||||||
state.order.parentAccountNumber = parentAcctNumber;
|
state.order.parentAccountNumber = parentAcctNumber;
|
||||||
},
|
},
|
||||||
|
updateIsInsurance(state, isInsurance) {
|
||||||
|
state.order.payment.isInsurance = isInsurance;
|
||||||
|
},
|
||||||
|
updateInsuranceVerifiedStatus(state, isVerified) {
|
||||||
|
state.order.payment.insuranceCoverage.isVerified = isVerified;
|
||||||
|
},
|
||||||
|
updateRegistrationLicensePlate(state, licensePlate){
|
||||||
|
state.order.vehicle.registration.licensePlate = licensePlate;
|
||||||
|
},
|
||||||
|
updateRegistrationState(state, registrationState){
|
||||||
|
state.order.vehicle.registration.state = registrationState;
|
||||||
|
},
|
||||||
|
updateRegistrationZipCode(state, reistrationZipCode){
|
||||||
|
state.order.vehicle.registration.zipCode = reistrationZipCode;
|
||||||
|
},
|
||||||
|
updateServiceLocationZip(state, serviceLocationZip){
|
||||||
|
state.order.serviceLocation.zip = serviceLocationZip;
|
||||||
|
},
|
||||||
|
updateCustomerEmailAddress(state, customerEmailAddress){
|
||||||
|
state.order.customer.emailAddress = customerEmailAddress;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
// EVENT BUS MUTATIONS
|
// EVENT BUS MUTATIONS
|
||||||
|
|
@ -176,23 +203,30 @@ export const mutations = {
|
||||||
state.order.referralNumber = orderInformation.referralNumber;
|
state.order.referralNumber = orderInformation.referralNumber;
|
||||||
state.order.referralDate = orderInformation.referralDate;
|
state.order.referralDate = orderInformation.referralDate;
|
||||||
state.order.referralCorrelationId = orderInformation.referralCorrelationId;
|
state.order.referralCorrelationId = orderInformation.referralCorrelationId;
|
||||||
state.order.vehicle = {
|
|
||||||
|
state.order.vehicle = Object.assign(state.order.vehicle, {
|
||||||
year: orderInformation.vehicle?.year,
|
year: orderInformation.vehicle?.year,
|
||||||
make: orderInformation.vehicle?.make,
|
make: orderInformation.vehicle?.make,
|
||||||
model: orderInformation.vehicle?.model,
|
model: orderInformation.vehicle?.model,
|
||||||
style: orderInformation.vehicle?.style,
|
style: orderInformation.vehicle?.style,
|
||||||
|
vin: orderInformation.vehicle?.vin,
|
||||||
carId: orderInformation.vehicle?.carId,
|
carId: orderInformation.vehicle?.carId,
|
||||||
category: orderInformation.vehicle?.category,
|
category: orderInformation.vehicle?.category,
|
||||||
imageUrl: orderInformation.vehicle?.imageUrl,
|
imageUrl: orderInformation.vehicle?.imageUrl,
|
||||||
imageVifNumber: orderInformation.vehicle?.imageVifNumber,
|
imageVifNumber: orderInformation.vehicle?.imageVifNumber,
|
||||||
imageColor: orderInformation.vehicle?.imageVifColor
|
imageColor: orderInformation.vehicle?.imageVifColor,
|
||||||
};
|
});
|
||||||
|
|
||||||
state.order.damage.glassToReplace = orderInformation.glassToReplace;
|
state.order.damage.glassToReplace = orderInformation.glassToReplace;
|
||||||
state.order.damage.isRepair = orderInformation.isRepair;
|
state.order.damage.isRepair = orderInformation.isRepair;
|
||||||
state.order.damage.numberOfChips = orderInformation.numberOfChips;
|
state.order.damage.numberOfChips = orderInformation.numberOfChips;
|
||||||
|
|
||||||
state.order.lineItems.glassParts = orderInformation.parts;
|
state.order.lineItems.glassParts = orderInformation.parts;
|
||||||
state.order.parentAccountNumber = orderInformation.parentAccountNumber;
|
state.order.parentAccountNumber = orderInformation.parentAccountNumber;
|
||||||
state.order.serviceLocation.zipCode = orderInformation.zipCode; // TODO CSR-416 Make sure this is correct
|
state.order.serviceLocation.zipCode = orderInformation.zipCode;
|
||||||
|
|
||||||
|
state.order.payment.isInsurance = orderInformation.IsInsuranceOrder;
|
||||||
|
state.order.payment.insuranceCoverage.isVerified = orderInformation?.insuranceInfo.coverageVerified;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -210,11 +244,10 @@ export const getters = {
|
||||||
eventBus: (state) => state.applicationUser.eventBus,
|
eventBus: (state) => state.applicationUser.eventBus,
|
||||||
damage: (state) => state.order.damage,
|
damage: (state) => state.order.damage,
|
||||||
lineItems: (state) => state.order.lineItems,
|
lineItems: (state) => state.order.lineItems,
|
||||||
pageData: (state) => (page) => {
|
pageData: (state) => (page) => { return state.applicationUser.pageData[page]; },
|
||||||
return state.applicationUser.pageData[page];
|
|
||||||
},
|
|
||||||
applicationUser: (state) => state.applicationUser,
|
applicationUser: (state) => state.applicationUser,
|
||||||
order: (state) => state.order,
|
order: (state) => state.order,
|
||||||
|
payment: (state) => state.order.payment,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Export Actions
|
// Export Actions
|
||||||
|
|
@ -248,7 +281,7 @@ export const actions = {
|
||||||
method: endpoints.LookupVinByPlate.method,
|
method: endpoints.LookupVinByPlate.method,
|
||||||
endpoint: endpoints.LookupVinByPlate.url,
|
endpoint: endpoints.LookupVinByPlate.url,
|
||||||
payload: {
|
payload: {
|
||||||
licensePlate: licensePlate,
|
licensePlate: licensePlate,
|
||||||
licenseState: licenseState
|
licenseState: licenseState
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
@ -304,12 +337,12 @@ export const actions = {
|
||||||
},
|
},
|
||||||
getDamageOptions(context, { carId }) {
|
getDamageOptions(context, { carId }) {
|
||||||
return globalMethods.callHttpClient({
|
return globalMethods.callHttpClient({
|
||||||
methods: endpoints.GetDamageOptions.method,
|
methods: endpoints.GetDamageOptions.method,
|
||||||
endpoint: `${endpoints.GetDamageOptions.url}/${carId}`,
|
endpoint: `${endpoints.GetDamageOptions.url}/${carId}`,
|
||||||
payload: {},
|
payload: {},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
validateZip(context, {zip}) {
|
validateZip(context, { zip }) {
|
||||||
return globalMethods.callHttpClient({
|
return globalMethods.callHttpClient({
|
||||||
methods: endpoints.ValidateZip.method,
|
methods: endpoints.ValidateZip.method,
|
||||||
endpoint: `${endpoints.ValidateZip.url}/${zip}`
|
endpoint: `${endpoints.ValidateZip.url}/${zip}`
|
||||||
|
|
@ -375,6 +408,19 @@ export const actions = {
|
||||||
context.commit(storeMutations.UPDATE_REFERRAL_CORRELATION_ID, referralCorrelationId);
|
context.commit(storeMutations.UPDATE_REFERRAL_CORRELATION_ID, referralCorrelationId);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
logExperimentExposure(context, { userId, sessionKey, pageName, universeName }) {
|
||||||
|
return globalMethods.callHttpClient({
|
||||||
|
method: endpoints.LogExperimentExposureIfAssigned.method,
|
||||||
|
endpoint: endpoints.LogExperimentExposureIfAssigned.url,
|
||||||
|
payload: {
|
||||||
|
userId: userId,
|
||||||
|
sessionKey: sessionKey,
|
||||||
|
pageName: pageName,
|
||||||
|
universeName: universeName
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
// Parts API Actions
|
// Parts API Actions
|
||||||
getPartsOrQuestions(context, { carId, glassArray, zipCode, vin = '' }) {
|
getPartsOrQuestions(context, { carId, glassArray, zipCode, vin = '' }) {
|
||||||
return globalMethods.callHttpClient({
|
return globalMethods.callHttpClient({
|
||||||
|
|
@ -414,16 +460,18 @@ export const actions = {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
loadOrder(context, { referralNumber, referralDate, referralCorrelationId }) {
|
loadOrder(context, { referralNumber, referralDate, referralCorrelationId, accountNumber}) {
|
||||||
return globalMethods.callHttpClient({
|
return globalMethods.callHttpClient({
|
||||||
method: endpoints.LoadOrder.method,
|
method: endpoints.LoadOrder.method,
|
||||||
endpoint: endpoints.LoadOrder.url,
|
endpoint: endpoints.LoadOrder.url,
|
||||||
payload: {
|
payload: {
|
||||||
referralNumber: referralNumber,
|
referralNumber: referralNumber,
|
||||||
referralDate: referralDate,
|
referralDate: referralDate,
|
||||||
referralCorrelationId: referralCorrelationId
|
referralCorrelationId: referralCorrelationId,
|
||||||
|
accountNumber: accountNumber
|
||||||
},
|
},
|
||||||
}).then((response) => {
|
}).then((response) => {
|
||||||
|
context.commit(storeMutations.RESET_STATE);
|
||||||
context.commit(storeMutations.UPDATE_STATE_WITH_ORDER_INFORMATION, response.data);
|
context.commit(storeMutations.UPDATE_STATE_WITH_ORDER_INFORMATION, response.data);
|
||||||
return response;
|
return response;
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -222,6 +222,7 @@ describe("Mutations", () => {
|
||||||
numberOfChips: 0,
|
numberOfChips: 0,
|
||||||
parts: [],
|
parts: [],
|
||||||
parentAccountNumber: "123456789",
|
parentAccountNumber: "123456789",
|
||||||
|
insuranceInfo: {}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
|
@ -232,6 +233,17 @@ describe("Mutations", () => {
|
||||||
expect(storeState.order.vehicle.model).toEqual("ILX");
|
expect(storeState.order.vehicle.model).toEqual("ILX");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("updateInsuranceVerifiedStatus, should set isVerified flag", () => {
|
||||||
|
// Arrange
|
||||||
|
const storeState = state;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
mutations.updateInsuranceVerifiedStatus(storeState, true);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(storeState.order.payment.insuranceCoverage.isVerified).toEqual(true);
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("Actions", () => {
|
describe("Actions", () => {
|
||||||
|
|
@ -690,4 +702,15 @@ describe("Getters", () => {
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("Payment getter, should return payment data", () => {
|
||||||
|
// Arrange
|
||||||
|
const storeState = state;
|
||||||
|
|
||||||
|
//Act
|
||||||
|
mutations.updateInsuranceVerifiedStatus(storeState, true );
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
expect(getters.payment(storeState).insuranceCoverage.isVerified).toEqual(true);
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
@ -1,92 +1,126 @@
|
||||||
.has-error {
|
html {
|
||||||
&.list-button,
|
.has-error {
|
||||||
&.list-card {
|
&.list-button,
|
||||||
border: 1px solid $red;
|
&.list-card {
|
||||||
color: $red;
|
|
||||||
input[type=checkbox]:focus + label,
|
|
||||||
input[type=radio]:focus + label {
|
|
||||||
box-shadow: 0 0 0 2.5px $red;
|
|
||||||
}
|
|
||||||
input[type=checkbox]:focus + label:hover,
|
|
||||||
input[type=radio]:focus + label {
|
|
||||||
box-shadow: 0 0 0 1px $red;
|
|
||||||
}
|
|
||||||
&:hover {
|
|
||||||
box-shadow: 0px 0px 0px 4px $red-200;
|
|
||||||
border-radius: 10px !important;
|
|
||||||
border: 1px solid $red;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type=checkbox]:checked + label,
|
|
||||||
input[type=radio]:checked + label {
|
|
||||||
box-shadow: 0 0 0 2.5px transparent !important;
|
|
||||||
}
|
|
||||||
&.list-button-horizontal {
|
|
||||||
color: $red;
|
|
||||||
label {
|
|
||||||
border: 1px solid $red;
|
border: 1px solid $red;
|
||||||
|
color: $red;
|
||||||
|
input[type=checkbox]:focus + label,
|
||||||
|
input[type=radio]:focus + label {
|
||||||
|
box-shadow: 0 0 0 2.5px $red;
|
||||||
|
}
|
||||||
|
input[type=checkbox]:checked + label {
|
||||||
|
box-shadow: 0 0 0 1px $red;
|
||||||
|
}
|
||||||
&:hover {
|
&:hover {
|
||||||
box-shadow: 0px 0px 0px 4px $red-200;
|
box-shadow: 0px 0px 0px 4px $red-200;
|
||||||
|
border-radius: 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
input[type=checkbox]:focus + label,
|
&.list-button-horizontal {
|
||||||
input[type=radio]:focus + label {
|
|
||||||
box-shadow: 0 0 1px $red !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.ui-radio,
|
|
||||||
&.ui-checkbox {
|
|
||||||
input[type=checkbox],
|
|
||||||
input[type=radio],
|
|
||||||
input[type=radio]+label:before,
|
|
||||||
input[type=checkbox]+label:before {
|
|
||||||
border: 1px solid $red;
|
|
||||||
}
|
|
||||||
input[type=checkbox]:checked + label:before {
|
|
||||||
border: 1px solid $blue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.textbox-question,
|
|
||||||
&.dropdown-question {
|
|
||||||
p {
|
|
||||||
color: $red;
|
color: $red;
|
||||||
}
|
label {
|
||||||
input,
|
border: 1px solid $red;
|
||||||
select {
|
&:hover {
|
||||||
border: 1px solid $red;
|
box-shadow: 0px 0px 0px 4px $red-200;
|
||||||
&:focus {
|
}
|
||||||
border: 1px solid transparent;
|
}
|
||||||
|
input[type=checkbox]:focus + label,
|
||||||
|
input[type=radio]:focus + label {
|
||||||
|
box-shadow: 0 0 1px $red;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
select {
|
&.ui-radio,
|
||||||
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 8.89' xml:space='preserve'%3e%3cpath d='M8 8.89c-.24 0-.46-.09-.63-.26L.26 1.53a.901.901 0 0 1 0-1.27C.43.1.66 0 .9 0s.47.1.64.26L8 6.74 14.47.27c.17-.17.4-.27.64-.27s.47.1.63.27c.17.17.26.4.26.64s-.1.47-.27.63l-7.1 7.09a.86.86 0 0 1-.63.26z' fill='%23d4281c'/%3e%3c/svg%3e");
|
&.ui-checkbox {
|
||||||
background-repeat: no-repeat;
|
input[type=checkbox],
|
||||||
background-position: right 0.75rem center;
|
input[type=radio],
|
||||||
background-size: 16px 12px;
|
input[type=radio]+label:before,
|
||||||
|
input[type=checkbox]+label:before {
|
||||||
|
border: 1px solid $red;
|
||||||
|
}
|
||||||
|
input[type=checkbox]:checked + label:before {
|
||||||
|
border: 1px solid $blue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.textbox-question,
|
||||||
|
&.dropdown-question {
|
||||||
|
p {
|
||||||
|
color: $red;
|
||||||
|
}
|
||||||
|
input,
|
||||||
|
select {
|
||||||
|
border: 1px solid $red;
|
||||||
|
&:focus {
|
||||||
|
border: 1px solid transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 8.89' xml:space='preserve'%3e%3cpath d='M8 8.89c-.24 0-.46-.09-.63-.26L.26 1.53a.901.901 0 0 1 0-1.27C.43.1.66 0 .9 0s.47.1.64.26L8 6.74 14.47.27c.17-.17.4-.27.64-.27s.47.1.63.27c.17.17.26.4.26.64s-.1.47-.27.63l-7.1 7.09a.86.86 0 0 1-.63.26z' fill='%23d4281c'/%3e%3c/svg%3e");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: right 0.75rem center;
|
||||||
|
background-size: 16px 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//Restore to default style if alert box is present
|
||||||
|
.alertError {
|
||||||
|
.has-error {
|
||||||
|
&.list-button,
|
||||||
|
&.list-card {
|
||||||
|
border: 1px solid $gray-500;
|
||||||
|
input:not(:focus) {
|
||||||
|
+ label {
|
||||||
|
box-shadow: 0 0 0 1px $gray-500;
|
||||||
|
border-radius: .5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
input:checked:focus {
|
||||||
|
+ label {
|
||||||
|
box-shadow: 0 0 0 1px $blue;
|
||||||
|
border-radius: .5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
input:checked:not(:focus) {
|
||||||
|
+ label {
|
||||||
|
box-shadow: 0 0 0 1px $blue;
|
||||||
|
border-radius: .5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
input:focus {
|
||||||
|
border: 1px solid $gray-500;
|
||||||
|
+ label {
|
||||||
|
box-shadow: 0 0 0 2.5px transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
box-shadow: 0 0 0 4px $blue-300;
|
||||||
|
+ label {
|
||||||
|
box-shadow: 0 0 0 2.5px transparent;
|
||||||
|
border: 1px solid $blue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-test-error {
|
||||||
|
color: $red;
|
||||||
|
font-size: .875rem;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-test-invalid {
|
||||||
|
&.btn.btn-primary {
|
||||||
|
color: $gray;
|
||||||
|
background: $gray-200;
|
||||||
|
cursor: pointer;
|
||||||
|
pointer-events: all;
|
||||||
|
}
|
||||||
|
&.btn.btn-primary:hover,
|
||||||
|
&.btn.btn-primary:focus,
|
||||||
|
&.btn.btn-primary:focus-visible {
|
||||||
|
color: $gray !important;
|
||||||
|
background: $gray-200;
|
||||||
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-test-error {
|
|
||||||
color: $red;
|
|
||||||
font-size: .875rem;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-test-invalid {
|
|
||||||
&.btn.btn-primary {
|
|
||||||
color: $gray;
|
|
||||||
background: $gray-200;
|
|
||||||
cursor: pointer;
|
|
||||||
pointer-events: all;
|
|
||||||
}
|
|
||||||
&.btn.btn-primary:hover,
|
|
||||||
&.btn.btn-primary:focus,
|
|
||||||
&.btn.btn-primary:focus-visible {
|
|
||||||
color: $gray !important;
|
|
||||||
background: $gray-200 !important;
|
|
||||||
box-shadow: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { shallowMount } from "@vue/test-utils";
|
import { shallowMount } from "@vue/test-utils";
|
||||||
import alert from "./alert";
|
import alert from "./alert";
|
||||||
|
|
||||||
describe("alert.vue", () => {
|
describe("alert.vue", () => {
|
||||||
|
|
@ -9,6 +9,13 @@ describe("alert.vue", () => {
|
||||||
propsData: {
|
propsData: {
|
||||||
isDismissible: true
|
isDismissible: true
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
splitAlertCopyForLink: {
|
||||||
|
get() {
|
||||||
|
return "TEST";
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
mixins: [mockMixin]
|
mixins: [mockMixin]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -24,6 +31,13 @@ describe("alert.vue", () => {
|
||||||
propsData: {
|
propsData: {
|
||||||
alertClass: 'warning'
|
alertClass: 'warning'
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
splitAlertCopyForLink: {
|
||||||
|
get() {
|
||||||
|
return "TEST";
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
mixins: [mockMixin]
|
mixins: [mockMixin]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,15 @@
|
||||||
:class="[isDismissible ? 'alert-dismissible' : '', this.alertClass]"
|
:class="[isDismissible ? 'alert-dismissible' : '', this.alertClass]"
|
||||||
>
|
>
|
||||||
<p class="m-0 fw-bold small alert-heading">{{ alertHeadline }}</p>
|
<p class="m-0 fw-bold small alert-heading">{{ alertHeadline }}</p>
|
||||||
<p class="m-0 text-body small">{{ alertCopy }}</p>
|
<p v-if="splitAlertCopyForLink.length">
|
||||||
|
<template v-for="copy in splitAlertCopyForLink" :key="copy">
|
||||||
|
<span v-if="copy.includes('routerLink:')" class="m-0 text-body small">
|
||||||
|
<router-link :to="{query: {fmgPage: `${copy.split(':')[1].split(',')[0]}`}, name: 'root'}">{{ copy.split(':')[1].split(',')[1] }}</router-link>
|
||||||
|
</span>
|
||||||
|
<span v-else class="m-0 text-body small" v-html="copy"></span>
|
||||||
|
</template>
|
||||||
|
</p>
|
||||||
|
<p v-else class="m-0 text-body small" v-html="alertCopy"></p>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn-close p-2"
|
class="btn-close p-2"
|
||||||
|
|
@ -26,6 +34,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "alert",
|
name: "alert",
|
||||||
props: {
|
props: {
|
||||||
|
|
@ -49,6 +58,10 @@ export default {
|
||||||
alertCopy(){
|
alertCopy(){
|
||||||
return this.cmsWidgetName ? this.getCmsContent(this.cmsWidgetName, 'BodyText') : this.manualCopy;
|
return this.cmsWidgetName ? this.getCmsContent(this.cmsWidgetName, 'BodyText') : this.manualCopy;
|
||||||
},
|
},
|
||||||
|
splitAlertCopyForLink(){
|
||||||
|
// Splits content when brackets are found in text so that text can be looped through and router-link can be injected when needed
|
||||||
|
return this.alertCopy.split(/{(.*?)}/g);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -115,5 +128,9 @@ export default {
|
||||||
height: 1rem;
|
height: 1rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
& p {
|
||||||
|
font-size: 14px;
|
||||||
|
margin-bottom: 0px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="list-group list-button-horizontal d-flex flex-column w-100 mb-2"
|
class="list-group list-button-horizontal d-flex flex-column w-100 mb-2"
|
||||||
:class="[errors.length > 0 ? 'has-error' : '', hasError ? 'has-error' : '']"
|
:class="[(errors.length > 0 || hasError) ? 'has-error' : '']"
|
||||||
@mouseup="handleClick(value)"
|
@mouseup="handleClick(value)"
|
||||||
@keyup.space="handleClick(value)"
|
@keyup.space="handleClick(value)"
|
||||||
>
|
>
|
||||||
|
|
@ -11,7 +11,6 @@
|
||||||
:name="groupName"
|
:name="groupName"
|
||||||
:value="value"
|
:value="value"
|
||||||
:aria-required="isRequired"
|
:aria-required="isRequired"
|
||||||
:data-focus-target="groupName"
|
|
||||||
v-model="checkValue"
|
v-model="checkValue"
|
||||||
@change="!selectingInitiatesLoad ? handleCheckChange() : ''"
|
@change="!selectingInitiatesLoad ? handleCheckChange() : ''"
|
||||||
/>
|
/>
|
||||||
|
|
@ -143,11 +142,11 @@ export default {
|
||||||
height: 0;
|
height: 0;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
&:focus-visible + label {
|
&:focus-visible + label {
|
||||||
box-shadow: 0 0 0 2px $blue;
|
box-shadow: 0 0 0 2.5px $blue;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
&:focus + label {
|
&:focus + label {
|
||||||
box-shadow: 0 0 0 2px $blue;
|
box-shadow: 0 0 0 2.5px $blue;
|
||||||
z-index: 3;
|
z-index: 3;
|
||||||
}
|
}
|
||||||
&:checked + label {
|
&:checked + label {
|
||||||
|
|
@ -156,6 +155,9 @@ export default {
|
||||||
outline: none;
|
outline: none;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
&:checked:focus + label {
|
||||||
|
box-shadow: 0 0 0 2.5px $blue;
|
||||||
|
}
|
||||||
&:checked + label p:first-child {
|
&:checked + label p:first-child {
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="list-group list-button d-flex flex-column w-100 mb-2"
|
class="list-group list-button d-flex flex-column w-100 mb-2"
|
||||||
:class="[errors.length > 0 ? 'has-error' : '', hasError ? 'has-error' : '']"
|
:class="[(errors.length > 0 || hasError) ? 'has-error' : '']"
|
||||||
@mouseup="handleClick(value)"
|
@mouseup="handleClick(value)"
|
||||||
@keyup.space="handleClick(value)"
|
@keyup.space="handleClick(value)"
|
||||||
>
|
>
|
||||||
|
|
@ -11,7 +11,6 @@
|
||||||
:name="groupName"
|
:name="groupName"
|
||||||
:value="value"
|
:value="value"
|
||||||
:aria-required="isRequired"
|
:aria-required="isRequired"
|
||||||
:data-focus-target="groupName"
|
|
||||||
v-model="checkValue"
|
v-model="checkValue"
|
||||||
@change="!selectingInitiatesLoad ? handleCheckChange() : ''"
|
@change="!selectingInitiatesLoad ? handleCheckChange() : ''"
|
||||||
>
|
>
|
||||||
|
|
@ -97,7 +96,7 @@ export default {
|
||||||
}
|
}
|
||||||
this.handleChange(value);
|
this.handleChange(value);
|
||||||
},
|
},
|
||||||
handleCheckChange(newValue, oldValue){
|
handleCheckChange(value, oldValue){
|
||||||
const isInitialization = typeof(oldValue) === 'function';
|
const isInitialization = typeof(oldValue) === 'function';
|
||||||
if (!isInitialization) {
|
if (!isInitialization) {
|
||||||
const emitEvent = {
|
const emitEvent = {
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,7 @@
|
||||||
class="list-card w-100 rounded-3 d-flex align-items-center h-100"
|
class="list-card w-100 rounded-3 d-flex align-items-center h-100"
|
||||||
:class="[
|
:class="[
|
||||||
isWide ? 'horizontal' : '',
|
isWide ? 'horizontal' : '',
|
||||||
errors.length > 0 ? 'has-error' : '',
|
(errors.length > 0 || hasError) ? 'has-error' : '',
|
||||||
hasError ? 'has-error' : '',
|
|
||||||
]"
|
]"
|
||||||
@mouseup="handleChange(value)"
|
@mouseup="handleChange(value)"
|
||||||
@keyup.space="handleChange(value)"
|
@keyup.space="handleChange(value)"
|
||||||
|
|
@ -16,7 +15,6 @@
|
||||||
:name="groupName"
|
:name="groupName"
|
||||||
:value="value"
|
:value="value"
|
||||||
:aria-required="isRequired"
|
:aria-required="isRequired"
|
||||||
:data-focus-target="groupName"
|
|
||||||
v-model="checkValue"
|
v-model="checkValue"
|
||||||
@change="handleCheckChange(value)"
|
@change="handleCheckChange(value)"
|
||||||
/>
|
/>
|
||||||
|
|
@ -315,8 +313,8 @@ export default {
|
||||||
|
|
||||||
&:checked + label::after {
|
&:checked + label::after {
|
||||||
content: "";
|
content: "";
|
||||||
margin: -0.25rem 0 0 0;
|
margin: -0.15rem 0 0 0;
|
||||||
left: 0.875rem;
|
left: 1.175rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:checked + label {
|
&:checked + label {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,13 @@ process.env.VUE_APP_CONSUMER_API_GATEWAY =
|
||||||
process.env.VUE_APP_HERITAGE_FUNNEL =
|
process.env.VUE_APP_HERITAGE_FUNNEL =
|
||||||
"http://localhost:38000/default.aspx";
|
"http://localhost:38000/default.aspx";
|
||||||
process.env.VUE_APP_GOOGLE_PLACES_API_KEY =
|
process.env.VUE_APP_GOOGLE_PLACES_API_KEY =
|
||||||
"AIzaSyDptGCkOPgN2uWJOy4ou4M33phRD4MAoJo"
|
"AIzaSyDptGCkOPgN2uWJOy4ou4M33phRD4MAoJo";
|
||||||
|
|
||||||
|
// GA & GTM
|
||||||
|
process.env.VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY = "(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src='https://www.googletagmanager.com/gtm.js?id='+i+dl+ '>m_auth=amlAYNhxUxuskQo7jmjadg>m_preview=env-38>m_cookies_win=x';f.parentNode.insertBefore(j,f);})(window,document,'script','dataLayer','GTM-M6XCRH');";
|
||||||
|
|
||||||
|
process.env.VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC = "https://www.googletagmanager.com/ns.html?id=GTM-M6XCRH>m_auth=amlAYNhxUxuskQo7jmjadg>m_preview=env-38>m_cookies_win=x";
|
||||||
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
outputDir: "dist/fmg",
|
outputDir: "dist/fmg",
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,10 @@ process.env.VUE_APP_CONSUMER_API_GATEWAY = "__VUE_APP_CONSUMER_API_GATEWAY__";
|
||||||
process.env.VUE_APP_GOOGLE_PLACES_API_KEY = "__VUE_APP_GOOGLE_PLACES_API_KEY__";
|
process.env.VUE_APP_GOOGLE_PLACES_API_KEY = "__VUE_APP_GOOGLE_PLACES_API_KEY__";
|
||||||
process.env.VUE_APP_HERITAGE_FUNNEL = "__VUE_APP_HERITAGE_FUNNEL__";
|
process.env.VUE_APP_HERITAGE_FUNNEL = "__VUE_APP_HERITAGE_FUNNEL__";
|
||||||
|
|
||||||
|
// GA & GTM
|
||||||
|
process.env.VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY = "__VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY__";
|
||||||
|
process.env.VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC = "__VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC__"
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
outputDir: "dist/fmg",
|
outputDir: "dist/fmg",
|
||||||
publicPath: "/fmg",
|
publicPath: "/fmg",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue