418 lines
17 KiB
Vue
418 lines
17 KiB
Vue
<template>
|
|
<div class="page-container-grouped-styles">
|
|
<div class="fade-on-route-transition">
|
|
<siteHeader
|
|
ref="siteHeader"
|
|
:cmsWidgetName="widget.siteHeader" />
|
|
<Form
|
|
id="searchProvidersForm"
|
|
@submit="onSubmit"
|
|
@invalidSubmit="onInvalidSubmit">
|
|
<div class="container-fluid pb-2">
|
|
<div class="mx-5">
|
|
<label
|
|
id="tpaSearchQuestionLabel"
|
|
for="tpaSearchQuestionField"
|
|
class="text-center fs-5 mt-5 mb-0 text-black w-100">
|
|
{{ tpaSearchQuestionLabel }}
|
|
</label>
|
|
<label
|
|
id="searchInstructions"
|
|
for="tpaSearchQuestionField"
|
|
class="text-center small darker-gray w-100 mb-4">
|
|
{{ searchInstructionsText }}
|
|
</label>
|
|
<div class="mb-5">
|
|
<textboxQuestion
|
|
id="tpaSearchQuestionField"
|
|
v-model="zipCode"
|
|
inputId="tpaSearchQuestionFieldInput"
|
|
:cmsWidgetName="widget.tpaSearchQuestion"
|
|
:includeSearchIcon="true"
|
|
:displayQuestionText="false"
|
|
isRequired
|
|
:isDisabled="reloadingProviders"
|
|
:validationRules="rules.zipCode"
|
|
@clickEvent="searchClick" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</Form>
|
|
<googleMap
|
|
id="map"
|
|
class="mb-4"
|
|
:addresses="providerAddresses"
|
|
:zipCode="mapZipCode" />
|
|
<Form
|
|
id="providerSelectionForm"
|
|
v-slot="{ meta }"
|
|
@submit="onSubmit"
|
|
@invalidSubmit="onInvalidSubmit">
|
|
<div class="container-fluid pb-2">
|
|
<div class="mx-5">
|
|
<dropdownQuestion
|
|
id="searchRadiusFilter"
|
|
v-model="filter"
|
|
:cmsWidgetName="widget.filterByQuestion"
|
|
inputId="searchRadiusFilterInput"
|
|
:options="filterOptions"
|
|
disableAutoFill
|
|
:isDisabled="reloadingProviders"
|
|
:validationRules="rules.filter" />
|
|
<loader
|
|
v-if="reloadingProviders"
|
|
id="providersLoader"
|
|
loaderPosition="center"
|
|
loaderColor="blue"
|
|
:width="2"
|
|
:height="2"
|
|
class="my-4" />
|
|
<div v-else>
|
|
<div class="my-4">
|
|
<buttonQuestion
|
|
id="selectProviderQuestion"
|
|
v-model="selectedProviderNumber"
|
|
buttonTypeString="shopListButton"
|
|
:buttonTypeObject="shopListButton"
|
|
class="radioQuestion"
|
|
:answers="providerButtonData"
|
|
groupName="chooseShop"
|
|
textPosition="text-start"
|
|
isRequired
|
|
:validationRules="rules.provider"
|
|
:additionalButtonData="additionalButtonData" />
|
|
<alert
|
|
v-if="providers?.length === 0 ?? true"
|
|
id="alertNoNetworkProviders"
|
|
:cmsWidgetName="widget.noNetworkShopsAlert"
|
|
alertClass="alert-warning"
|
|
:isDismissible="false"
|
|
:manualHeadline="noNetworkShopsAlertHeaderText" />
|
|
</div>
|
|
<div class="text-center">
|
|
<textLink
|
|
id="preferredShopNotListedLink"
|
|
linkType="navigation"
|
|
href="javascript:void(0)"
|
|
:text="shopNotListedModalLink"
|
|
@clickEvent="doNotSeeMyShopLinkClick" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<siteFooter
|
|
ref="siteFooter"
|
|
:cmsWidgetName="widget.siteFooter"
|
|
:isForwardActionDisabled="!meta.valid"
|
|
@ForwardClicked="forwardButtonAction"
|
|
@backClicked="navigateBack" />
|
|
</div>
|
|
</Form>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<script>
|
|
// Components
|
|
import { Form } from 'vee-validate';
|
|
import siteHeader from '@/iss-components/site-header/site-header.vue';
|
|
import textboxQuestion from '@/digital-components/textbox-question/textbox-question.vue';
|
|
import dropdownQuestion from '@/digital-components/dropdown-question/dropdown-question.vue';
|
|
import buttonQuestion from '@/digital-components/button-question/button-question.vue';
|
|
import textLink from '@/ux-components/text-link/text-link.vue';
|
|
import alert from '@/ux-components/alert/alert.vue';
|
|
import siteFooter from '@/iss-components/site-footer/site-footer.vue';
|
|
import googleMap from '@/iss-components/google-map/google-map.vue';
|
|
import shopListButton from '@/iss-components/shop-list-button/shop-list-button.vue';
|
|
import loader from '@/ux-components/loader/loader.vue';
|
|
|
|
// Supporting files
|
|
import { fetchCmsContentForPage } from '@/helpers/cms-content-helper';
|
|
import { useMainStore } from '@/store';
|
|
import BaseFormMixin from '@/mixins/base-form-mixin.js';
|
|
import globalRules from '@/constants/global-rules.js';
|
|
import widgetFields from '@/constants/cms-widget-fields.js';
|
|
import { shallowRef } from 'vue';
|
|
import { toTitleCase, toDisplayPhoneNumber } from '@/helpers/text-helper.js';
|
|
import bailoutMessage from '@/constants/bailoutMessage';
|
|
|
|
const radiusFilterPairs = [
|
|
{ radius: 15, filter: '15 miles' },
|
|
{ radius: 25, filter: '25 miles' },
|
|
{ radius: 50, filter: '50 miles' },
|
|
{ radius: 100, filter: '100 miles' }
|
|
];
|
|
|
|
function convertRadiusFilterToInteger(filter) {
|
|
const pair = radiusFilterPairs.find((p) => p.filter === filter);
|
|
return pair?.radius ?? 0;
|
|
}
|
|
|
|
async function getInitialSearchData() {
|
|
const customerZipCode = useMainStore().order.customer.address.zipCode;
|
|
const serviceLocationZipCode = useMainStore().order.serviceLocation.zipCode;
|
|
const zipCode = serviceLocationZipCode ?? customerZipCode;
|
|
|
|
const { searchFilter } = useMainStore().order.serviceLocation;
|
|
let { providerNumber } = useMainStore().order.serviceLocation.provider;
|
|
|
|
let providers = [];
|
|
let radius = 0;
|
|
if (searchFilter) {
|
|
radius = convertRadiusFilterToInteger(searchFilter);
|
|
const result = await useMainStore().getTpaProviders(zipCode, radius);
|
|
providers = result?.data?.shopProviders ?? [];
|
|
|
|
return { zipCode, filter: searchFilter, providers, providerNumber };
|
|
}
|
|
|
|
providerNumber = null;
|
|
let pairIndex = 0;
|
|
let filter = '';
|
|
while (providers.length === 0 && pairIndex < radiusFilterPairs.length) {
|
|
const pair = radiusFilterPairs[pairIndex];
|
|
radius = pair.radius;
|
|
filter = pair.filter;
|
|
// eslint-disable-next-line no-await-in-loop
|
|
const result = await useMainStore().getTpaProviders(zipCode, radius);
|
|
providers = result?.data?.shopProviders ?? [];
|
|
|
|
pairIndex += 1;
|
|
}
|
|
|
|
return { zipCode, filter, providers, providerNumber };
|
|
}
|
|
|
|
export default {
|
|
name: 'tpa-search',
|
|
components: {
|
|
siteHeader,
|
|
textboxQuestion,
|
|
dropdownQuestion,
|
|
buttonQuestion,
|
|
textLink,
|
|
alert,
|
|
loader,
|
|
googleMap,
|
|
siteFooter,
|
|
// eslint-disable-next-line vue/no-reserved-component-names
|
|
Form
|
|
},
|
|
mixins: [BaseFormMixin],
|
|
async beforeRouteEnter(to, from, next) {
|
|
const { zipCode, filter, providers, providerNumber } = await getInitialSearchData();
|
|
const cmsContent = await fetchCmsContentForPage(to.query.issPage);
|
|
|
|
next(async (vm) => {
|
|
vm.setCmsContent(cmsContent);
|
|
vm.setInitialSearchData(zipCode, filter, providers, providerNumber);
|
|
});
|
|
},
|
|
data() {
|
|
return {
|
|
dataLoaded: false,
|
|
zipCode: null,
|
|
mapZipCode: null,
|
|
filter: '',
|
|
providers: [],
|
|
selectedProviderNumber: '',
|
|
reloadingProviders: false,
|
|
additionalButtonData: {
|
|
displayAvailabilityIndicators: false
|
|
},
|
|
widget: {
|
|
siteHeader: 'SiteHeaderWidget',
|
|
tpaSearchQuestion: 'TPASearchQuestion',
|
|
searchInstructions: 'SearchInstructions',
|
|
filterByQuestion: 'FilterByQuestion',
|
|
noNetworkShopsAlert: 'NoNetworkShopsAlertWidget',
|
|
shopNotListedLink: 'ShopNotListedLink',
|
|
siteFooter: 'SiteFooterWidget'
|
|
},
|
|
rules: {
|
|
zipCode: `${globalRules.ZIP_CODE_REQUIRED}|${globalRules.ZIP_CODE_SEARCH_FORMAT}`,
|
|
filter: globalRules.OPTION_REQUIRED, // TODO do we even need this?
|
|
provider: globalRules.OPTION_REQUIRED
|
|
},
|
|
shopListButton: shallowRef(shopListButton)
|
|
};
|
|
},
|
|
computed: {
|
|
filterOptions() {
|
|
const filterByAnswers = this.getCmsContent(
|
|
this.widget.filterByQuestion,
|
|
widgetFields.INPUT_QUESTION_WIDGET.ANSWERS
|
|
) ?? [];
|
|
const filterByAnswersObj = {};
|
|
[...filterByAnswers].forEach((answer) => {
|
|
filterByAnswersObj[answer.Name] = answer.Name;
|
|
});
|
|
return filterByAnswersObj;
|
|
},
|
|
tpaSearchQuestionLabel() {
|
|
return this.getCmsContent(
|
|
this.widget.tpaSearchQuestion,
|
|
widgetFields.INPUT_QUESTION_WIDGET.QUESTION_TEXT
|
|
);
|
|
},
|
|
searchInstructionsText() {
|
|
return this.getCmsContent(
|
|
this.widget.searchInstructions,
|
|
widgetFields.TEXT_BLOCK_WIDGET.TEXT
|
|
);
|
|
},
|
|
shopNotListedModalLink() {
|
|
return this.getCmsContent(
|
|
this.widget.shopNotListedLink,
|
|
widgetFields.TEXT_BLOCK_WIDGET.TEXT
|
|
);
|
|
},
|
|
radiusInMiles() {
|
|
return convertRadiusFilterToInteger(this.filter);
|
|
},
|
|
noNetworkShopsAlertHeaderText() {
|
|
return this.getCmsContent(
|
|
this.widget.noNetworkShopsAlert,
|
|
widgetFields.ALERT_WIDGET.HEADLINE_TEXT
|
|
)?.replaceAll('{custom:radiusInMiles}', this.radiusInMiles);
|
|
},
|
|
providerAddresses() {
|
|
return this.providers?.map((provider) => this.getProviderAddress(provider)) ?? [];
|
|
},
|
|
providerButtonData() {
|
|
return this.providers?.map((provider) => this.getShopButtonDataFromProvider(provider)) ?? [];
|
|
},
|
|
selectedProviderIsSafeliteShop() {
|
|
return this.providers?.find((p) => p.providerNumber === this.selectedProviderNumber)?.isSafeliteShop ?? false;
|
|
}
|
|
},
|
|
watch: {
|
|
async filter() {
|
|
if (this.dataLoaded) {
|
|
await this.getProviderButtonData().then((providers) => {
|
|
this.providers = providers;
|
|
});
|
|
this.mapZipCode = this.zipCode;
|
|
}
|
|
},
|
|
providers(newProviders) {
|
|
if (this.dataLoaded) {
|
|
this.selectedProviderNumber = newProviders?.length === 1 ?? false
|
|
? newProviders[0]?.providerNumber ?? ''
|
|
: '';
|
|
}
|
|
},
|
|
selectedProviderNumber(newNumber) {
|
|
const provider = this.providers?.find((p) => p.providerNumber === newNumber);
|
|
if (provider && this.dataLoaded) {
|
|
useMainStore().updateServiceLocation({
|
|
searchFilter: this.filter,
|
|
zipCode: this.zipCode,
|
|
provider: {
|
|
providerNumber: provider?.providerNumber,
|
|
address: {
|
|
streetAddress: provider.address?.streetAddress,
|
|
city: provider.address?.city,
|
|
state: provider.address?.state,
|
|
zipCode: provider.address?.zipCode,
|
|
zipCodeCtu: provider.address?.zipCodeCtu
|
|
},
|
|
companyName: provider?.companyName,
|
|
phoneNumber: provider?.phoneNumber
|
|
}
|
|
});
|
|
}
|
|
}
|
|
},
|
|
beforeUpdate() {
|
|
if (!this.dataLoaded) {
|
|
this.dataLoaded = true;
|
|
}
|
|
},
|
|
methods: {
|
|
setInitialSearchData(zipCode, filter, providers, providerNumber) {
|
|
this.zipCode = zipCode;
|
|
this.mapZipCode = zipCode;
|
|
this.filter = filter;
|
|
this.providers = providers;
|
|
this.selectedProviderNumber = providerNumber;
|
|
},
|
|
getProviderAddress(provider) {
|
|
const city = toTitleCase(provider?.address?.city);
|
|
const state = provider?.address?.state ?? '';
|
|
const zipCode = provider?.address?.zipCode ?? '';
|
|
let addressLine2 = '';
|
|
if (city) {
|
|
addressLine2 += city;
|
|
if (state || zipCode) {
|
|
addressLine2 += state ? ', ' : ' ';
|
|
}
|
|
}
|
|
if (state) {
|
|
addressLine2 += state;
|
|
addressLine2 += zipCode ? ' ' : '';
|
|
}
|
|
if (zipCode) {
|
|
addressLine2 += zipCode;
|
|
}
|
|
|
|
const addressLine1 = toTitleCase(provider?.address?.streetAddress);
|
|
const joinString = addressLine1.length > 0 && addressLine2.length > 0 ? ', ' : '';
|
|
return [addressLine1, addressLine2].join(joinString);
|
|
},
|
|
async getProviderButtonData() {
|
|
this.reloadingProviders = true;
|
|
const getTpaProvidersResult = await useMainStore().getTpaProviders(this.zipCode, this.radiusInMiles);
|
|
this.reloadingProviders = false;
|
|
return getTpaProvidersResult?.data?.shopProviders ?? [];
|
|
},
|
|
doNotSeeMyShopLinkClick() {
|
|
this.mainStore.setBailout(this.$router.currentRoute, bailoutMessage.doNotSeeMyShop());
|
|
this.$router.navigate(
|
|
this.navigationScenarios.CLICKED_DO_NOT_SEE_MY_SHOP_LINK,
|
|
this.$route
|
|
);
|
|
},
|
|
async searchClick() {
|
|
this.providers = await this.getProviderButtonData();
|
|
this.mapZipCode = this.zipCode;
|
|
},
|
|
forwardButtonAction() {
|
|
if (this.selectedProviderIsSafeliteShop) {
|
|
useMainStore().updateIsSafeliteProvider(true);
|
|
}
|
|
const scenario = this.selectedProviderIsSafeliteShop
|
|
? this.navigationScenarios.CLICKED_FORWARD_WITH_SAFELITE_SHOP
|
|
: this.navigationScenarios.CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP;
|
|
this.$router.navigate(scenario, this.$route);
|
|
},
|
|
getCustomValueFromString(str) {
|
|
switch (str) {
|
|
case 'radiusInMiles':
|
|
return this.radiusInMiles;
|
|
default:
|
|
return null;
|
|
}
|
|
},
|
|
getShopButtonDataFromProvider(provider) {
|
|
const cellNumber = toDisplayPhoneNumber(provider?.phoneNumber);
|
|
const distance = provider?.distanceInMiles !== null && !Number.isNaN(parseFloat(provider?.distanceInMiles))
|
|
? +provider.distanceInMiles.toFixed(1)
|
|
: null;
|
|
|
|
return {
|
|
buttonLabel: provider?.companyName ?? '',
|
|
buttonLabelSubCopy: distance === null
|
|
? ''
|
|
: `${distance} mi`,
|
|
buttonBodyCopy: `${this.getProviderAddress(provider)}<br>${cellNumber ?? ''}`,
|
|
value: provider?.providerNumber ?? ''
|
|
};
|
|
}
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.darker-gray {
|
|
color: map-get($colors, "darker-gray");
|
|
}
|
|
</style>
|