Merge branch 'develop' into feature/CSR-98

This commit is contained in:
Katie 2022-03-11 12:31:18 -05:00
commit 585938d1ec
12 changed files with 241 additions and 47 deletions

View file

@ -37,8 +37,8 @@
</div> </div>
</fieldset> </fieldset>
</div> </div>
<div class="row mt-2 form-test-error" v-if="!suppressError"> <div class="row mt-2 form-test-error">
<error-message :name="groupName"></error-message> <error-message :name="groupName" v-if="!suppressError"></error-message>
</div> </div>
</div> </div>
</template> </template>
@ -133,9 +133,10 @@ export default {
if(this.isMultiSelect && this.selectedValues) { if(this.isMultiSelect && this.selectedValues) {
// Add or remove item to array of data to emit // Add or remove item to array of data to emit
const newSelectedValues = this.selectedValues; const newSelectedValues = this.selectedValues;
if(Array.isArray(this.selectedValues)) { if(Array.isArray(this.selectedValues)) {
val.checkValue ? newSelectedValues.push(val.value) : newSelectedValues.splice(newSelectedValues.indexOf(val.value), 1); val.checkValue ? newSelectedValues.push(val.value) : newSelectedValues.splice(newSelectedValues.indexOf(val.value), 1);
this.selectedValues = newSelectedValues; this.selectedValues = newSelectedValues;
} }
} else { } else {
this.selectedValues = [val.value]; this.selectedValues = [val.value];
@ -154,11 +155,11 @@ export default {
<style lang="scss" scoped> <style lang="scss" scoped>
.button-question-overflow { .button-question-overflow {
height: calc(100vh - 280px); height: calc(100vh - 252px);
.overflow-scroll { .overflow-scroll {
// Height will be determined by overall height of content above list // Height will be determined by overall height of content above list
height: calc(100% - 320px); height: calc(100% - 300px);
-webkit-overflow-scrolling: touch; -webkit-overflow-scrolling: touch;
} }
} }

View file

@ -10,6 +10,7 @@
buttonType="listCard" buttonType="listCard"
v-model="selectedValues" v-model="selectedValues"
:validationRules="validationRules" :validationRules="validationRules"
:suppressError="suppressError"
/> />
</div> </div>
</transition> </transition>
@ -35,6 +36,7 @@ export default ({
modelValue: Array, modelValue: Array,
isMultiSelect: Boolean, isMultiSelect: Boolean,
validationRules: String, validationRules: String,
suppressError: Boolean,
}, },
methods: { methods: {
initializeComponent(cmsContent, replaceOptions){ initializeComponent(cmsContent, replaceOptions){

View file

@ -19,6 +19,7 @@
ref="windshieldOptions" ref="windshieldOptions"
v-model="selectedWindshieldOptions" v-model="selectedWindshieldOptions"
:hasRepairReplaceConflict="hasRepairReplaceConflict" :hasRepairReplaceConflict="hasRepairReplaceConflict"
:hasSplitSingleConflict="hasSplitSingleConflict"
:selectedDamageLocations="selectedDamageLocations" :selectedDamageLocations="selectedDamageLocations"
/> />
<alert <alert
@ -392,6 +393,23 @@ export default {
hasRepairReplaceConflict() { hasRepairReplaceConflict() {
return this.isWindshieldDamageLocation && this.selectedDamageLocations.length > 1 && this.isWindshieldRepair; return this.isWindshieldDamageLocation && this.selectedDamageLocations.length > 1 && this.isWindshieldRepair;
}, },
hasSplitSingleConflict() {
if (!this.selectedWindshieldOptions.selectedWindshieldReplaceOptions) return false;
return this.selectedWindshieldOptions.selectedWindshieldReplaceOptions.some(selectedSingleWindshield =>
{
return selectedSingleWindshield.toUpperCase() === damageLocationsSelected.SINGLE.toUpperCase();
}) &&
(this.selectedWindshieldOptions.selectedWindshieldReplaceOptions.some(selectedDriverWindshield =>
{
return selectedDriverWindshield.toUpperCase() === damageLocationsSelected.DRIVER.toUpperCase();
}) ||
this.selectedWindshieldOptions.selectedWindshieldReplaceOptions.some(selectedPassengerWindshield =>
{
return selectedPassengerWindshield.toUpperCase() === damageLocationsSelected.PASSENGER.toUpperCase();
})
);
},
}, },
components: { components: {
@ -408,11 +426,3 @@ export default {
}, },
}; };
</script> </script>
<style lang="scss">
.vehicle-damage-form {
display: flex;
flex-direction: column;
height: 100%;
}
</style>

View file

@ -1,12 +1,20 @@
<template> <template>
<div class="windshield-options"> <div class="windshield-options">
<windshieldDamageTypeQuestion ref="windshieldDamageTypeQuestion" <windshieldDamageTypeQuestion ref="windshieldDamageTypeQuestion"
:isAvailable=isWindshieldDamageLocation :isAvailable=isWindshieldDamageLocation
:suppressError="hasRepairReplaceConflict" :suppressError="hasRepairReplaceConflict || showNoReplacementAvailableError"
groupName="WindshieldDamageTypeQuestion" groupName="WindshieldDamageTypeQuestion"
v-model="selectedWindshieldDamageTypeValues" v-model="selectedWindshieldDamageTypeValues"
validationRules="windshield-damage-type-required|checkForRepairAndReplace:@DamageLocationQuestion" :validationRules="windshieldDamageTypeQuestionValidationRules"
/> />
<alert
class="my-3"
v-show="showNoReplacementAvailableError"
alertClass="alert-danger"
alertHeadline="Service not available"
alertCopy="We're sorry, but we currently offer only repair service for your vehicle type. Need help with next steps? Call us at 800-394-0288."
:isDismissible="false"
/>
<windshieldChipCountQuestion ref="windshieldChipCountQuestion" <windshieldChipCountQuestion ref="windshieldChipCountQuestion"
:isAvailable="isRepairOptionSelected && !hasRepairReplaceConflict" :isAvailable="isRepairOptionSelected && !hasRepairReplaceConflict"
groupName="WindshieldChipCountQuestion" groupName="WindshieldChipCountQuestion"
@ -18,8 +26,17 @@
isMultiSelect isMultiSelect
groupName="WindshieldReplaceOptions" groupName="WindshieldReplaceOptions"
v-model="selectedWindshieldReplaceOptionsValues" v-model="selectedWindshieldReplaceOptionsValues"
validationRules="windshield-replace-options-required" validationRules="windshield-replace-options-required|preventSplitAndSingleTogether"
:suppressError="hasSplitSingleConflict"
/> />
<alert
class="my-3"
v-show="hasSplitSingleConflict"
alertClass="alert-danger"
alertHeadline="Single-piece or split?"
alertCopy="Please select just one windshield option: single-piece or split."
:isDismissible="false"
/>
</div> </div>
</template> </template>
@ -27,18 +44,40 @@
import windshieldDamageTypeQuestion from"@/layouts/vehicle-damage/windshield-options/windshield-damage-type-question/windshield-damage-type-question"; import windshieldDamageTypeQuestion from"@/layouts/vehicle-damage/windshield-options/windshield-damage-type-question/windshield-damage-type-question";
import windshieldChipCountQuestion from"@/layouts/vehicle-damage/windshield-options/windshield-chip-count-question/windshield-chip-count-question"; import windshieldChipCountQuestion from"@/layouts/vehicle-damage/windshield-options/windshield-chip-count-question/windshield-chip-count-question";
import replaceOptionsQuestion from"@/layouts/vehicle-damage/replace-options-question/replace-options-question"; import replaceOptionsQuestion from"@/layouts/vehicle-damage/replace-options-question/replace-options-question";
import alert from "@/ux-components/alert/alert";
import { defineRule } from "vee-validate"; import { defineRule } from "vee-validate";
import { required } from "@/helpers/validation-rules"; import { required } from "@/helpers/validation-rules";
import { errorMessages } from "@/constants/error-messages"; import { errorMessages } from "@/constants/error-messages";
import { damageLocationsSelected } from "@/constants/damage-locations-selected.js";
// DEFINE VALIDATION RULES // DEFINE VALIDATION RULES
defineRule("windshield-damage-type-required", required(errorMessages.WINDSHIELD_DAMAGE_TYPE_REQUIRED)); defineRule("windshield-damage-type-required", required(errorMessages.WINDSHIELD_DAMAGE_TYPE_REQUIRED));
defineRule("windshield-chip-count-required", required(errorMessages.WINDSHIELD_CHIP_COUNT_REQUIRED)); defineRule("windshield-chip-count-required", required(errorMessages.WINDSHIELD_CHIP_COUNT_REQUIRED));
defineRule("windshield-replace-options-required", required(errorMessages.WINSHIELD_REPLACE_OPTIONS_REQUIRED)); defineRule("windshield-replace-options-required", required(errorMessages.WINSHIELD_REPLACE_OPTIONS_REQUIRED));
defineRule("checkForRepairAndReplace", (value, [other]) => { defineRule("checkForRepairAndReplace", (value, [otherFieldValue]) => {
if (value.toString().toUpperCase().includes("REPAIR") && other.toString().toUpperCase().includes("WINDSHIELD") && other.length > 1) { if (value.toString().toUpperCase().includes(damageLocationsSelected.REPAIR.toUpperCase()) &&
return "checkForRepairAndReplace error" otherFieldValue.toString().toUpperCase().includes(damageLocationsSelected.WINDSHIELD.toUpperCase()) &&
Array.isArray(otherFieldValue) &&
otherFieldValue.length > 1)
{
return false;
}
return true;
});
defineRule("repairOnly", (value) => {
if (value.toString().toUpperCase() === damageLocationsSelected.REPAIR.toUpperCase()) {
return true;
}
return false;
});
defineRule("preventSplitAndSingleTogether", (value) => {
if (value.toString().toUpperCase().includes(damageLocationsSelected.SINGLE.toUpperCase()) &&
(value.toString().toUpperCase().includes(damageLocationsSelected.DRIVER.toUpperCase()) ||
value.toString().toUpperCase().includes(damageLocationsSelected.PASSENGER.toUpperCase())))
{
return false;
} }
return true; return true;
}); });
@ -46,10 +85,17 @@ defineRule("checkForRepairAndReplace", (value, [other]) => {
export default ({ export default ({
name: "windshieldOptions", name: "windshieldOptions",
data(){
return {
windshieldAvailableReplacementOptions: Object,
}
},
props: { props: {
modelValue: Array, modelValue: Array,
selectedDamageLocations: Array, selectedDamageLocations: Array,
hasRepairReplaceConflict: Boolean hasRepairReplaceConflict: Boolean,
hasSplitSingleConflict: Boolean,
}, },
methods: { methods: {
@ -59,12 +105,15 @@ export default ({
this.$refs.windshieldDamageTypeQuestion.initializeComponent(windshieldDamageTypeQuestionFromCms); this.$refs.windshieldDamageTypeQuestion.initializeComponent(windshieldDamageTypeQuestionFromCms);
this.$refs.windshieldChipCountQuestion.initializeComponent(windshieldChipCountQuestionFromCms); this.$refs.windshieldChipCountQuestion.initializeComponent(windshieldChipCountQuestionFromCms);
this.$refs.replaceOptionsQuestion.initializeComponent(windshieldReplaceOptionsQuestionFromCms, windshieldAvailableReplacementOptions); this.$refs.replaceOptionsQuestion.initializeComponent(windshieldReplaceOptionsQuestionFromCms, windshieldAvailableReplacementOptions);
this.windshieldAvailableReplacementOptions = windshieldAvailableReplacementOptions;
}, },
getWindshieldOptions(selectedWindshieldDamageType, selectedWindshieldChipCount, selectedWindshieldReplaceOptions){ getWindshieldOptions(selectedWindshieldDamageType, selectedWindshieldChipCount, selectedWindshieldReplaceOptions){
// ONLY UPDATE THE NEW VALUE IF IT IS TRUTHY (NOT NULL)
return { return {
selectedWindshieldDamageType: selectedWindshieldDamageType, selectedWindshieldDamageType: selectedWindshieldDamageType ? selectedWindshieldDamageType : this.selectedValues.selectedWindshieldDamageType,
selectedWindshieldChipCount: selectedWindshieldChipCount, selectedWindshieldChipCount: selectedWindshieldChipCount ? selectedWindshieldChipCount : this.selectedValues.selectedWindshieldChipCount,
selectedWindshieldReplaceOptions: selectedWindshieldReplaceOptions selectedWindshieldReplaceOptions: selectedWindshieldReplaceOptions ? selectedWindshieldReplaceOptions : this.selectedValues.selectedWindshieldReplaceOptions,
} }
}, },
}, },
@ -102,7 +151,7 @@ export default ({
} }
}, },
isWindshieldDamageLocation() { isWindshieldDamageLocation() {
return this.selectedDamageLocations.some(selectedDamages => return this.selectedDamageLocations.some(selectedDamages =>
{ {
return Boolean(selectedDamages.toUpperCase() === "WINDSHIELD"); return Boolean(selectedDamages.toUpperCase() === "WINDSHIELD");
}); });
@ -117,11 +166,37 @@ export default ({
return this.selectedWindshieldDamageTypeValues.some(val => val.toUpperCase() === "REPLACE") && this.isWindshieldDamageLocation; return this.selectedWindshieldDamageTypeValues.some(val => val.toUpperCase() === "REPLACE") && this.isWindshieldDamageLocation;
}, },
isWindshieldReplaceAvailable() {
return !(Array.isArray(this.windshieldAvailableReplacementOptions) && this.windshieldAvailableReplacementOptions.length < 1);
},
isSplitWindshieldOption() {
const options = this.windshieldAvailableReplacementOptions.toString().toUpperCase();
if (options.includes('DRIVER') && options.includes('PASSENGER')) {
return true;
}
return false;
},
showNoReplacementAvailableError() {
if (this.isReplaceOptionSelected && !this.isWindshieldReplaceAvailable) {
return true;
}
return false;
},
windshieldDamageTypeQuestionValidationRules() {
// Note: the validation rules string is not dynamic (it cannot be changed once component has been created)
let validationRules = "windshield-damage-type-required|checkForRepairAndReplace:@DamageLocationQuestion";
// if vehicle has no windshield replacement option
if (!this.isWindshieldReplaceAvailable) {
validationRules = validationRules.concat('|repairOnly');
}
return validationRules;
}
}, },
components: { components: {
windshieldDamageTypeQuestion, windshieldDamageTypeQuestion,
windshieldChipCountQuestion, windshieldChipCountQuestion,
replaceOptionsQuestion replaceOptionsQuestion,
alert,
}, },
}) })
</script> </script>

View file

@ -0,0 +1 @@
test.todo("some test to be written in the future");

View file

@ -0,0 +1,102 @@
<template>
<div class="container-fluid shadow rounded-3 p-2 position-relative make-tall">
<funnelHeader ref="funnelHeader" />
<vehicleBanner ref="vehicleBanner" />
<funnelSubHeader ref="funnelSubHeader" />
<h1>VIN Lookup Placeholder Page</h1>
<Form
@submit="onSubmit"
@invalid-submit="onInvalidSubmit"
ref="theForm"
v-slot="{ meta }"
class="d-flex flex-column h-100"
>
<funnel-footer
ref="funnelFooter"
:isDisabled="!meta.valid"
@back-clicked="backButtonAction"
@ForwardClicked="forwardButtonAction"
/>
</Form>
</div>
</template>
<script>
// Components
import funnelHeader from "@/common-components/funnel-header/funnel-header";
import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner";
import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header";
import funnelFooter from "@/common-components/funnel-footer/funnel-footer";
// Supporting files
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
import { settleAllPromises } from "@/helpers/layout-helper";
import { storeActions } from "@/constants/store-actions";
import store from "@/store";
import baseMixin from "@/mixins/base-mixin";
import { Form, defineRule } from "vee-validate";
import { required } from "@/helpers/validation-rules";
import { errorMessages } from "@/constants/error-messages";
// DEFINE VALIDATION RULES
defineRule("replace-options-required", required(errorMessages.REPLACE_OPTIONS_REQUIRED));
export default {
name: "vin-lookup",
async beforeRouteEnter(to, from, next) {
// Call APIs
const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage);
const damageOptionsPromise =
baseMixin.methods.dispatchNonBlockingStoreAction(
storeActions.GET_DAMAGE_OPTIONS,
{ carId: store.getters.vehicle.carId }
);
// Settle promises and get results
const promiseResultMap = [
{
resultKey: "cmsContent",
promise: cmsContentPromise,
},
{
resultKey: "damageOptions",
promise: damageOptionsPromise,
},
];
const resultMap = await settleAllPromises(promiseResultMap);
// Call the "next" function to complete the transition to this page.
next((vm) => {
vm.$refs.funnelHeader.initializeComponent(
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: {
arePagePrerequisitesValid() {
return true;
},
},
components: {
funnelHeader,
vehicleBanner,
funnelSubHeader,
funnelFooter,
Form,
},
};
</script>
<style lang="scss">
</style>

View file

@ -4,9 +4,10 @@ const fmgPageValues = {
VEHICLE_MODEL: "vehicle-model", VEHICLE_MODEL: "vehicle-model",
VEHICLE_STYLE: "vehicle-style", VEHICLE_STYLE: "vehicle-style",
VEHICLE_DAMAGE: "vehicle-damage", VEHICLE_DAMAGE: "vehicle-damage",
VIN_LOOKUP: "vin-lookup",
VEHICLE_PARTS: "vehicle-parts", VEHICLE_PARTS: "vehicle-parts",
PART_QUESTIONS: "part-questions", PART_QUESTIONS: "part-questions",
REVEAL : "reveal", REVEAL : "reveal",
}; };
export { fmgPageValues }; export { fmgPageValues };

View file

@ -94,6 +94,19 @@ const routingTable = [
}, },
], ],
}, },
{
fmgPageValue: fmgPageValues.REVEAL,
maps: [
{
scenario: navigationScenarios.CLICKED_BACK,
destinationFmgPageValue: fmgPageValues.VEHICLE_DAMAGE,
},
{
scenario: navigationScenarios.SELECTED_PARTS,
destinationFmgPageValue: fmgPageValues.VIN_LOOKUP,
},
],
},
]; ];
export { routingTable }; export { routingTable };

View file

@ -128,11 +128,7 @@ export const mutations = {
resetDamageState(state) { resetDamageState(state) {
state.order.damage.isRepair = null; state.order.damage.isRepair = null;
state.order.damage.numberOfChips = null; state.order.damage.numberOfChips = null;
state.order.damage.windshieldGlassToReplace = null; state.order.damage.glassToReplace = null;
state.order.damage.driverSideGlassToReplace = null;
state.order.damage.passengerSideGlassToReplace = null;
state.order.damage.rearGlassToReplace = null;
}, },
resetRegistrationState(state) { resetRegistrationState(state) {

View file

@ -139,19 +139,13 @@ describe("Mutations", () => {
storeState.order.damage = { storeState.order.damage = {
isRepair: true, isRepair: true,
numberOfChips: 2, numberOfChips: 2,
windshieldGlassToReplace: "Front", glassToReplace: [{location: 'Rear', name: 'Stationary'}]
driverSideGlassToReplace: "Rear",
passengerSideGlassToReplace: "Rear",
rearGlassToReplace: "Slider"
} }
// Expect // Expect
expect(storeState.order.damage.isRepair).toEqual(true); expect(storeState.order.damage.isRepair).toEqual(true);
expect(storeState.order.damage.numberOfChips).toEqual(2); expect(storeState.order.damage.numberOfChips).toEqual(2);
expect(storeState.order.damage.windshieldGlassToReplace).toEqual("Front"); expect(storeState.order.damage.glassToReplace).toStrictEqual([{location: 'Rear', name: 'Stationary'}]);
expect(storeState.order.damage.driverSideGlassToReplace).toEqual("Rear");
expect(storeState.order.damage.passengerSideGlassToReplace).toEqual("Rear");
expect(storeState.order.damage.rearGlassToReplace).toEqual("Slider");
// Act // Act
mutations.resetDamageState(storeState); mutations.resetDamageState(storeState);
@ -159,11 +153,7 @@ describe("Mutations", () => {
// Expect // Expect
expect(storeState.order.damage.isRepair).toEqual(null); expect(storeState.order.damage.isRepair).toEqual(null);
expect(storeState.order.damage.numberOfChips).toEqual(null); expect(storeState.order.damage.numberOfChips).toEqual(null);
expect(storeState.order.damage.windshieldGlassToReplace).toEqual(null); expect(storeState.order.damage.glassToReplace).toEqual(null);
expect(storeState.order.damage.driverSideGlassToReplace).toEqual(null);
expect(storeState.order.damage.passengerSideGlassToReplace).toEqual(null);
expect(storeState.order.damage.rearGlassToReplace).toEqual(null);
}); });
it("Updates number of chips in state", () => { it("Updates number of chips in state", () => {

View file

@ -3,6 +3,7 @@
body { body {
font-size: 16px; font-size: 16px;
background-color: #fff; background-color: #fff;
color: #4D5151;
.container-fluid { .container-fluid {
max-width: 576px; //Remove once desktop app is complete max-width: 576px; //Remove once desktop app is complete
&.container-shadow { &.container-shadow {
@ -22,7 +23,7 @@ body {
} }
.container, .container,
.container-fluid { .container-fluid {
overflow-x: hidden; overflow-x: hidden !important;
} }
.sr-only { .sr-only {
@ -34,4 +35,3 @@ body {
overflow: hidden; overflow: hidden;
} }
} }
// Hide horizontal scrollbar

View file

@ -107,6 +107,9 @@ $theme-colors: (
"dark": $dark "dark": $dark
); );
//Default font color
$body-color: $gray-600;
//Fonts //Fonts
$font-family-sans-serif: Roboto, Arial, Helvetica, sans-serif; $font-family-sans-serif: Roboto, Arial, Helvetica, sans-serif;
$font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; $font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;