Finished incorporating map into tpa search page
This commit is contained in:
parent
c56a404704
commit
d897fbbad1
9 changed files with 224 additions and 70 deletions
|
|
@ -5,6 +5,13 @@
|
||||||
window.dataLayer = [{}];
|
window.dataLayer = [{}];
|
||||||
</script>
|
</script>
|
||||||
<script><%= process.env.VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY %></script>
|
<script><%= process.env.VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY %></script>
|
||||||
|
<!-- TODO move google maps script to environment variable -->
|
||||||
|
<script>
|
||||||
|
(g=>{var h,a,k,p="The Google Maps JavaScript API",c="google",l="importLibrary",q="__ib__",m=document,b=window;b=b[c]||(b[c]={});var d=b.maps||(b.maps={}),r=new Set,e=new URLSearchParams,u=()=>h||(h=new Promise(async(f,n)=>{await (a=m.createElement("script"));e.set("libraries",[...r]+"");for(k in g)e.set(k.replace(/[A-Z]/g,t=>"_"+t[0].toLowerCase()),g[k]);e.set("callback",c+".maps."+q);a.src=`https://maps.${c}apis.com/maps/api/js?`+e;d[q]=f;a.onerror=()=>h=n(Error(p+" could not load."));a.nonce=m.querySelector("script[nonce]")?.nonce||"";m.head.append(a)}));d[l]?console.warn(p+" only loads once. Ignoring:",g):d[l]=(f,...n)=>r.add(f)&&u().then(()=>d[l](f,...n))})({
|
||||||
|
key: "<%= process.env.VUE_APP_GOOGLE_PLACES_API_KEY %>",
|
||||||
|
v: "weekly",
|
||||||
|
});
|
||||||
|
</script>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||||
|
|
|
||||||
|
|
@ -136,7 +136,7 @@ const endpoints = Object.freeze({
|
||||||
method: 'GET'
|
method: 'GET'
|
||||||
},
|
},
|
||||||
GooglePlaces: {
|
GooglePlaces: {
|
||||||
url: 'https://maps.googleapis.com/maps/api/js?key={apiKey}&libraries=places'
|
url: (apiKey) => `https://maps.googleapis.com/maps/api/js?key=${apiKey}&libraries=places`
|
||||||
},
|
},
|
||||||
ValidateClientTag: {
|
ValidateClientTag: {
|
||||||
url: '/clientauth/api/v1/clientauth/validate-client-tag',
|
url: '/clientauth/api/v1/clientauth/validate-client-tag',
|
||||||
|
|
|
||||||
|
|
@ -196,7 +196,7 @@ export default {
|
||||||
const addressField1 = document.getElementById('autocomplete');
|
const addressField1 = document.getElementById('autocomplete');
|
||||||
const self = this;
|
const self = this;
|
||||||
|
|
||||||
const url = endpoints.GooglePlaces.url.replace('{apiKey}', applicationConfig.GOOGLE_PLACES_API_KEY);
|
const url = endpoints.GooglePlaces.url(applicationConfig.GOOGLE_PLACES_API_KEY);
|
||||||
|
|
||||||
this.$loadScript(url)
|
this.$loadScript(url)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
|
|
||||||
0
src/iss-components/google-map/google-map.spec.js
Normal file
0
src/iss-components/google-map/google-map.spec.js
Normal file
89
src/iss-components/google-map/google-map.vue
Normal file
89
src/iss-components/google-map/google-map.vue
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div id="map" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'google-map',
|
||||||
|
components: { },
|
||||||
|
props: {
|
||||||
|
addresses: Array,
|
||||||
|
zipCode: String
|
||||||
|
},
|
||||||
|
emits: [],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
country: 'USA',
|
||||||
|
geocoder: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
zipCodeAddress() {
|
||||||
|
return `${this.zipCode} ${this.country}`;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
async addresses(newAddresses) {
|
||||||
|
this.createMapWithMarkersForAddresses(newAddresses);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async getMap(center) {
|
||||||
|
const { Map } = await window.google.maps.importLibrary('maps');
|
||||||
|
return new Map(document.getElementById('map'), {
|
||||||
|
center,
|
||||||
|
mapId: 'map_id',
|
||||||
|
mapTypeControl: false
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getBounds(locations) {
|
||||||
|
const bounds = new window.google.maps.LatLngBounds();
|
||||||
|
locations.forEach((location) => bounds.extend(location));
|
||||||
|
return bounds;
|
||||||
|
},
|
||||||
|
async setGeocoder() {
|
||||||
|
if (this.geocoder == null) {
|
||||||
|
const { Geocoder } = await window.google.maps.importLibrary('geocoding');
|
||||||
|
this.geocoder = new Geocoder();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async addMarkersToMap(map, positions) {
|
||||||
|
const { AdvancedMarkerElement } = await window.google.maps.importLibrary('marker');
|
||||||
|
positions.forEach((position) => new AdvancedMarkerElement({ map, position }));
|
||||||
|
},
|
||||||
|
async getLocationsFromAddresses(addresses) {
|
||||||
|
await this.setGeocoder();
|
||||||
|
|
||||||
|
const geocodeShopAddressPromises = addresses?.map((address) => this.geocoder.geocode({ address })) ?? [];
|
||||||
|
const geocodeShopAddressResults = await Promise.all(geocodeShopAddressPromises);
|
||||||
|
return geocodeShopAddressResults.map((result) => result.results[0].geometry.location);
|
||||||
|
},
|
||||||
|
async getBoundsFromAddress(address) {
|
||||||
|
await this.setGeocoder();
|
||||||
|
const result = await this.geocoder.geocode({ address });
|
||||||
|
return result.results[0].geometry.bounds;
|
||||||
|
},
|
||||||
|
async createMapWithMarkersForAddresses(addresses) {
|
||||||
|
const shopPositions = await this.getLocationsFromAddresses(addresses);
|
||||||
|
const zipBounds = await this.getBoundsFromAddress(this.zipCodeAddress);
|
||||||
|
const map = await this.getMap(zipBounds.getCenter());
|
||||||
|
|
||||||
|
await this.addMarkersToMap(map, shopPositions);
|
||||||
|
|
||||||
|
const positionsToDisplay = shopPositions.concat(zipBounds.getNorthEast(), zipBounds.getSouthWest());
|
||||||
|
const bounds = this.getBounds(positionsToDisplay);
|
||||||
|
map.fitBounds(bounds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
|
#map {
|
||||||
|
height: 250px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
id="buttonLabelSubCopySpan"
|
id="buttonLabelSubCopySpan"
|
||||||
class="m-0 caption ms-1"
|
class="m-0 caption ms-2"
|
||||||
:class="textPosition">{{ buttonLabelSubCopy }}
|
:class="textPosition">{{ buttonLabelSubCopy }}
|
||||||
</span>
|
</span>
|
||||||
<div
|
<div
|
||||||
|
|
|
||||||
|
|
@ -512,7 +512,7 @@ describe('TPA search page', () => {
|
||||||
useMainStore().getTpaProviders = jest.fn().mockImplementationOnce(() => (newProviders));
|
useMainStore().getTpaProviders = jest.fn().mockImplementationOnce(() => (newProviders));
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
const result = await wrapper.vm.getProviders();
|
const result = await wrapper.vm.getProviderButtonData();
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(result).toEqual([]);
|
expect(result).toEqual([]);
|
||||||
|
|
@ -525,7 +525,7 @@ describe('TPA search page', () => {
|
||||||
useMainStore().getTpaProviders = jest.fn().mockImplementationOnce(() => (providers));
|
useMainStore().getTpaProviders = jest.fn().mockImplementationOnce(() => (providers));
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
const result = await wrapper.vm.getProviders();
|
const result = await wrapper.vm.getProviderButtonData();
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(result).toEqual(providers.data);
|
expect(result).toEqual(providers.data);
|
||||||
|
|
|
||||||
|
|
@ -36,18 +36,17 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Form>
|
</Form>
|
||||||
|
<googleMap
|
||||||
|
id="map"
|
||||||
|
class="mb-4"
|
||||||
|
:addresses="providerAddresses"
|
||||||
|
:zipCode="mapZipCode"></googleMap>
|
||||||
<Form
|
<Form
|
||||||
id="providerSelectionForm"
|
id="providerSelectionForm"
|
||||||
v-slot="{ meta }"
|
v-slot="{ meta }"
|
||||||
@submit="onSubmit"
|
@submit="onSubmit"
|
||||||
@invalidSubmit="onInvalidSubmit">
|
@invalidSubmit="onInvalidSubmit">
|
||||||
<div class="container-fluid pb-2">
|
<div class="container-fluid pb-2">
|
||||||
<p
|
|
||||||
id="map"
|
|
||||||
class="mb-4">
|
|
||||||
Placeholder for Map
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div class="mx-5">
|
<div class="mx-5">
|
||||||
<dropdownQuestion
|
<dropdownQuestion
|
||||||
id="searchRadiusFilter"
|
id="searchRadiusFilter"
|
||||||
|
|
@ -65,7 +64,7 @@
|
||||||
buttonTypeString="shopListButton"
|
buttonTypeString="shopListButton"
|
||||||
:buttonTypeObject="shopListButton"
|
:buttonTypeObject="shopListButton"
|
||||||
class="radioQuestion"
|
class="radioQuestion"
|
||||||
:answers="providers"
|
:answers="providerButtonData"
|
||||||
groupName="chooseShop"
|
groupName="chooseShop"
|
||||||
textPosition="text-start"
|
textPosition="text-start"
|
||||||
isRequired
|
isRequired
|
||||||
|
|
@ -105,11 +104,12 @@ import { Form } from 'vee-validate';
|
||||||
import siteHeader from '@/iss-components/site-header/site-header.vue';
|
import siteHeader from '@/iss-components/site-header/site-header.vue';
|
||||||
import textboxQuestion from '@/digital-components/textbox-question/textbox-question.vue';
|
import textboxQuestion from '@/digital-components/textbox-question/textbox-question.vue';
|
||||||
import dropdownQuestion from '@/digital-components/dropdown-question/dropdown-question.vue';
|
import dropdownQuestion from '@/digital-components/dropdown-question/dropdown-question.vue';
|
||||||
import buttonQuestion from '@/digital-components/button-question/button-question.vue'
|
import buttonQuestion from '@/digital-components/button-question/button-question.vue';
|
||||||
import shopListButton from '@/iss-components/shop-list-button/shop-list-button.vue'
|
|
||||||
import textLink from '@/ux-components/text-link/text-link.vue';
|
import textLink from '@/ux-components/text-link/text-link.vue';
|
||||||
import alert from '@/ux-components/alert/alert.vue';
|
import alert from '@/ux-components/alert/alert.vue';
|
||||||
import siteFooter from '@/iss-components/site-footer/site-footer.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';
|
||||||
|
|
||||||
// Supporting files
|
// Supporting files
|
||||||
import { fetchCmsContentForPage } from '@/helpers/cms-content-helper';
|
import { fetchCmsContentForPage } from '@/helpers/cms-content-helper';
|
||||||
|
|
@ -118,7 +118,6 @@ import { useMainStore } from '@/store';
|
||||||
import BaseFormMixin from '@/mixins/base-form-mixin.js';
|
import BaseFormMixin from '@/mixins/base-form-mixin.js';
|
||||||
import globalRules from '@/constants/global-rules.js';
|
import globalRules from '@/constants/global-rules.js';
|
||||||
import widgetFields from '@/constants/cms-widget-fields.js';
|
import widgetFields from '@/constants/cms-widget-fields.js';
|
||||||
import routerParams from '@/router/router-constants/router-params';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'tpa-search',
|
name: 'tpa-search',
|
||||||
|
|
@ -129,6 +128,7 @@ export default {
|
||||||
buttonQuestion,
|
buttonQuestion,
|
||||||
textLink,
|
textLink,
|
||||||
alert,
|
alert,
|
||||||
|
googleMap,
|
||||||
siteFooter,
|
siteFooter,
|
||||||
// eslint-disable-next-line vue/no-reserved-component-names
|
// eslint-disable-next-line vue/no-reserved-component-names
|
||||||
Form
|
Form
|
||||||
|
|
@ -144,34 +144,16 @@ export default {
|
||||||
];
|
];
|
||||||
const resultMap = await settleAllPromises(promiseResultMap);
|
const resultMap = await settleAllPromises(promiseResultMap);
|
||||||
|
|
||||||
const radiusOptions = [25, 50, 100];
|
next(async (vm) => {
|
||||||
const { zipCode } = useMainStore().order.customer.address;
|
|
||||||
|
|
||||||
const tpaProvidersRadius25 = await useMainStore().getTpaProviders(zipCode, radiusOptions[0]);
|
|
||||||
const tpaProvidersRadius50 = await useMainStore().getTpaProviders(zipCode, radiusOptions[1]);
|
|
||||||
const tpaProvidersRadius100 = await useMainStore().getTpaProviders(zipCode, radiusOptions[2]);
|
|
||||||
let radius = '25 miles';
|
|
||||||
|
|
||||||
let providers = tpaProvidersRadius25;
|
|
||||||
if ((tpaProvidersRadius25?.data ?? []).length === 0) {
|
|
||||||
radius = '50 miles';
|
|
||||||
providers = tpaProvidersRadius50;
|
|
||||||
if ((tpaProvidersRadius50?.data ?? []).length === 0) {
|
|
||||||
radius = '100 miles';
|
|
||||||
providers = tpaProvidersRadius100;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
next((vm) => {
|
|
||||||
vm.setCmsContent(resultMap.cmsContent);
|
vm.setCmsContent(resultMap.cmsContent);
|
||||||
vm.setFilter(radius);
|
await vm.setInitialFilterAndProviders();
|
||||||
vm.setProviders(providers);
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
const { zipCode } = useMainStore().order.customer.address;
|
const { zipCode } = useMainStore().order.customer.address;
|
||||||
return {
|
return {
|
||||||
zipCode,
|
zipCode,
|
||||||
|
mapZipCode: zipCode,
|
||||||
filter: '',
|
filter: '',
|
||||||
providers: [],
|
providers: [],
|
||||||
selectedProviderNumber: '',
|
selectedProviderNumber: '',
|
||||||
|
|
@ -242,24 +224,56 @@ export default {
|
||||||
this.widget.noNetworkShopsAlert,
|
this.widget.noNetworkShopsAlert,
|
||||||
widgetFields.ALERT_WIDGET.HEADLINE_TEXT
|
widgetFields.ALERT_WIDGET.HEADLINE_TEXT
|
||||||
)?.replaceAll('{custom:radiusInMiles}', this.radiusInMiles);
|
)?.replaceAll('{custom:radiusInMiles}', this.radiusInMiles);
|
||||||
|
},
|
||||||
|
providerAddresses() {
|
||||||
|
return this.providers?.map((provider) => this.getProviderAddress(provider)) ?? [];
|
||||||
|
},
|
||||||
|
providerButtonData() {
|
||||||
|
return this.providers?.map((provider) => this.getShopButtonDataFromProvider(provider)) ?? [];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
async filter() {
|
async filter() {
|
||||||
// TODO right now this results in getProviders being called once more than it needs to be
|
// TODO right now this results in getProviders being called once more than it needs to be
|
||||||
this.providers = await this.getProviders();
|
this.providers = await this.getProviderButtonData();
|
||||||
|
this.mapZipCode = this.zipCode;
|
||||||
},
|
},
|
||||||
providers(newProviders) {
|
providers(newProviders) {
|
||||||
this.selectedProviderNumber = newProviders?.length === 1 ?? false
|
this.selectedProviderNumber = newProviders?.length === 1 ?? false
|
||||||
? newProviders[0].value
|
? newProviders[0].providerNumber
|
||||||
: '';
|
: '';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods:
|
methods:
|
||||||
{
|
{
|
||||||
async getProviders() {
|
getProviderAddress(provider) {
|
||||||
|
const addressLine2 = `${provider.address.city}, ${provider.address.state} ${provider.address.zipCode}`;
|
||||||
|
return `${provider.address.streetAddress}, ${addressLine2}`;
|
||||||
|
},
|
||||||
|
async setInitialFilterAndProviders() {
|
||||||
|
const radiusOptions = [25, 50, 100];
|
||||||
|
const { zipCode } = useMainStore().order.customer.address;
|
||||||
|
|
||||||
|
const tpaProvidersRadius25 = await useMainStore().getTpaProviders(zipCode, radiusOptions[0]);
|
||||||
|
const tpaProvidersRadius50 = await useMainStore().getTpaProviders(zipCode, radiusOptions[1]);
|
||||||
|
const tpaProvidersRadius100 = await useMainStore().getTpaProviders(zipCode, radiusOptions[2]);
|
||||||
|
let radius = '25 miles';
|
||||||
|
|
||||||
|
let providers = tpaProvidersRadius25;
|
||||||
|
if ((tpaProvidersRadius25?.data?.shopProviders ?? []).length === 0) {
|
||||||
|
radius = '50 miles';
|
||||||
|
providers = tpaProvidersRadius50;
|
||||||
|
if ((tpaProvidersRadius50?.data?.shopProviders ?? []).length === 0) {
|
||||||
|
radius = '100 miles';
|
||||||
|
providers = tpaProvidersRadius100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.filter = radius;
|
||||||
|
this.providers = providers.data.shopProviders;
|
||||||
|
},
|
||||||
|
async getProviderButtonData() {
|
||||||
const getTpaProvidersResult = await useMainStore().getTpaProviders(this.zipCode, this.radiusInMiles);
|
const getTpaProvidersResult = await useMainStore().getTpaProviders(this.zipCode, this.radiusInMiles);
|
||||||
return getTpaProvidersResult?.data ?? [];
|
return getTpaProvidersResult?.data?.shopProviders ?? [];
|
||||||
},
|
},
|
||||||
doNotSeeMyShopLinkClick() {
|
doNotSeeMyShopLinkClick() {
|
||||||
this.$router.navigate(
|
this.$router.navigate(
|
||||||
|
|
@ -268,7 +282,8 @@ export default {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
async searchClick() {
|
async searchClick() {
|
||||||
this.providers = await this.getProviders();
|
this.providers = await this.getProviderButtonData();
|
||||||
|
this.mapZipCode = this.zipCode;
|
||||||
},
|
},
|
||||||
backButtonAction() {
|
backButtonAction() {
|
||||||
this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route);
|
this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route);
|
||||||
|
|
@ -287,11 +302,16 @@ export default {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setFilter(filter) {
|
getShopButtonDataFromProvider(provider) {
|
||||||
this.filter = filter;
|
const { phoneNumber } = provider;
|
||||||
},
|
const distance = +provider.distanceInMiles.toFixed(1);
|
||||||
setProviders(providers) {
|
|
||||||
this.providers = providers;
|
return {
|
||||||
|
buttonLabel: provider.name,
|
||||||
|
buttonLabelSubCopy: `${distance} mi`,
|
||||||
|
buttonBodyCopy: `${this.getProviderAddress(provider)}<br>${phoneNumber}`,
|
||||||
|
value: provider.providerNumber
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -806,38 +806,76 @@ export const useMainStore = defineStore({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
// TODO what info is returned here
|
// TODO update with real endpoint
|
||||||
getTpaProviders(zipCode, radius) {
|
getTpaProviders(zipCode, radius) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (radius === 25) {
|
if (radius === 25) {
|
||||||
resolve({});
|
resolve({
|
||||||
|
data: {
|
||||||
|
mobileProviderNumber: null,
|
||||||
|
shopProviders: null
|
||||||
|
}
|
||||||
|
});
|
||||||
} else if (radius === 50) {
|
} else if (radius === 50) {
|
||||||
resolve({
|
resolve({
|
||||||
data: [
|
data: {
|
||||||
{
|
mobileProviderNumber: null,
|
||||||
buttonLabel: 'USA Auto Glass',
|
shopProviders: [
|
||||||
buttonLabelSubCopy: '1.5 mi',
|
{
|
||||||
buttonBodyCopy: '760 Dearborn Park Ln, Worthington, OH 43085<br>614-123-5555',
|
name: 'USA Auto Glass',
|
||||||
value: '000123'
|
address: {
|
||||||
}
|
city: 'WESTERVILLE',
|
||||||
]
|
country: 'US',
|
||||||
|
state: 'OH',
|
||||||
|
streetAddress: '4403 EXECUTIVE PKWY',
|
||||||
|
streetAddress2: '',
|
||||||
|
zipCode: '43081',
|
||||||
|
zipCodeCtu: '01820'
|
||||||
|
},
|
||||||
|
distanceInMiles: 8.393453111956896,
|
||||||
|
providerNumber: '003335',
|
||||||
|
phoneNumber: '614-123-5555'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
resolve({
|
resolve({
|
||||||
data: [
|
data: {
|
||||||
{
|
mobileProviderNumber: null,
|
||||||
buttonLabel: 'USA Auto Glass',
|
shopProviders: [
|
||||||
buttonLabelSubCopy: '1.5 mi',
|
{
|
||||||
buttonBodyCopy: '760 Dearborn Park Ln, Worthington, OH 43085<br>614-123-5555',
|
name: 'USA Auto Glass',
|
||||||
value: '000123'
|
address: {
|
||||||
},
|
city: 'WESTERVILLE',
|
||||||
{
|
country: 'US',
|
||||||
buttonLabel: 'USA Auto Glass',
|
state: 'OH',
|
||||||
buttonLabelSubCopy: '1.5 mi',
|
streetAddress: '4403 EXECUTIVE PKWY',
|
||||||
buttonBodyCopy: '760 Dearborn Park Ln, Worthington, OH 43085<br>614-123-5555',
|
streetAddress2: '',
|
||||||
value: '000123'
|
zipCode: '43081',
|
||||||
}
|
zipCodeCtu: '01820'
|
||||||
]
|
},
|
||||||
|
distanceInMiles: 8.393453111956896,
|
||||||
|
providerNumber: '003335',
|
||||||
|
phoneNumber: '614-123-5555'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Safelite AutoGlass',
|
||||||
|
address: {
|
||||||
|
city: 'WORTHINGTON',
|
||||||
|
country: 'US',
|
||||||
|
state: 'OH',
|
||||||
|
streetAddress: '760 DEARBORN PARK LN',
|
||||||
|
streetAddress2: '',
|
||||||
|
zipCode: '43085',
|
||||||
|
zipCodeCtu: '01820'
|
||||||
|
},
|
||||||
|
distanceInMiles: 8.704336770196678,
|
||||||
|
providerNumber: '001820',
|
||||||
|
phoneNumber: '740-555-1234'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue