CASH-816
CASH-816 finished moving shop functionality into drawer
This commit is contained in:
parent
f7bb4f3736
commit
55b9a86fe4
5 changed files with 530 additions and 299 deletions
|
|
@ -50,6 +50,7 @@
|
|||
:autocomplete="autocomplete" />
|
||||
<button
|
||||
v-if="includeSearchIcon"
|
||||
class="search-icon-button"
|
||||
type="submit"
|
||||
aria-label="Search button"
|
||||
@click="focusSearchInput" />
|
||||
|
|
@ -185,6 +186,7 @@ export default {
|
|||
// Focus cursor in input when search icon is clicked
|
||||
const field = document.querySelector("input");
|
||||
field.focus();
|
||||
this.$emit("search-icon-click");
|
||||
},
|
||||
async imageChanged(e) {
|
||||
let file = e.target.files[0];
|
||||
|
|
@ -263,6 +265,10 @@ export default {
|
|||
.input-wrapper {
|
||||
position: relative;
|
||||
&.has-search-icon {
|
||||
.form-control {
|
||||
border-radius: 48px;
|
||||
padding-left: 1.5rem;
|
||||
}
|
||||
button[type="submit"] {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
|
|
@ -271,10 +277,11 @@ export default {
|
|||
background-image: url("data:image/svg+xml,%3Csvg width='16' height='16' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M15.7817 14.7328L11.8252 10.7762C12.8833 9.45005 13.3936 7.76911 13.2513 6.07849C13.1091 4.38788 12.325 2.81587 11.0601 1.6852C9.79515 0.554524 8.14538 -0.0490261 6.44946 -0.00154744C4.75353 0.0459312 3.14012 0.740836 1.94045 1.94051C0.740775 3.14018 0.0458701 4.75359 -0.00160848 6.44952C-0.0490871 8.14545 0.554463 9.79521 1.68514 11.0601C2.81581 12.325 4.38782 13.1091 6.07843 13.2514C7.76905 13.3937 9.44999 12.8834 10.7762 11.8252L14.7349 15.7839C14.8044 15.8527 14.8869 15.907 14.9774 15.9439C15.068 15.9808 15.165 15.9995 15.2628 15.9989C15.3606 15.9983 15.4573 15.9784 15.5475 15.9405C15.6376 15.9025 15.7194 15.8471 15.7881 15.7776C15.8568 15.708 15.9112 15.6256 15.9481 15.535C15.985 15.4444 16.0036 15.3474 16.0031 15.2496C16.0025 15.1518 15.9826 15.0551 15.9446 14.965C15.9067 14.8748 15.8513 14.7931 15.7817 14.7243V14.7328ZM6.63737 11.7913C5.61803 11.7913 4.62157 11.4891 3.77402 10.9228C2.92646 10.3564 2.26587 9.5515 1.87578 8.60975C1.4857 7.668 1.38363 6.63172 1.5825 5.63196C1.78136 4.6322 2.27222 3.71386 2.99301 2.99307C3.7138 2.27229 4.63214 1.78142 5.6319 1.58256C6.63166 1.38369 7.66793 1.48576 8.60969 1.87585C9.55144 2.26593 10.3564 2.92652 10.9227 3.77408C11.489 4.62163 11.7913 5.61809 11.7913 6.63743C11.7896 8.00382 11.2461 9.31376 10.2799 10.2799C9.3137 11.2461 8.00376 11.7897 6.63737 11.7913Z' fill='%231574A1'/%3E%3C/svg%3E%0A");
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
background-color: transparent;
|
||||
//background-color: transparent;
|
||||
width: 2.75rem;
|
||||
height: 100%;
|
||||
border: none;
|
||||
border-radius: 0 48px 48px 0;
|
||||
display: flex;
|
||||
&:hover {
|
||||
cursor: default;
|
||||
|
|
@ -351,6 +358,7 @@ export default {
|
|||
&:hover {
|
||||
box-shadow: 0 0 0 4px transparent;
|
||||
border: 1px solid $gray-500;
|
||||
//border-radius: 48px;
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
|
|
@ -362,4 +370,14 @@ export default {
|
|||
display: none;
|
||||
}
|
||||
}
|
||||
.search-icon-button {
|
||||
background-color: #e5f1fa;
|
||||
}
|
||||
.search-icon-button:hover,
|
||||
.search-icon-button:focus {
|
||||
border: 1px solid $gray-500;
|
||||
box-shadow: 0 0 0 4px $blue-300;
|
||||
background-color: #e5f1fa; // keep the background on hover
|
||||
outline: none;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
@updated-serviceability="setServiceabilityDetails"
|
||||
@updated-contains-military-base="setContainsMilitaryBase"
|
||||
@updated-bill-to-account-number="setBillToAccountNumber"
|
||||
@updated-zipcode-ctu="setZipcodeCtu"
|
||||
linkWidgetName="ServiceZipLinkWidget"
|
||||
modalWidgetName="ServiceZipModalWidget" />
|
||||
|
||||
|
|
@ -107,25 +108,36 @@
|
|||
@setMobileLocation="setMobileLocationQuestions"
|
||||
@set-mobile-location-invalid="setMobileLocationInValid" />
|
||||
|
||||
<!-- <shopQuestion
|
||||
ref="shopQuestion"
|
||||
v-show="isShopQuestionDisplayed"
|
||||
v-model="selectedProvider"
|
||||
:selectedAppointmentType="selectedAppointmentType"
|
||||
:shopProviderData="shopProviderData"
|
||||
:isDisplayed="isShopQuestionDisplayed"
|
||||
cmsWidgetName="YourSafeliteShopWidget" /> -->
|
||||
|
||||
<!-- <div>
|
||||
|
||||
</div> -->
|
||||
<button-question
|
||||
v-if="selectedAppointmentType === 'Inshop'"
|
||||
class="shop-question-button"
|
||||
ref="buttonQuestion"
|
||||
:answers="selectedShopAnswer"
|
||||
:modelValue="selectedProvider?.providerNumber"
|
||||
:isMultiSelect="false"
|
||||
:readonly="true"
|
||||
:questionText="questionText"
|
||||
buttonTypeString="shopListButton"
|
||||
:buttonTypeObject="shopListButton"
|
||||
groupName="chooseShop"
|
||||
textPosition="text-start" />
|
||||
|
||||
<shopQuestionPopup
|
||||
modalWidgetName="ShopQuestionPopupWidget"
|
||||
:selectedAppointmentType="selectedAppointmentType"
|
||||
:modelValue="{
|
||||
zipCode: zipCode,
|
||||
providerNumber: selectedProvider?.providerNumber,
|
||||
}"
|
||||
:preselectedProviderNumber="selectedProvider?.providerNumber"
|
||||
:shopProviderData="shopProviderData"
|
||||
:isServiceableInshop="isServiceableInshop"
|
||||
@updated-serviceability="setServiceabilityDetails"
|
||||
@shop-selected="onShopSelected"
|
||||
v-model="selectedProvider"
|
||||
@update:modelValue="onShopModelUpdated"
|
||||
@updated-zipcode-ctu="onZipCodeCtuUpdated"
|
||||
@update-shop-provider-data="shopProviderData = $event"
|
||||
:zipCode="zipCode"
|
||||
:isDisplayed="isShopQuestionDisplayed"
|
||||
cmsWidgetName="YourSafeliteShopWidget" />
|
||||
|
||||
|
|
@ -151,6 +163,9 @@ import mobileLocationModalQuestions from "@/layouts/service-location/mobile-loca
|
|||
import appointmentTypeQuestion from "@/layouts/service-location/appointment-type-question/appointment-type-question";
|
||||
import shopQuestion from "@/layouts/service-location/shop-question/shop-question";
|
||||
import shopQuestionPopup from "@/layouts/service-location/shop-question/shop-question-popup";
|
||||
import buttonQuestion from "@/digital-components/button-question/button-question.vue";
|
||||
import shopListButton from "@/layouts/service-location/shop-question/shop-list-button/shop-list-button";
|
||||
import { getAvailabilityRating } from "@/layouts/service-location/helpers/service-location-helper/service-location-helper";
|
||||
|
||||
import funnelHeader from "@/fmg-components/funnel-header/funnel-header";
|
||||
import navbar from "@/fmg-components/nav-bar/nav-bar";
|
||||
|
|
@ -236,6 +251,7 @@ export default {
|
|||
shopProviderData: null,
|
||||
navigatingForward: false,
|
||||
isMobileAddressValid: true,
|
||||
shopListButton: shopListButton,
|
||||
};
|
||||
},
|
||||
async beforeRouteEnter(to, from, next) {
|
||||
|
|
@ -443,6 +459,54 @@ export default {
|
|||
isForwardActionDisabled() {
|
||||
return this.displayNoShopsAlert || !this.isMobileAddressValid;
|
||||
},
|
||||
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,
|
||||
};
|
||||
},
|
||||
selectedShopAnswer() {
|
||||
const toTitleCase = (str) => {
|
||||
if (!str) return "";
|
||||
return str.replace(/\w\S*/g, function (txt) {
|
||||
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
|
||||
});
|
||||
};
|
||||
|
||||
if (this.selectedProvider && this.selectedProvider.address) {
|
||||
const provider = this.shopProviderData?.shopProviders?.find(
|
||||
(p) => p.providerNumber === this.selectedProvider?.providerNumber
|
||||
);
|
||||
const streetAddress = toTitleCase(this.selectedProvider.address.streetAddress);
|
||||
const city = toTitleCase(this.selectedProvider.address.city);
|
||||
const state = this.selectedProvider.address.state;
|
||||
const zipCode = this.selectedProvider.address.zipCode;
|
||||
const distanceInMiles = provider ? Math.round(provider.distanceInMiles * 2) / 2 : 0;
|
||||
|
||||
return [
|
||||
{
|
||||
buttonLabel: `${city}`,
|
||||
buttonLabelSubCopy: `${distanceInMiles} mi`,
|
||||
buttonBodyCopy: `${streetAddress}, ${city}, ${state} ${zipCode}`,
|
||||
//additionalButtonData: this.additionalButtonData,
|
||||
value: this.selectedProvider.providerNumber,
|
||||
},
|
||||
];
|
||||
}
|
||||
return [];
|
||||
},
|
||||
questionText() {
|
||||
return this.getCmsContent("YourSafeliteShopWidget", "QuestionText");
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
arePagePrerequisitesValid() {
|
||||
|
|
@ -726,8 +790,6 @@ export default {
|
|||
this.selectedProvider.address.zipCode = null;
|
||||
this.selectedProvider.address.zipCodeCtu = null;
|
||||
} else {
|
||||
// if we have a provider.zipCodeCtu that's different than the servicelocation.zipCodeCtu then they
|
||||
// may have selected a shop in a different ctu. change the servicelocation zip/ctu if different
|
||||
if (this.zipCodeCtu != this.selectedProvider.address.zipCodeCtu) {
|
||||
ctu = this.selectedProvider.address.zipCodeCtu;
|
||||
}
|
||||
|
|
@ -777,12 +839,59 @@ export default {
|
|||
setMobileLocationInValid(isMobileLocationInValid) {
|
||||
this.isMobileAddressValid = !isMobileLocationInValid;
|
||||
},
|
||||
onShopSelected(providerNumber) {
|
||||
// Find the provider object from your shopProviderData
|
||||
const selectedProvider = this.shopProviderData.shopProviders.find(
|
||||
(p) => p.providerNumber === providerNumber
|
||||
onShopSelected(providerObject) {
|
||||
const current = this.shopProviderData?.shopProviders?.find(
|
||||
(p) => p.providerNumber === this.selectedProvider?.providerNumber
|
||||
);
|
||||
this.selectedProvider = selectedProvider;
|
||||
if (current) {
|
||||
this.selectedProvider = current;
|
||||
}
|
||||
|
||||
let provider = this.shopProviderData?.shopProviders?.find(
|
||||
(p) => p.providerNumber === providerObject?.providerNumber
|
||||
);
|
||||
if (!provider && this.shopProviderData?.shopProviders?.length > 0) {
|
||||
provider = this.shopProviderData.shopProviders[0];
|
||||
}
|
||||
this.selectedProvider = provider;
|
||||
if (provider && provider.address) {
|
||||
this.zipCode = provider.address.zipCode;
|
||||
this.state = provider.address.state;
|
||||
}
|
||||
if (
|
||||
!this.selectedProvider ||
|
||||
!this.shopProviderData?.shopProviders?.some(
|
||||
(p) => p.providerNumber === this.selectedProvider?.providerNumber
|
||||
)
|
||||
) {
|
||||
this.selectedProvider = this.shopProviderData?.shopProviders?.[0];
|
||||
}
|
||||
if (providerObject && providerObject.address) {
|
||||
this.zipCode = providerObject.address.zipCode;
|
||||
this.state = providerObject.address.state;
|
||||
}
|
||||
},
|
||||
setZipcodeCtu(zipcodeCtu) {
|
||||
this.zipCodeCtu = zipcodeCtu;
|
||||
},
|
||||
onShopModelUpdated(newModel) {
|
||||
if (newModel.zipCode) {
|
||||
this.zipCode = newModel.zipCode;
|
||||
}
|
||||
if (newModel.zipCodeCtu) {
|
||||
this.zipCodeCtu = newModel.zipCodeCtu;
|
||||
}
|
||||
if (newModel.state) {
|
||||
this.state = newModel.state;
|
||||
}
|
||||
if (this.shopProviderData && newModel.selectedProviderNumber) {
|
||||
const provider = this.shopProviderData.shopProviders.find(
|
||||
(p) => p.providerNumber === newModel.selectedProviderNumber
|
||||
);
|
||||
if (provider) {
|
||||
this.selectedProvider = provider;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
|
|
@ -796,7 +905,7 @@ export default {
|
|||
this.shopProviderData.mobileProviderNumber
|
||||
);
|
||||
} else {
|
||||
this.selectedProvider = new Provider();
|
||||
this.isMobileAddressValid = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -819,7 +928,7 @@ export default {
|
|||
}
|
||||
this.$refs.mobileLocationQuestions.resetAlerts();
|
||||
} else {
|
||||
this.selectedProvider = new Provider();
|
||||
this.selectedProvider = this.shopProviderData.shopProviders[0];
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
@ -835,8 +944,8 @@ export default {
|
|||
Form,
|
||||
loadingModal,
|
||||
contentGroupModal,
|
||||
//shopQuestion,
|
||||
shopQuestionPopup,
|
||||
buttonQuestion,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -861,4 +970,8 @@ export default {
|
|||
color: $black;
|
||||
}
|
||||
}
|
||||
.shop-question-button .list-button-content {
|
||||
background: #e4f1f7 !important;
|
||||
box-shadow: 0 0 0 1px $primary !important;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@
|
|||
questionAlignment="center"
|
||||
cornerStyle="rounded"
|
||||
mask="#####"
|
||||
:includeSearchIcon="includeSearchIcon"
|
||||
@search-icon-click="$emit('search-icon-click')"
|
||||
:displayQuestionText="false"
|
||||
:isRequired="isRequired"
|
||||
:validationRules="computedValidationRules" />
|
||||
|
|
@ -35,6 +37,10 @@ export default {
|
|||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
includeSearchIcon: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
value: {
|
||||
|
|
|
|||
|
|
@ -8,29 +8,6 @@
|
|||
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" />
|
||||
<!-- <textLink
|
||||
v-if="displaySeeMoreLocationsLink"
|
||||
ref="showMoreShopsLink"
|
||||
class="show-more-shops-link"
|
||||
id="showMoreShopsId"
|
||||
cmsWidgetName="ShowMoreShopsLinkWidget"
|
||||
linkType="text"
|
||||
:text="showMoreShopsLinkText"
|
||||
href="#!"
|
||||
@click-event="getNextShopsFromList(1)"
|
||||
:aria-label="showMoreShopsLinkText" /> -->
|
||||
<div class="modal-link">
|
||||
<text-link
|
||||
linkType="text"
|
||||
|
|
@ -44,15 +21,24 @@
|
|||
<modal
|
||||
ref="shopQuestionModal"
|
||||
:headerText="modalHeaderText"
|
||||
:onModalOpenedCallback="onModalOpened"
|
||||
:onModalClosedCallback="onModalClosed"
|
||||
:footerButtonText="modalFooterText"
|
||||
@footer-button-event="onSaveShop">
|
||||
<serviceZipQuestion
|
||||
ref="serviceZipQuestion"
|
||||
customInputId="serviceZipCode"
|
||||
v-model="internalModel.zipCode"
|
||||
v-on="{ 'textboxQuestionEvent.inputIdAssigned': onInputIdAssigned }"
|
||||
cmsWidgetName="ServiceZipQuestionWidget"
|
||||
:isRequired="false" />
|
||||
@footer-button-event="SetZipCodeAndShop">
|
||||
<div class="zip-search-group">
|
||||
<div class="zip-search-input">
|
||||
<serviceZipQuestion
|
||||
ref="serviceZipQuestion"
|
||||
customInputId="serviceZipCode"
|
||||
v-model="internalModel.zipCode"
|
||||
v-on="{ 'textboxQuestionEvent.inputIdAssigned': onInputIdAssigned }"
|
||||
cmsWidgetName="ServiceZipQuestionWidget"
|
||||
@search-icon-click="onSearchZip"
|
||||
@keydown.enter="onSearchZip"
|
||||
includeSearchIcon
|
||||
:isRequired="false" />
|
||||
</div>
|
||||
</div>
|
||||
<alert
|
||||
ref="alertMilitaryBaseZip"
|
||||
class="my-5"
|
||||
|
|
@ -106,39 +92,30 @@
|
|||
v-model="selectedProviderNumber"
|
||||
isRequired
|
||||
validationRules="option-required" />
|
||||
<input
|
||||
type="hidden"
|
||||
name="selectedProviderNumber"
|
||||
:value="selectedProviderNumber"
|
||||
v-validate="'required'" />
|
||||
<textLink
|
||||
v-if="displaySeeMoreLocationsLink"
|
||||
ref="showMoreShopsLink"
|
||||
class="show-more-shops-link"
|
||||
id="showMoreShopsId2"
|
||||
cmsWidgetName="ShowMoreShopsLinkWidget"
|
||||
linkType="text"
|
||||
:text="showMoreShopsLinkText"
|
||||
href="#!"
|
||||
@click-event="getNextShopsFromList(3)"
|
||||
:aria-label="showMoreShopsLinkText" />
|
||||
<div class="show-more-shops-link">
|
||||
<textLink
|
||||
v-if="displaySeeMoreLocationsLink"
|
||||
ref="showMoreShopsLink"
|
||||
id="showMoreShopsId2"
|
||||
cmsWidgetName="ShowMoreShopsLinkWidget"
|
||||
linkType="text"
|
||||
:text="showMoreShopsLinkText"
|
||||
href="#!"
|
||||
@click-event="getNextShopsFromList(3)"
|
||||
:aria-label="showMoreShopsLinkText" />
|
||||
</div>
|
||||
</modal>
|
||||
</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 shopQuestion from "@/layouts/service-location/shop-question/shop-question.vue";
|
||||
import modal from "@/digital-components/modal/modal";
|
||||
import textLink from "@/ux-components/text-link/text-link.vue";
|
||||
import serviceZipQuestion from "@/layouts/service-location/service-zip-modal-question/service-zip-question/service-zip-question.vue";
|
||||
import buttonQuestion from "@/digital-components/button-question/button-question.vue";
|
||||
import alert from "@/ux-components/alert/alert.vue";
|
||||
import shopListButton from "./shop-list-button/shop-list-button";
|
||||
//import input from "@/ux-components/input/input.vue";
|
||||
import loadingModal from "@/fmg-components/loading-modal/loading-modal.vue";
|
||||
|
||||
// Supporting files
|
||||
import { defineRule } from "vee-validate";
|
||||
|
|
@ -152,10 +129,27 @@ import { getAvailabilityRating } from "@/layouts/service-location/helpers/servic
|
|||
import { Provider } from "@/layouts/service-location/classes/provider";
|
||||
import { AppointmentTypeStrings } from "@/constants/schedule-constants";
|
||||
|
||||
import {
|
||||
getPricedMobileFeePart,
|
||||
getPricedRecycleFeePart,
|
||||
getServiceabilityDetails,
|
||||
getBillToAccountNumber,
|
||||
getClosestApplicableShops,
|
||||
getShopProviderData,
|
||||
getZipCodeData,
|
||||
} from "@/layouts/service-location/helpers/service-location-helper/service-location-helper";
|
||||
|
||||
defineRule("option-required", required(errorMessages.OPTION_REQUIRED));
|
||||
|
||||
function getShopsByZip(zipCode, shops, numberToGet = 3) {
|
||||
return shops
|
||||
.filter((shop) => shop.address && shop.address.zipCode && shop.address.zipCode !== "")
|
||||
.sort((a, b) => a.distanceInMiles - b.distanceInMiles)
|
||||
.slice(0, numberToGet);
|
||||
}
|
||||
|
||||
export default {
|
||||
name: "shop-question",
|
||||
name: "shop-question-popup",
|
||||
mixins: [baseMixin],
|
||||
data() {
|
||||
return {
|
||||
|
|
@ -165,13 +159,16 @@ export default {
|
|||
shopListButton: shopListButton,
|
||||
shopIndex: 0,
|
||||
displaySeeMoreLocationsLink: false,
|
||||
serviceZipCodeTextInputId: "",
|
||||
localShopProviderData: this.shopProviderData,
|
||||
selectedProviderNumber: null,
|
||||
isLoadingShops: false,
|
||||
lastSearchedZip: "",
|
||||
lastSuccessfulZip: "",
|
||||
hasLoadedShopData: false,
|
||||
};
|
||||
},
|
||||
props: {
|
||||
// modelValue: {
|
||||
// type: Object,
|
||||
// default: () => null,
|
||||
// },
|
||||
selectedAppointmentType: String,
|
||||
shopProviderData: Object,
|
||||
modelValue: {
|
||||
|
|
@ -181,171 +178,24 @@ export default {
|
|||
zipCode: "",
|
||||
}),
|
||||
},
|
||||
// cmsWidgetName: String,
|
||||
// validationRules: String,
|
||||
isDisplayed: Boolean,
|
||||
modelWidgetName: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
isServiceableInshop: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
zipCode: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
preSelectedProviderNumber: {
|
||||
type: [String, Number],
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
// computed: {
|
||||
// questionText() {
|
||||
// return this.getCmsContent(this.cmsWidgetName, "QuestionText");
|
||||
// },
|
||||
// shopProviders() {
|
||||
// return this.shopProviderData?.shopProviders ?? [];
|
||||
// },
|
||||
// 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: {
|
||||
// async getNextShopsFromList(numberToGet = 3) {
|
||||
// const logFirstShopsDisplayed = this.shopIndex == 0;
|
||||
|
||||
// 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}`,
|
||||
// additionalButtonData: this.additionalButtonData,
|
||||
// value: shopProvider.providerNumber,
|
||||
// };
|
||||
// });
|
||||
|
||||
// var gaAction = this.GaActions.MORE_LOCATIONS_CLICKED;
|
||||
// if (logFirstShopsDisplayed) {
|
||||
// gaAction = this.GaActions.SHOPS_FIRST_DISPLAYED;
|
||||
// }
|
||||
// if (
|
||||
// this.selectedAppointmentType === AppointmentTypeStrings.IN_SHOP ||
|
||||
// this.selectedAppointmentType === AppointmentTypeStrings.DROP_OFF
|
||||
// ) {
|
||||
// var shops = mappedData.map((shop) => {
|
||||
// if (shop.value.length > 5 && shop.value.startsWith("00")) {
|
||||
// return shop.value.substring(1);
|
||||
// } else {
|
||||
// return shop.value;
|
||||
// }
|
||||
// });
|
||||
|
||||
// var joinedShops = shops.join(",");
|
||||
// if (!joinedShops) {
|
||||
// joinedShops = "no-shops";
|
||||
// }
|
||||
|
||||
// this.pushEventToGA(this.GaCategories.SERVICE_LOCATION, gaAction, joinedShops, true);
|
||||
// }
|
||||
|
||||
// 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;
|
||||
// },
|
||||
// getSelectedProviderObject(providerNumber) {
|
||||
// const provider =
|
||||
// this.shopProviders?.find((provider) => provider.providerNumber == providerNumber) ??
|
||||
// new Provider();
|
||||
|
||||
// return provider;
|
||||
// },
|
||||
// getSelectedProviderIndex(providers, selectedProviderNumber) {
|
||||
// const index = providers.findIndex(
|
||||
// (provider) => provider.providerNumber == selectedProviderNumber
|
||||
// );
|
||||
|
||||
// return index;
|
||||
// },
|
||||
// async handleUpdate(selectedShopIndex = null) {
|
||||
// this.resetAnswers();
|
||||
// if (selectedShopIndex >= 3) {
|
||||
// await this.getNextShopsFromList(selectedShopIndex + 1);
|
||||
// } else {
|
||||
// await this.getNextShopsFromList();
|
||||
// await nextTick();
|
||||
// }
|
||||
// },
|
||||
// },
|
||||
computed: {
|
||||
shopQuestionLinkText() {
|
||||
return this.getCmsContent("ChangeMyLocationLinkWidget", "Text");
|
||||
|
|
@ -356,36 +206,45 @@ export default {
|
|||
modalHeaderText() {
|
||||
return this.getCmsContent("ShopQuestionWidget", "QuestionText");
|
||||
},
|
||||
selectShopText() {
|
||||
return this.getCmsContent("YourSafeliteShopWidget", "QuestionText");
|
||||
},
|
||||
modal() {
|
||||
return this.$refs[this.modalWidgetName];
|
||||
return this.$refs.shopQuestionModal;
|
||||
},
|
||||
shopProviders() {
|
||||
console.log("shopProviders", this.shopProviderData?.shopProviders);
|
||||
return this.shopProviderData?.shopProviders ?? [];
|
||||
return this.localShopProviderData?.shopProviders ?? [];
|
||||
},
|
||||
modalFooterText() {
|
||||
return "Save store location";
|
||||
return this.getCmsContent("SaveLocationWidget", "Text");
|
||||
},
|
||||
selectedValue: {
|
||||
get: function () {
|
||||
return this.modelValue;
|
||||
},
|
||||
set: function (newValue) {
|
||||
this.$emit("update:modelValue", newValue);
|
||||
},
|
||||
set: function (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);
|
||||
},
|
||||
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,
|
||||
};
|
||||
},
|
||||
showMoreShopsLinkText() {
|
||||
return this.getCmsContent("ShowMoreShopsLinkWidget", "Text");
|
||||
},
|
||||
displayNoShopsAlert() {
|
||||
return this.hasLoadedShopData && this.answers.length === 0;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
openModal(event) {
|
||||
|
|
@ -477,8 +336,6 @@ export default {
|
|||
});
|
||||
}
|
||||
|
||||
console.log("this.answers" + this.answers);
|
||||
|
||||
await nextTick();
|
||||
|
||||
if (this.shopIndex == this.shopProviders.length) {
|
||||
|
|
@ -510,60 +367,235 @@ export default {
|
|||
this.answers = [];
|
||||
this.shopIndex = 0;
|
||||
},
|
||||
onSaveShop() {
|
||||
// Emit the selected provider number or object to the parent
|
||||
this.$emit("shop-selected", this.selectedProviderNumber);
|
||||
// Optionally close the modal here if not handled by the modal itself
|
||||
this.$refs.shopQuestionModal.closeModal?.();
|
||||
onModalOpened() {
|
||||
this.shopIndex = 3;
|
||||
this.internalModel = this.copyModel(this.modelValue);
|
||||
this.internalModel.zipCode = this.zipCode; // Always use the parent's current zip
|
||||
this.localShopProviderData = this.shopProviderData; // Always use the parent's current shop data
|
||||
this.answers = [];
|
||||
this.updateShopsForZip(this.internalModel.zipCode);
|
||||
this.focusOnZipInput();
|
||||
this.selectedProviderNumber = this.preSelectedProviderNumber;
|
||||
if (this.internalModel.zipCode && this.shopProviders.length > 0) {
|
||||
this.updateShopsForZip(this.internalModel.zipCode); // or directly call updateShopsForZip(this.internalModel.zipCode)
|
||||
} else {
|
||||
this.internalModel.zipCode = this.zipCode;
|
||||
this.updateShopsForZip(this.internalModel.zipCode);
|
||||
}
|
||||
if (
|
||||
(!this.selectedProviderNumber ||
|
||||
!this.shopProviders.some(
|
||||
(p) => p.providerNumber == this.selectedProviderNumber
|
||||
)) &&
|
||||
this.shopProviders.length > 0
|
||||
) {
|
||||
this.selectedProviderNumber = this.shopProviders[0].providerNumber;
|
||||
}
|
||||
},
|
||||
closeModal() {
|
||||
this.modal.closeModal();
|
||||
},
|
||||
onModalClosed() {
|
||||
this.internalModel = this.copyModel(this.modelValue);
|
||||
this.selectedProviderNumber = this.preSelectedProviderNumber;
|
||||
this.resetsOnZipInput();
|
||||
let selectedProvider = this.shopProviders.find(
|
||||
(provider) => provider.providerNumber == this.selectedProviderNumber
|
||||
);
|
||||
if (!selectedProvider && this.shopProviders.length > 0) {
|
||||
selectedProvider = this.shopProviders[0];
|
||||
}
|
||||
// if (!this.displayNoShopsAlert) {
|
||||
// this.$emit("shop-selected", selectedProvider);
|
||||
// }
|
||||
},
|
||||
resetsOnZipInput() {
|
||||
this.resetAlerts();
|
||||
},
|
||||
async onSearchZip(event) {
|
||||
if (event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
const zip = this.internalModel.zipCode;
|
||||
if (this.isVehicleHeavyTruck) {
|
||||
// check the location endpoint to verify this zip can service a heavy truck
|
||||
const closestShops = await getClosestApplicableShops(
|
||||
this.internalModel.zipCode,
|
||||
this.carId,
|
||||
"service-location"
|
||||
);
|
||||
|
||||
if (!closestShops || closestShops.providers?.length === 0) {
|
||||
this.displayNoServiceAlert = true;
|
||||
this.focusOnZipInput();
|
||||
this.resetModalButtonStyle();
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
getShopProviderData(zip).then(async (result) => {
|
||||
this.localShopProviderData = result.data;
|
||||
this.updateShopsForZip(this.internalModel.zipCode);
|
||||
if (this.selectedAppointmentType === "Mobile") {
|
||||
this.selectedProvider = new Provider(
|
||||
this.shopProviderData.mobileProviderNumber
|
||||
);
|
||||
} else {
|
||||
this.selectedProvider = new Provider();
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
async updateShopsForZip(zipCode) {
|
||||
this.lastSearchedZip = zipCode;
|
||||
if (!zipCode) {
|
||||
this.isLoadingShops = false;
|
||||
this.answers = [];
|
||||
return;
|
||||
}
|
||||
this.isLoadingShops = true;
|
||||
if (!zipCode || !this.shopProviders.length) {
|
||||
// fallback to default behavior
|
||||
this.handleUpdate();
|
||||
return;
|
||||
}
|
||||
// You need a helper to sort/filter shops by distance to zipCode
|
||||
// Example: const sortedShops = getShopsByZip(zipCode, this.shopProviders);
|
||||
// For now, let's assume you have such a helper:
|
||||
// const sortedShops = getShopsByZip(zipCode, this.shopProviders); // implement or import this
|
||||
|
||||
// Map sortedShops to the format expected by buttonQuestion
|
||||
//const mappedData = sortedShops.map(shopProvider => {
|
||||
// ...mapping logic as in getNextShopsFromList
|
||||
// (copy from your getNextShopsFromList method)
|
||||
// });
|
||||
// this.answers = mappedData;
|
||||
}
|
||||
const sortedShops = getShopsByZip(zipCode, this.shopProviders, 3);
|
||||
|
||||
const toTitleCase = (str) => {
|
||||
return str.replace(/\w\S*/g, function (txt) {
|
||||
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
|
||||
});
|
||||
};
|
||||
|
||||
const mappedData = sortedShops.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}`,
|
||||
additionalButtonData: this.additionalButtonData,
|
||||
value: shopProvider.providerNumber,
|
||||
};
|
||||
});
|
||||
this.answers = mappedData;
|
||||
this.shopIndex = mappedData.length;
|
||||
this.displaySeeMoreLocationsLink = this.shopIndex < this.shopProviders.length;
|
||||
if (mappedData.length > 0) {
|
||||
this.lastSuccessfulZip = zipCode;
|
||||
}
|
||||
this.isLoadingShops = false;
|
||||
},
|
||||
onInputIdAssigned(inputId) {
|
||||
this.serviceZipCodeTextInputId = inputId;
|
||||
},
|
||||
focusOnZipInput() {
|
||||
const input = document.getElementById(this.serviceZipCodeTextInputId);
|
||||
input?.focus();
|
||||
},
|
||||
resetAlerts() {
|
||||
this.displayInvalidZipAlert = false;
|
||||
this.displayNoServiceAlert = false;
|
||||
},
|
||||
async SetZipCodeAndShop() {
|
||||
const selectedProvider = this.shopProviders.find(
|
||||
(provider) => provider.providerNumber == this.selectedProviderNumber
|
||||
);
|
||||
if (selectedProvider.address.zipCode !== this.internalModel.zipCode) {
|
||||
this.resetAlerts();
|
||||
|
||||
const zipCodeData = await this.getZipCodeData(
|
||||
selectedProvider.address.zipCode,
|
||||
"service-location"
|
||||
);
|
||||
|
||||
if (!zipCodeData.isValid) {
|
||||
this.displayInvalidZipAlert = true;
|
||||
this.focusOnZipInput();
|
||||
this.resetModalButtonStyle();
|
||||
} else {
|
||||
if (this.isVehicleHeavyTruck) {
|
||||
// check the location endpoint to verify this zip can service a heavy truck
|
||||
const closestShops = await getClosestApplicableShops(
|
||||
selectedProvider.address.zipCode,
|
||||
this.carId,
|
||||
"service-location"
|
||||
);
|
||||
|
||||
if (!closestShops || closestShops.providers?.length === 0) {
|
||||
this.displayNoServiceAlert = true;
|
||||
this.focusOnZipInput();
|
||||
this.resetModalButtonStyle();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
selectedProvider.address.state = zipCodeData.state;
|
||||
selectedProvider.address.zipCodeCtu = zipCodeData.zipCodeCtu;
|
||||
|
||||
// retrieve mobile fee part
|
||||
const serviceZipCode = selectedProvider.address.zipCode;
|
||||
const mobileFeePart = await getPricedMobileFeePart(
|
||||
serviceZipCode,
|
||||
"service-location"
|
||||
);
|
||||
|
||||
// retrieve recycle fee part
|
||||
const recycleFeePart = await getPricedRecycleFeePart(
|
||||
serviceZipCode,
|
||||
"service-location"
|
||||
);
|
||||
|
||||
// retrieve serviceability details
|
||||
const serviceabilityDetails = await getServiceabilityDetails(
|
||||
serviceZipCode,
|
||||
null,
|
||||
"service-location"
|
||||
);
|
||||
|
||||
const billToAccountNumber = await getBillToAccountNumber(
|
||||
selectedProvider.address.zipCodeCtu
|
||||
);
|
||||
|
||||
// update content related to service zip code
|
||||
this.$emit("updated-mobile-fee-part", mobileFeePart);
|
||||
this.$emit("updated-recycle-fee-part", recycleFeePart);
|
||||
this.$emit("updated-serviceability", serviceabilityDetails.data);
|
||||
this.$emit("updated-contains-military-base", zipCodeData.containsMilitaryBase);
|
||||
this.$emit("updated-bill-to-account-number", billToAccountNumber);
|
||||
this.$emit("updated-zipcode-ctu", selectedProvider.address.zipCodeCtu);
|
||||
|
||||
this.$emit("update:modelValue", this.selectedValue);
|
||||
this.$emit("update-shop-provider-data", this.localShopProviderData);
|
||||
this.$emit("shop-selected", selectedProvider);
|
||||
|
||||
this.closeModal();
|
||||
}
|
||||
} else {
|
||||
this.closeModal();
|
||||
}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
selectedAppointmentType: {
|
||||
// Question: Does it matter if this fires when clicking Mobile? Ideally it should not, but it doesn't seem to effect anything
|
||||
// What we DON'T want to do here is create a dependency by checking if the selectedAppointmentType is Mobile as that has
|
||||
// nothing to do with this component. We could mitigate this by showing / hiding this component with v-if but that messes
|
||||
// up the component initialization on page load.
|
||||
async handler() {
|
||||
this.handleUpdate();
|
||||
shopProviderData: {
|
||||
immediate: true,
|
||||
handler(newVal) {
|
||||
this.localShopProviderData = newVal;
|
||||
this.hasLoadedShopData = true;
|
||||
if (this.internalModel && this.internalModel.zipCode) {
|
||||
this.updateShopsForZip(this.internalModel.zipCode);
|
||||
}
|
||||
},
|
||||
},
|
||||
shopProviders: {
|
||||
async handler(newValue) {
|
||||
const selectedShopIndex = this.getSelectedProviderIndex(
|
||||
newValue,
|
||||
this.selectedProviderNumber
|
||||
);
|
||||
this.handleUpdate(selectedShopIndex);
|
||||
},
|
||||
},
|
||||
|
||||
'internalModel.zipCode': {
|
||||
handler(newZip) {
|
||||
this.updateShopsForZip(newZip);
|
||||
},
|
||||
immediate: false
|
||||
preSelectedProviderNumber(newVal) {
|
||||
this.selectedProviderNumber = newVal;
|
||||
},
|
||||
},
|
||||
components: {
|
||||
//shopQuestion,
|
||||
modal,
|
||||
textLink,
|
||||
serviceZipQuestion,
|
||||
|
|
@ -612,4 +644,66 @@ export default {
|
|||
.modal-link {
|
||||
text-align: center;
|
||||
}
|
||||
.modal.modal-component .modal-dialog .modal-content .modal-body .textbox-question {
|
||||
padding: 0;
|
||||
}
|
||||
.zip-search-group {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: stretch;
|
||||
width: 100%;
|
||||
height: 48px;
|
||||
border-radius: 48px;
|
||||
padding-left: 16px;
|
||||
border: 1px solid #ccc;
|
||||
background: #fff;
|
||||
margin-bottom: 20px;
|
||||
@include media-breakpoint-up(md) {
|
||||
height: auto;
|
||||
padding-left: 0;
|
||||
}
|
||||
.zip-search-input {
|
||||
align-items: stretch;
|
||||
flex: 1 1 0;
|
||||
height: 100%;
|
||||
margin-bottom: 0;
|
||||
input {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: none;
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
outline: none;
|
||||
font-size: 1rem;
|
||||
background: transparent;
|
||||
box-sizing: border-box;
|
||||
border-radius: 48px 0 0 48px;
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
.btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background-color: #e5f1fa;
|
||||
justify-content: center;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
|
||||
border: none;
|
||||
|
||||
border-radius: 0 48px 48px 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
@media (max-width: 767.98px) {
|
||||
.zip-search-group {
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
.show-more-shops-link a {
|
||||
text-align: center;
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ export default {
|
|||
},
|
||||
},
|
||||
methods: {
|
||||
async getNextShopsFromList(numberToGet = 1) {
|
||||
async getNextShopsFromList(numberToGet = 3) {
|
||||
const logFirstShopsDisplayed = this.shopIndex == 0;
|
||||
|
||||
const shopIterator = (array, n) => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue