Merge branch 'develop' into feature/CSR-1112

This commit is contained in:
Leah Schumann 2023-04-26 13:10:42 -04:00
commit 1bc9050113
16 changed files with 145 additions and 46 deletions

View file

@ -19,6 +19,7 @@ const GaActions = {
CLICKED: "Clicked",
VIF: "vif",
SUBMITTED: "Submitted",
DISPLAYED: "Displayed",
};
const GaLabels = {

View file

@ -87,6 +87,7 @@ import listButtonHorizontal from "@/ux-components/list-button-horizontal/list-bu
import listCard from "@/ux-components/list-card/list-card";
import radio from "@/ux-components/radio/radio";
import { useField, ErrorMessage } from "vee-validate";
import { queryStrings } from "@/constants/query-strings";
export default {
name: "buttonQuestion",
@ -130,6 +131,10 @@ export default {
isSmallQuestionText: Boolean,
availability: String,
customButtonQuestionId: String,
logDisplayedValuesEvent: {
type: Boolean,
default: false,
},
},
setup(props) {
const propsClone = Object.assign({}, props);
@ -254,6 +259,29 @@ export default {
modelValue() {
this.resetField();
},
answers() {
//once we get the answers to display from parent, see if we need a GA event to log what we showed
if (this.logDisplayedValuesEvent && this.answers.length > 0) {
var eventLabel = "";
//build comma separated list of all items in button list that we are going to display on page
this.answers.forEach((item) => {
if (item.Name) {
eventLabel += item.Name + ",";
}
if (item.buttonLabel) {
eventLabel += item.buttonLabel + ",";
}
});
eventLabel = eventLabel.slice(0, -1); //remove the last comma
this.pushEventToGA(
this.$route.query[queryStrings.FMG_PAGE],
this.GaActions.DISPLAYED,
eventLabel,
true
);
}
},
},
components: {
listButton,

View file

@ -1,6 +1,7 @@
import store from "@/store";
import baseMixin from "@/mixins/base-mixin.js";
import { storeActions } from "@/constants/store-actions";
import { damageLocationsSelected as glassLocations } from "@/constants/damage-locations-selected";
export function getDamageString() {
// If it's a repair it's always a windshield.
@ -45,6 +46,14 @@ export function getIsWindshieldOnly() {
return returnString;
}
export function includesWindshieldReplacement() {
const windshieldMatches =
store.getters.order.damage.glassToReplace?.filter(
(glassToReplace) => glassToReplace.glassLocation === glassLocations.WINDSHIELD
) ?? [];
return windshieldMatches.length > 0;
}
export async function isGlassAvailableForCarId(carId) {
const newGlassOptions = await baseMixin.methods.dispatchStoreAction(
storeActions.GET_DAMAGE_OPTIONS,

View file

@ -7,6 +7,7 @@ import { storeActions } from "@/constants/store-actions.js";
import { settleAllPromises } from "@/helpers/layout-helper";
import experimentMixin from "@/mixins/experiment-mixin";
import { experimentSettings } from "@/constants/experiments";
import { includesWindshieldReplacement } from "@/helpers/damage-helper";
import store from "@/store";
import router from "@/router";
@ -73,6 +74,7 @@ export async function skipVinLookup() {
return (
store.getters.damage.isRepair ||
isVinOptionalVehicle ||
!includesWindshieldReplacement() ||
experimentMixin.methods.hasSettingEqualTo(experimentSettings.SUPPRESS_VIN_CAPTURE, "true")
);
}
@ -85,6 +87,7 @@ export async function skipVinLookupNotRepair() {
return (
!store.getters.damage.isRepair &&
(isVinOptionalVehicle ||
!includesWindshieldReplacement() ||
experimentMixin.methods.hasSettingEqualTo(
experimentSettings.SUPPRESS_VIN_CAPTURE,
"true"

View file

@ -135,7 +135,7 @@ describe("getPageToRouteExistingOrderTo", () => {
expect(result).toBe(fmgPageValues.VEHICLE_DAMAGE);
});
test("user has YMMS and no vehicle questions > should return vin-lookup", async () => {
test("user has YMMS and no vehicle questions > should return estimate", async () => {
// Arrange
const toRoute = {
query: {},
@ -172,7 +172,7 @@ describe("getPageToRouteExistingOrderTo", () => {
const result = await getPageToRouteExistingOrderTo(toRoute, false);
//Assert
expect(result).toBe(fmgPageValues.VIN_LOOKUP);
expect(result).toBe(fmgPageValues.ESTIMATE);
});
test("user has YMMS but no questions or carId > should return estimate", async () => {

View file

@ -45,7 +45,7 @@
<div class="row mb-4" v-show="showAddressFields" aria-live="polite">
<div class="col">
<dropdownQuestion
customInputId="state"
customDropdownId="state"
cmsWidgetName="StateQuestionWidget"
v-model="addressModel.state"
ref="state"
@ -115,6 +115,10 @@ export default {
type: Boolean,
default: false,
},
preserveCityAndStateOnReset: {
type: Boolean,
default: false,
},
},
data() {
return {
@ -390,8 +394,10 @@ export default {
this.displayNoMatchWarning = true;
this.addressModel.city = "";
this.addressModel.state = "";
this.addressModel.zipCode = "";
if (!this.preserveCityAndStateOnReset) {
this.addressModel.state = "";
this.addressModel.zipCode = "";
}
this.showAddressFields = true;
this.displayVerificationWarning = false;

View file

@ -101,6 +101,9 @@ describe("estimate.vue", () => {
//Arrange
const { wrapper } = setupMocks({});
delete window.location;
window.location = { search: "?fmgPage=estimate&zipcode=43015" };
//Act
estimate.beforeRouteEnter.call(
wrapper.vm,

View file

@ -18,7 +18,8 @@
buttonTypeString="listButton"
v-model="selectedVinLookupMethod"
isRequired
validationRules="option-required" />
validationRules="option-required"
:logDisplayedValuesEvent="true" />
</div>
<div v-else>
<alert
@ -147,11 +148,17 @@ export default {
const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString);
const hasZip = urlParams.has(queryStrings.ZIP_CODE);
const zip = urlParams.get(queryStrings.ZIP_CODE);
const lowerCaseParams = new URLSearchParams();
for (const [name, value] of urlParams) {
lowerCaseParams.append(name.toLowerCase(), value);
}
const zip = lowerCaseParams.get(queryStrings.ZIP_CODE)
? lowerCaseParams.get(queryStrings.ZIP_CODE)
: store.getters.order.serviceLocation.zipCode;
var vinByAddressPromise;
if (hasZip) {
if (zip) {
vinByAddressPromise = baseMixin.methods.dispatchStoreAction(
storeActions.IS_VIN_BY_ADDRESS_PERMISSIBLE,
zip,
@ -191,7 +198,7 @@ export default {
}
}
if (zip && resultMap.vinByAddress === false) {
if (!zip || resultMap.vinByAddress === false) {
var indexToRemove = resultMap.cmsContent.VinLookupMethod.Answers.findIndex(
(answer) => answer.Name === "HomeAddress"
);
@ -199,6 +206,7 @@ export default {
resultMap.cmsContent.VinLookupMethod.Answers.splice(indexToRemove, 1);
}
}
vm.setCmsContent(resultMap.cmsContent);
});
},

View file

@ -7,7 +7,7 @@
:displayGenericVehicleImage="false" />
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" />
<div class="fade-on-route-transition sub-container make-tall">
<div class="row my-2">
<div class="row mt-2 mb-4">
<div class="col">
<textboxQuestion
cmsWidgetName="LicensePlateNumberQuestionWidget"
@ -17,7 +17,7 @@
validationRules="license-plate-required" />
</div>
</div>
<div class="row my-2">
<div class="row mt-0 mb-4">
<div class="col">
<textboxQuestion
cmsWidgetName="RegistrationZipQuestionWidget"
@ -27,7 +27,7 @@
validationRules="zip-required|zip-format" />
</div>
</div>
<div class="row mt-2">
<div class="row mt-0">
<div class="col">
<textboxQuestion
cmsWidgetName="EmailAddressQuestionWidget"

View file

@ -7,7 +7,8 @@
:buttonTypeObject="servicePackageRadio"
v-model="selectedPackageName"
:validationRules="validationRules"
:isRequired="isRequired" />
:isRequired="isRequired"
:logDisplayedValuesEvent="true" />
</template>
<script>

View file

@ -66,6 +66,18 @@ export default {
},
},
watch: {
answersToDisplay: {
handler(newValue) {
// If there is only one option to display and that option is 'Mobile' then select it
if (
newValue.length == 1 &&
newValue.findIndex((answer) => answer.Name == "Mobile") != -1
) {
this.selectedValues = "Mobile";
}
},
immediate: true,
},
isMobileOnly: {
handler(newValue) {
if (newValue) {

View file

@ -17,15 +17,15 @@
@click-event="openModal"
aria-label="Modal window" />
</div>
<textBlock
:customText="mobileFeeText"
cmsWidgetName="MobileFeeDisclaimerWidget"
typeStyle="caption" />
<div v-show="errorMessage" class="row my-1 form-test-error">
<span class="d-inline-flex small mt-0 center-error-message" role="alert">
{{ errorMessage }}
</span>
</div>
<textBlock
:customText="mobileFeeText"
cmsWidgetName="MobileFeeDisclaimerWidget"
typeStyle="caption" />
</div>
<modal
:ref="modalName"
@ -37,7 +37,8 @@
<addressQuestions
ref="addressQuestions"
v-model="internalModel.addressQuestions"
captureApartmentNumberOrBusinessName="true" />
captureApartmentNumberOrBusinessName="true"
preserveCityAndStateOnReset="true" />
<vehicleProtectedQuestion
ref="vehicleProtectedQuestion"
v-model="internalModel.isVehicleProtected"

View file

@ -1,6 +1,6 @@
<template>
<div class="vin-information">
<div class="vin-toggle mt-2" :class="[isActive ? 'active' : '']" @click="toggleClass()">
<div class="vin-toggle" :class="[isActive ? 'active' : '']" @click="toggleClass()">
<textLink linkType="text" href="#!" :text="WhereCanIFindMyVINHeadline" />
</div>
<div class="vin-info">

View file

@ -36,12 +36,12 @@
alertClass="alert-danger" />
</div>
</div>
<div class="row mb-2">
<div class="row">
<div class="col">
<vinInformation />
</div>
</div>
<div class="row my-2">
<div class="row mb-0 mt-4">
<div class="col">
<textboxQuestion
cmsWidgetName="ServiceZipQuestionWidget"
@ -52,7 +52,7 @@
validationRules="zip-required|zip-format" />
</div>
</div>
<div class="row mt-2">
<div class="row mt-4">
<div class="col">
<textboxQuestion
cmsWidgetName="EmailAddressQuestionWidget"

View file

@ -143,18 +143,18 @@ export default {
false
);
if (response.data) {
if (response.data.sessionKey && skey === 0) {
if (response?.data) {
if (response?.data.sessionKey && skey === 0) {
setCookieProperties(
{ [cookieNames.SESSION_KEY]: response.data.sessionKey },
{ [cookieNames.SESSION_KEY]: response?.data.sessionKey },
{
useDefaultFunnelCookieAttributes: false,
}
);
}
if (response.data.sessionId && sid === "00000000-0000-0000-0000-000000000000") {
if (response?.data.sessionId && sid === "00000000-0000-0000-0000-000000000000") {
setCookieProperties(
{ [cookieNames.SESSION_ID]: response.data.sessionId },
{ [cookieNames.SESSION_ID]: response?.data.sessionId },
{
maxAge: 60 * 30, // 30 minutes
}

View file

@ -727,12 +727,21 @@ export const actions = {
experimentsForUser: experimentsForUser,
};
return globalMethods.callHttpClient({
method: endpoints.LogPageView.method,
endpoint: endpoints.LogPageView.url,
payload: payload,
logApiCall: false,
});
return globalMethods
.callHttpClient({
method: endpoints.LogPageView.method,
endpoint: endpoints.LogPageView.url,
payload: payload,
logApiCall: false,
})
.then(
(response) => {
return response;
},
(error) => {
console.log("Analytics Service Error: " + error.data);
}
);
},
logCustomEvent(
context,
@ -763,12 +772,21 @@ export const actions = {
experimentsForUser: experimentsForUser,
};
return globalMethods.callHttpClient({
method: endpoints.LogCustomEvent.method,
endpoint: endpoints.LogCustomEvent.url,
payload: payload,
logApiCall: false,
});
return globalMethods
.callHttpClient({
method: endpoints.LogCustomEvent.method,
endpoint: endpoints.LogCustomEvent.url,
payload: payload,
logApiCall: false,
})
.then(
(response) => {
return response;
},
(error) => {
console.log("Analytics Service Error: " + error.data);
}
);
},
initializeSession(context, { userId, sessionId, userAgent, referrer }) {
var payload = {
@ -782,12 +800,21 @@ export const actions = {
referrer: referrer,
};
return globalMethods.callHttpClient({
method: endpoints.InitializeSession.method,
endpoint: endpoints.InitializeSession.url,
payload: payload,
logApiCall: false,
});
return globalMethods
.callHttpClient({
method: endpoints.InitializeSession.method,
endpoint: endpoints.InitializeSession.url,
payload: payload,
logApiCall: false,
})
.then(
(response) => {
return response;
},
(error) => {
console.log("Analytics Service Error: " + error.data);
}
);
},
// Misc Actions