287 lines
9.2 KiB
Vue
287 lines
9.2 KiB
Vue
<template>
|
|
<transition
|
|
name="fade"
|
|
mode="out-in">
|
|
<div
|
|
v-if="isDisplayed"
|
|
class="shop-question"
|
|
aria-live="polite">
|
|
<alert
|
|
v-if="displayDropoffInformation"
|
|
ref="alertDropoffInformation"
|
|
class="mb-4 drop-off-alert"
|
|
cmsWidgetName="AlertDropoffInformationWidget"
|
|
alertClass="alert-info"
|
|
:isDismissible="false" />
|
|
<buttonQuestion
|
|
ref="buttonQuestion"
|
|
v-model="selectedProviderNumber"
|
|
buttonTypeString="shopListButton"
|
|
:buttonTypeObject="shopListButton"
|
|
class="radioQuestion"
|
|
:questionText="questionText"
|
|
:answers="answers"
|
|
groupName="chooseShop"
|
|
textPosition="text-start"
|
|
isRequired
|
|
validationRules="option-required"
|
|
:additionalButtonData="additionalButtonData" />
|
|
<textLink
|
|
v-show="displaySeeMoreLocationsLink"
|
|
id="showMoreShopsId"
|
|
ref="showMoreShopsLink"
|
|
class="show-more-shops-link"
|
|
cmsWidgetName="ShowMoreShopsLinkWidget"
|
|
linkType="text"
|
|
:text="showMoreShopsLinkText"
|
|
href="#!"
|
|
:aria-label="showMoreShopsLinkText"
|
|
@clickEvent="getNextShopsFromList" />
|
|
</div>
|
|
</transition>
|
|
</template>
|
|
|
|
<script>
|
|
// Components
|
|
import alert from '@/ux-components/alert/alert.vue';
|
|
import buttonQuestion from '@/digital-components/button-question/button-question.vue';
|
|
import textLink from '@/ux-components/text-link/text-link.vue';
|
|
|
|
// Supporting files
|
|
import { AppointmentTypeStrings } from '@/constants/schedule-constants.js';
|
|
import baseMixin from '@/mixins/base-mixin.js';
|
|
import { defineRule } from 'vee-validate';
|
|
import { required } from '@/helpers/validation-rules';
|
|
import errorMessages from '@/constants/error-messages';
|
|
import { useMainStore } from '@/store/index.js';
|
|
import { nextTick } from 'vue';
|
|
import { getAvailabilityRating } from '@/helpers/service-location-helper';
|
|
import shopListButton from './shop-list-button/shop-list-button.vue';
|
|
|
|
defineRule('option-required', required(errorMessages.OPTION_REQUIRED));
|
|
|
|
export default {
|
|
name: 'shop-question',
|
|
components: {
|
|
alert,
|
|
buttonQuestion,
|
|
textLink
|
|
},
|
|
mixins: [baseMixin],
|
|
props: {
|
|
modelValue: {
|
|
type: Object,
|
|
default: () => null
|
|
},
|
|
selectedAppointmentType: String,
|
|
cmsWidgetName: String,
|
|
validationRules: String,
|
|
isDisplayed: Boolean
|
|
},
|
|
emits: ['update:modelValue'],
|
|
data() {
|
|
return {
|
|
shopProviders: [],
|
|
shopListButton,
|
|
answers: [],
|
|
shopIndex: 0,
|
|
displaySeeMoreLocationsLink: false
|
|
};
|
|
},
|
|
computed: {
|
|
questionText() {
|
|
return this.getCmsContent(this.cmsWidgetName, 'QuestionText');
|
|
},
|
|
selectedValue: {
|
|
get() {
|
|
return this.modelValue;
|
|
},
|
|
set(newValue) {
|
|
this.$emit('update:modelValue', newValue);
|
|
}
|
|
},
|
|
selectedProviderNumber: {
|
|
get() {
|
|
return this.selectedValue?.providerNumber;
|
|
},
|
|
set(newValue) {
|
|
// Button Question only supports Number, or String data types so we must get the full object to emit
|
|
this.selectedValue = this.getSelectedProviderObject(newValue);
|
|
}
|
|
},
|
|
displayDropoffInformation() {
|
|
return this.selectedAppointmentType === 'Dropoff';
|
|
},
|
|
showMoreShopsLinkText() {
|
|
return this.getCmsContent('ShowMoreShopsLinkWidget', 'Text');
|
|
},
|
|
additionalButtonData() {
|
|
const startDate = new Date();
|
|
const endDate = new Date();
|
|
endDate.setDate(startDate.getDate() + 6);
|
|
|
|
const formattedStartDate = startDate.toISOString().split('T')[0];
|
|
const formattedEndDate = endDate.toISOString().split('T')[0];
|
|
|
|
return {
|
|
availabilityRatingCallback: getAvailabilityRating,
|
|
startDate: formattedStartDate,
|
|
endDate: formattedEndDate,
|
|
shopAppointmentType: this.selectedAppointmentType
|
|
};
|
|
}
|
|
},
|
|
watch: {
|
|
selectedAppointmentType: {
|
|
async handler(newValue) {
|
|
this.resetAnswers();
|
|
|
|
await nextTick();
|
|
|
|
this.selectedProviderNumber = null;
|
|
|
|
await nextTick();
|
|
|
|
if (newValue !== AppointmentTypeStrings.MOBILE && newValue !== AppointmentTypeStrings.MOBILE_INSURANCE) {
|
|
this.getNextShopsFromList();
|
|
}
|
|
}
|
|
},
|
|
shopProviders: {
|
|
async handler(newValue) {
|
|
await nextTick();
|
|
|
|
if (this.selectedAppointmentType) {
|
|
const selectedShopIndex = this.getSelectedProviderIndex(
|
|
newValue,
|
|
this.selectedProviderNumber
|
|
);
|
|
|
|
if (selectedShopIndex >= 3) {
|
|
await this.getNextShopsFromList(selectedShopIndex + 1);
|
|
} else {
|
|
await this.getNextShopsFromList();
|
|
await nextTick();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
methods: {
|
|
loadInitialData(serviceZipCode) {
|
|
return this.loadData(serviceZipCode);
|
|
},
|
|
loadData(serviceZipCode) {
|
|
return useMainStore().getProviders(serviceZipCode);
|
|
},
|
|
initializeComponent(shopQuestionInitialData) {
|
|
this.shopProviders = shopQuestionInitialData.shopProviders;
|
|
},
|
|
async getNextShopsFromList(numberToGet = 3) {
|
|
const shopIterator = (array, n) => {
|
|
const l = array.length;
|
|
return () => {
|
|
const end = this.shopIndex + n;
|
|
const part = array.slice(this.shopIndex, end);
|
|
this.shopIndex = end < l ? end : this.shopProviders.length;
|
|
return part;
|
|
};
|
|
};
|
|
|
|
const toTitleCase = (str) => str.replace(/\w\S*/g, (txt) => txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase());
|
|
|
|
const nextShop = shopIterator(this.shopProviders, numberToGet);
|
|
|
|
// Map API result data
|
|
const mappedData = nextShop().map((shopProvider) => {
|
|
const streetAddress = toTitleCase(shopProvider.address.streetAddress);
|
|
const city = toTitleCase(shopProvider.address.city);
|
|
const { state } = shopProvider.address;
|
|
const { zipCode } = shopProvider.address;
|
|
const distanceInMiles = Math.round(shopProvider.distanceInMiles * 2) / 2;
|
|
|
|
return {
|
|
buttonLabel: city,
|
|
buttonLabelSubCopy: `${distanceInMiles} mi`,
|
|
buttonBodyCopy: `${streetAddress}, ${city}, ${state} ${zipCode}`,
|
|
value: shopProvider.providerNumber
|
|
};
|
|
});
|
|
|
|
if (this.answers.length === 0) {
|
|
this.answers = mappedData;
|
|
} else {
|
|
mappedData.forEach((shop) => {
|
|
this.answers.push(shop);
|
|
});
|
|
}
|
|
|
|
await nextTick();
|
|
|
|
if (this.shopIndex === this.shopProviders.length) {
|
|
this.displaySeeMoreLocationsLink = false;
|
|
} else {
|
|
this.displaySeeMoreLocationsLink = true;
|
|
}
|
|
|
|
await nextTick();
|
|
|
|
this.scrollToPageBottom();
|
|
},
|
|
resetAnswers() {
|
|
this.answers = [];
|
|
this.shopIndex = 0;
|
|
},
|
|
async reloadShopData(serviceZipCode) {
|
|
const result = await this.loadData(serviceZipCode);
|
|
this.initializeComponent(result.data);
|
|
|
|
this.resetAnswers();
|
|
|
|
await nextTick();
|
|
await this.getNextShopsFromList();
|
|
},
|
|
getSelectedProviderObject(providerNumber) {
|
|
const provider = this.shopProviders?.find((p) => p.providerNumber === providerNumber);
|
|
return provider;
|
|
},
|
|
getSelectedProviderIndex(providers, selectedProviderNumber) {
|
|
const index = providers.findIndex((p) => p.providerNumber === selectedProviderNumber);
|
|
return index;
|
|
}
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
@import "@/styles/ux-variables-svg-strings.scss";
|
|
|
|
.shop-question {
|
|
margin-top: 1rem;
|
|
text-align: center;
|
|
|
|
.button-question {
|
|
.question-text {
|
|
margin-top: 0.5rem;
|
|
}
|
|
}
|
|
|
|
.drop-off-alert {
|
|
background: url($svg-drop-off-alert);
|
|
background-repeat: no-repeat;
|
|
background-size: 0.75rem;
|
|
background-position: 0.5rem 0.75rem;
|
|
border-radius: 0.5rem;
|
|
display: flex;
|
|
flex-direction: row;
|
|
padding: 0.5rem 0.5rem 0.5rem 1.5rem !important;
|
|
gap: 0.25rem;
|
|
|
|
.alert-heading {
|
|
text-align: left;
|
|
font-size: 0.75rem;
|
|
line-height: 1.25rem;
|
|
}
|
|
}
|
|
}
|
|
</style>
|