292 lines
12 KiB
Vue
292 lines
12 KiB
Vue
<template>
|
|
<transition name="fade" mode="out-in">
|
|
<div v-if="isDisplayed" class="shop-question" aria-live="polite">
|
|
<alert
|
|
ref="alertDropoffInformation"
|
|
v-if="displayDropoffInformation"
|
|
class="mb-4 drop-off-alert"
|
|
cmsWidgetName="AlertDropoffInformationWidget"
|
|
alertClass="alert-info"
|
|
v-bind:isDismissible="false" />
|
|
<buttonQuestion
|
|
ref="buttonQuestion"
|
|
buttonTypeString="shopListButton"
|
|
:buttonTypeObject="shopListButton"
|
|
class="radioQuestion"
|
|
:questionText="questionText"
|
|
:answers="answers"
|
|
groupName="chooseShop"
|
|
textPosition="text-start"
|
|
v-model="selectedProviderNumber"
|
|
isRequired
|
|
validationRules="option-required"
|
|
:additionalButtonData="additionalButtonData" />
|
|
<textLink
|
|
v-show="displaySeeMoreLocationsLink"
|
|
ref="showMoreShopsLink"
|
|
class="show-more-shops-link"
|
|
id="showMoreShopsId"
|
|
cmsWidgetName="ShowMoreShopsLinkWidget"
|
|
linkType="text"
|
|
:text="showMoreShopsLinkText"
|
|
href="#!"
|
|
@click-event="getNextShopsFromList"
|
|
:aria-label="showMoreShopsLinkText" />
|
|
</div>
|
|
</transition>
|
|
</template>
|
|
|
|
<script>
|
|
// Components
|
|
import alert from "@/ux-components/alert/alert";
|
|
import buttonQuestion from "@/digital-components/button-question/button-question";
|
|
import shopListButton from "./shop-list-button/shop-list-button";
|
|
import textLink from "@/ux-components/text-link/text-link";
|
|
|
|
import { storeActions } from "@/constants/store-actions.js";
|
|
|
|
// Supporting files
|
|
import { defineRule } from "vee-validate";
|
|
import { required } from "@/helpers/validation-rules";
|
|
import { errorMessages } from "@/constants/error-messages";
|
|
import baseMixin from "@/mixins/base-mixin.js";
|
|
import { nextTick } from "vue";
|
|
|
|
import { getAvailabilityRating } from "@/layouts/service-location/helpers/service-location-helper/service-location-helper";
|
|
|
|
defineRule("option-required", required(errorMessages.OPTION_REQUIRED));
|
|
|
|
export default {
|
|
name: "shop-question",
|
|
mixins: [baseMixin],
|
|
data() {
|
|
return {
|
|
shopProviders: [],
|
|
shopListButton: shopListButton,
|
|
answers: [],
|
|
shopIndex: 0,
|
|
displaySeeMoreLocationsLink: false,
|
|
};
|
|
},
|
|
props: {
|
|
modelValue: {
|
|
type: Object,
|
|
default: () => null,
|
|
},
|
|
selectedAppointmentType: String,
|
|
cmsWidgetName: String,
|
|
validationRules: String,
|
|
isDisplayed: Boolean,
|
|
},
|
|
computed: {
|
|
questionText() {
|
|
return this.getCmsContent(this.cmsWidgetName, "QuestionText");
|
|
},
|
|
selectedValue: {
|
|
get: function () {
|
|
return this.modelValue;
|
|
},
|
|
set: function (newValue) {
|
|
this.$emit("update:modelValue", newValue);
|
|
},
|
|
},
|
|
selectedProviderNumber: {
|
|
get: function () {
|
|
return this.selectedValue?.providerNumber;
|
|
},
|
|
set: function (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,
|
|
};
|
|
},
|
|
},
|
|
methods: {
|
|
loadInitialData(serviceZipCode) {
|
|
return this.loadData(serviceZipCode);
|
|
},
|
|
loadData(serviceZipCode) {
|
|
return baseMixin.methods.dispatchStoreAction(storeActions.GET_PROVIDERS, {
|
|
serviceZipCode: 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) => {
|
|
return str.replace(/\w\S*/g, function (txt) {
|
|
return 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.state;
|
|
const zipCode = shopProvider.address.zipCode;
|
|
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(
|
|
(provider) => provider.providerNumber == providerNumber
|
|
);
|
|
|
|
return provider;
|
|
},
|
|
getSelectedProviderIndex(providers, selectedProviderNumber) {
|
|
const index = providers.findIndex(
|
|
(provider) => provider.providerNumber == selectedProviderNumber
|
|
);
|
|
|
|
return index;
|
|
},
|
|
},
|
|
watch: {
|
|
selectedAppointmentType: {
|
|
async handler(newValue) {
|
|
this.resetAnswers();
|
|
|
|
await nextTick();
|
|
|
|
this.selectedProviderNumber = null;
|
|
|
|
await nextTick();
|
|
|
|
if (newValue !== "Mobile") {
|
|
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();
|
|
}
|
|
}
|
|
},
|
|
},
|
|
},
|
|
components: {
|
|
alert,
|
|
buttonQuestion,
|
|
textLink,
|
|
},
|
|
};
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
.shop-question {
|
|
margin-top: 1rem;
|
|
text-align: center;
|
|
|
|
.button-question {
|
|
.question-text {
|
|
margin-top: 0.5rem;
|
|
}
|
|
}
|
|
}
|
|
|
|
.drop-off-alert {
|
|
background: url("data:image/svg+xml,%3Csvg viewBox='0 0 12 12' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cg clip-path='url(%23clip0_13957_112512)'%3E%3Cpath d='M5.99865 0C4.81147 4.82643e-07 3.65095 0.352111 2.66392 1.01179C1.67688 1.67146 0.907678 2.60907 0.45361 3.70599C-0.000459241 4.80291 -0.11899 6.00986 0.113013 7.17415C0.345015 8.33845 0.917126 9.40778 1.75697 10.2469C2.59682 11.086 3.66666 11.6571 4.83117 11.8881C5.99567 12.119 7.20251 11.9994 8.29902 11.5443C9.39553 11.0893 10.3324 10.3192 10.9912 9.33159C11.65 8.34396 12.0011 7.18313 12 5.99594C11.9971 4.40566 11.3638 2.88142 10.2388 1.75742C9.11375 0.633431 7.58894 0.00143011 5.99865 0V0ZM5.99865 11.2478C4.96135 11.2473 3.94748 10.9392 3.08518 10.3627C2.22288 9.7861 1.55085 8.96685 1.15401 8.00846C0.75718 7.05006 0.653353 5.99554 0.855656 4.97815C1.05796 3.96077 1.55731 3.02618 2.29061 2.29251C3.0239 1.55884 3.95823 1.059 4.97551 0.856176C5.99279 0.653349 7.04737 0.756633 8.00597 1.15297C8.96457 1.54931 9.78416 2.22092 10.3612 3.08293C10.9382 3.94493 11.2467 4.95864 11.2478 5.99594C11.2478 7.38835 10.6949 8.72377 9.71053 9.70861C8.7262 10.6934 7.39106 11.2471 5.99865 11.2478V11.2478Z' fill='%2306577C'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M6.22736 8.84695C6.30613 8.76818 6.35038 8.66135 6.35038 8.54996V5.30996C6.35038 5.19857 6.30613 5.09174 6.22736 5.01298C6.1486 4.93421 6.04177 4.88996 5.93038 4.88996C5.81899 4.88996 5.71216 4.93421 5.63339 5.01298C5.55463 5.09174 5.51038 5.19857 5.51038 5.30996V8.54996C5.51038 8.66135 5.55463 8.76818 5.63339 8.84695C5.71216 8.92571 5.81899 8.96996 5.93038 8.96996C6.04177 8.96996 6.1486 8.92571 6.22736 8.84695ZM5.69704 3.97918C5.76611 4.02533 5.84731 4.04996 5.93038 4.04996C5.98558 4.05012 6.04026 4.03936 6.09129 4.01831C6.14232 3.99726 6.18868 3.96633 6.22771 3.9273C6.26675 3.88827 6.29768 3.8419 6.31873 3.79088C6.33978 3.73985 6.35053 3.68516 6.35038 3.62996C6.35038 3.54689 6.32574 3.46569 6.27959 3.39662C6.23344 3.32755 6.16785 3.27372 6.0911 3.24193C6.01436 3.21014 5.92991 3.20183 5.84844 3.21803C5.76697 3.23424 5.69213 3.27424 5.63339 3.33298C5.57465 3.39171 5.53465 3.46655 5.51845 3.54802C5.50224 3.6295 5.51056 3.71394 5.54235 3.79069C5.57414 3.86743 5.62797 3.93303 5.69704 3.97918Z' fill='%2306577C'/%3E%3C/g%3E%3Cdefs%3E%3CclipPath id='clip0_13957_112512'%3E%3Crect width='12' height='12' fill='white'/%3E%3C/clipPath%3E%3C/defs%3E%3C/svg%3E%0A");
|
|
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;
|
|
gap: 0.25rem;
|
|
|
|
.alert-heading {
|
|
text-align: left;
|
|
font-size: 0.75rem;
|
|
line-height: 1.25rem;
|
|
}
|
|
}
|
|
</style>
|