Merge branch 'develop' into feature/SSR-862

This commit is contained in:
Katie Kroell 2023-11-17 08:57:05 -05:00
commit e9df2ed6ee
15 changed files with 1100 additions and 112 deletions

View file

@ -88,6 +88,7 @@ stages:
indexDeployVariables:
__VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY__: $(__VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY__)
__VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC__: $(__VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC__)
__VUE_APP_GOOGLE_MAPS_API_SCRIPT__: $(__VUE_APP_GOOGLE_MAPS_API_SCRIPT__)
- template: templates/digital/invalidate-cloudfront-cache.yml@AzureDevOps
parameters:
awsCliContainer: awscli
@ -137,6 +138,7 @@ stages:
indexDeployVariables:
__VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY__: $(__VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY__)
__VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC__: $(__VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC__)
__VUE_APP_GOOGLE_MAPS_API_SCRIPT__: $(__VUE_APP_GOOGLE_MAPS_API_SCRIPT__)
- template: templates/digital/invalidate-cloudfront-cache.yml@AzureDevOps
parameters:
awsCliContainer: awscli
@ -186,6 +188,7 @@ stages:
indexDeployVariables:
__VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY__: $(__VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY__)
__VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC__: $(__VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC__)
__VUE_APP_GOOGLE_MAPS_API_SCRIPT__: $(__VUE_APP_GOOGLE_MAPS_API_SCRIPT__)
- template: templates/digital/invalidate-cloudfront-cache.yml@AzureDevOps
parameters:
awsCliContainer: awscli
@ -235,6 +238,7 @@ stages:
indexDeployVariables:
__VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY__: $(__VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY__)
__VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC__: $(__VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC__)
__VUE_APP_GOOGLE_MAPS_API_SCRIPT__: $(__VUE_APP_GOOGLE_MAPS_API_SCRIPT__)
- template: templates/digital/invalidate-cloudfront-cache.yml@AzureDevOps
parameters:
awsCliContainer: awscli

View file

@ -5,6 +5,7 @@
window.dataLayer = [{}];
</script>
<script><%= process.env.VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY %></script>
<script><%= process.env.VUE_APP_GOOGLE_MAPS_API_SCRIPT %></script>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">

View file

@ -140,7 +140,7 @@ const endpoints = Object.freeze({
method: 'GET'
},
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: {
url: '/clientauth/api/v1/clientauth/validate-client-tag',

View file

@ -196,7 +196,7 @@ export default {
const addressField1 = document.getElementById('autocomplete');
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)
.then(() => {

View file

@ -0,0 +1,8 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Google Map should render expected initial data 1`] = `
Object {
"country": "USA",
"geocoder": null,
}
`;

View file

@ -0,0 +1,428 @@
import { shallowMount } from '@vue/test-utils';
import googleMap from '@/iss-components/google-map/google-map.vue';
const mockFitBounds = jest.fn();
const mockMapInstance = {
fitBounds: mockFitBounds
};
const mockMap = jest.fn(() => mockMapInstance);
const mockGeocode = jest.fn();
const mockGeocoderInstance = {
geocode: mockGeocode
};
const mockGeocoder = jest.fn(() => (mockGeocoderInstance));
const mockAdvancedMarkerElement = jest.fn();
const mockImportLibrary = jest.fn().mockImplementation(() => (Promise.resolve({
Map: mockMap,
Geocoder: mockGeocoder,
AdvancedMarkerElement: mockAdvancedMarkerElement
})));
const mockExtend = jest.fn();
const mockLatLngBound = { extend: mockExtend };
const setupGoogleMock = () => {
global.window.google = {
maps: {
importLibrary: mockImportLibrary,
LatLngBounds: jest.fn(() => (mockLatLngBound))
}
};
};
beforeEach(() => {
// clear constructors
mockMap.mockClear();
mockGeocoder.mockClear();
mockAdvancedMarkerElement.mockClear();
// clear methods
mockExtend.mockClear();
mockGeocode.mockClear();
mockFitBounds.mockClear();
setupGoogleMock();
});
describe('Google Map', () => {
describe('should render', () => {
test('expected initial data', async () => {
// Arrange
const wrapper = shallowMount(googleMap, {});
await wrapper.vm.$nextTick();
// Assert
expect(wrapper.vm.$data).toMatchSnapshot();
});
test('map', () => {
// Arrange
const mapReference = '#map';
const wrapper = shallowMount(googleMap, {});
// Act
const map = wrapper.find(mapReference);
// Assert
expect(map.exists()).toBeTruthy();
});
});
test.each([
['13001', '13001 USA'],
['9', '9 USA'],
['', ' USA'],
[null, ' USA']
])(
'given that zipcode set to "%p", computed zipCodeAddress should return "%p"',
async (zipCode, expected) => {
// Arrange
const wrapper = shallowMount(googleMap, {});
await wrapper.setData({ zipCode });
// Act
const result = wrapper.vm.zipCodeAddress;
// Assert
expect(result).toBe(expected);
}
);
test('watch addresses creates map with expected markers', async () => {
// Arrange
const wrapper = shallowMount(googleMap, {});
const zipCode = '00882';
await wrapper.setData({ zipCode });
const mockGetCenter = jest.fn();
const mockNorthEast = jest.fn();
const mockSouthWest = jest.fn();
const zipCodeGeocodeResult = {
results: [
{
geometry: {
bounds:
{
getCenter: mockGetCenter,
getNorthEast: mockNorthEast,
getSouthWest: mockSouthWest
}
}
}
]
};
const [address1, address2, position1, position2] = ['a1', 'a2', 'p1', 'p2'];
mockGeocode.mockImplementation((obj) => {
let result = {};
if (obj.address.includes(zipCode)) {
result = zipCodeGeocodeResult;
} else if (obj.address === address1) {
result = {
results: [
{ geometry: { location: position1 } }
]
};
} else if (obj.address === address2) {
result = {
results: [
{ geometry: { location: position2 } }
]
};
}
return Promise.resolve(result);
});
const newAddresses = [address1, address2];
// Act
await wrapper.vm.$options.watch.addresses.call(wrapper.vm, newAddresses);
for (let i = 0; i < 11; i++) {
// eslint-disable-next-line no-await-in-loop
await wrapper.vm.$nextTick();
}
// Assert
expect(mockMap).toBeCalledTimes(1);
expect(mockAdvancedMarkerElement).toBeCalledTimes(2);
expect(mockAdvancedMarkerElement).toBeCalledWith(expect.objectContaining({
map: expect.anything(),
position: position1
}));
expect(mockAdvancedMarkerElement).toBeCalledWith(expect.objectContaining({
map: expect.anything(),
position: position2
}));
expect(mockGeocoder).toBeCalledTimes(1);
expect(mockExtend).toBeCalledTimes(4);
expect(mockFitBounds).toBeCalledTimes(1);
expect(mockGetCenter).toBeCalledTimes(1);
expect(mockNorthEast).toBeCalledTimes(1);
expect(mockSouthWest).toBeCalledTimes(1);
});
describe('method', () => {
test('getMap calls map constructor with expected parameters', async () => {
// Arrange
const wrapper = shallowMount(googleMap, {});
const center = { lat: 90, lng: -5 };
// Act
const result = await wrapper.vm.getMap(center);
// Assert
expect(mockMap).toHaveBeenCalledWith(
null,
expect.objectContaining({ center, mapId: 'map_id', mapTypeControl: false })
);
expect(result).toBe(mockMapInstance);
});
test.each([
[undefined, 0],
[null, 0],
[[], 0],
[['loc1'], 1],
[['loc1', 'loc1', 'loc1', 'loc1'], 4]
])(
'given parameter %p, getBounds calls extend %p time(s)',
(locations, numberOfExtendCalls) => {
// Arrange
const wrapper = shallowMount(googleMap, {});
// Act
const result = wrapper.vm.getBounds(locations);
// Assert
expect(mockExtend).toHaveBeenCalledTimes(numberOfExtendCalls);
expect(result).toBe(mockLatLngBound);
}
);
test.each([
[1, null, mockGeocoderInstance],
[1, undefined, mockGeocoderInstance],
[0, {}, {}],
[0, { value: 'any value' }, { value: 'any value' }]
])(
'setGeocoder calls Geocoder constructor %p time(s) when original geocoder %p and geocoder set to %p',
async (numberOfCalls, originalGeocoder, newGeocoder) => {
// Arrange
const wrapper = shallowMount(googleMap, {});
await wrapper.setData({ geocoder: originalGeocoder });
// Act
await wrapper.vm.setGeocoder();
// Assert
expect(mockGeocoder).toHaveBeenCalledTimes(numberOfCalls);
expect(wrapper.vm.geocoder).toEqual(newGeocoder);
}
);
test.each([
[null, 0],
[undefined, 0],
[[], 0],
[['p1'], 1],
[['p1', 'p2', 'p3'], 3]
])(
'addMarkersToMap given positions argument %p, creates AdvancedMarkerElement %p time(s)',
async (positions, numberOfCalls) => {
// Arrange
const wrapper = shallowMount(googleMap, {});
const map = jest.fn();
// Act
await wrapper.vm.addMarkersToMap(map, positions);
// Assert
expect(mockAdvancedMarkerElement).toHaveBeenCalledTimes(numberOfCalls);
}
);
describe('getLocationsFromAddresses', () => {
test.each([
[null, 0],
[undefined, 0],
[[], 0],
[['a1'], 1],
[['a1', 'a2', 'a3'], 3]
])(
'given address(es) %p calls geocode %p times',
async (addresses, numberOfCalls) => {
// Arrange
const wrapper = shallowMount(googleMap, {});
// Act
await wrapper.vm.getLocationsFromAddresses(addresses);
// Assert
expect(mockGeocode).toHaveBeenCalledTimes(numberOfCalls);
expect(wrapper.vm.geocoder).not.toBeNull();
}
);
test('only returns geocode results that are not null', async () => {
// Arrange
const wrapper = shallowMount(googleMap, {});
const addresses = ['a1', 'a2', 'a3', 'a4', 'a5', 'a6', 'a7', 'a8'];
mockGeocode.mockImplementationOnce(() => null)
.mockImplementationOnce(() => ({ results: null }))
.mockImplementationOnce(() => ({ results: [] }))
.mockImplementationOnce(() => ({ results: [{}] }))
.mockImplementationOnce(() => (
{ results: [{ geometry: undefined }] }
))
.mockImplementationOnce(() => (
{ results: [{ geometry: {} }] }
))
.mockImplementationOnce(() => (
{
results: [
{ geometry: { location: 'some value' } }
]
}
))
.mockImplementationOnce(() => (
{
results: [
{ geometry: { location: 'some other value' } }
]
}
));
const expectedResult = ['some value', 'some other value'];
// Act
const result = await wrapper.vm.getLocationsFromAddresses(addresses);
// Assert
expect(result.length).toBe(2);
expect(result).toEqual(expectedResult);
});
});
describe('getBoundsFromAddress', () => {
test('returns expected when geocode returns valid result', async () => {
// Arrange
const wrapper = shallowMount(googleMap, {});
const address = 'random address';
const expectedResult = 'bounds returned';
const geocodeResult = {
results: [
{
geometry: { bounds: expectedResult }
}
]
};
mockGeocode.mockImplementationOnce(() => Promise.resolve(geocodeResult));
// Act
const result = await wrapper.vm.getBoundsFromAddress(address);
// Assert
expect(result).toBe(expectedResult);
expect(wrapper.vm.geocoder).not.toBeNull();
});
test('returns null when geocode returns invalid result', async () => {
// Arrange
const wrapper = shallowMount(googleMap, {});
const address = 'random address';
mockGeocode.mockImplementationOnce(() => Promise.resolve({}));
// Act
const result = await wrapper.vm.getBoundsFromAddress(address);
// Assert
expect(result).toBeNull();
expect(wrapper.vm.geocoder).not.toBeNull();
});
});
describe('createMapWithMarkersForAddresses', () => {
test('calls expected when addresses provided', async () => {
// Arrange
const wrapper = shallowMount(googleMap, {});
const zipCode = '00882';
await wrapper.setData({ zipCode });
const mockGetCenter = jest.fn();
const mockNorthEast = jest.fn();
const mockSouthWest = jest.fn();
const zipCodeGeocodeResult = {
results: [
{
geometry: {
bounds:
{
getCenter: mockGetCenter,
getNorthEast: mockNorthEast,
getSouthWest: mockSouthWest
}
}
}
]
};
const shopGeocodeResult = {
results: [
{ geometry: { location: 'valid location' } }
]
};
mockGeocode.mockImplementation((obj) =>
Promise.resolve(obj.address.includes(zipCode)
? zipCodeGeocodeResult
: shopGeocodeResult));
const addresses = ['a1', 'a2'];
// Act
await wrapper.vm.createMapWithMarkersForAddresses(addresses);
// Assert
expect(mockMap).toBeCalledTimes(1);
expect(mockAdvancedMarkerElement).toBeCalledTimes(2);
expect(mockGeocoder).toBeCalledTimes(1);
expect(mockExtend).toBeCalledTimes(4);
expect(mockFitBounds).toBeCalledTimes(1);
expect(mockGetCenter).toBeCalledTimes(1);
expect(mockNorthEast).toBeCalledTimes(1);
expect(mockSouthWest).toBeCalledTimes(1);
});
test.each([null, undefined, []])(
'calls expected when addresses argument is "%p"',
async (addresses) => {
// Arrange
const wrapper = shallowMount(googleMap, {});
const zipCode = '00882';
await wrapper.setData({ zipCode });
const mockGetCenter = jest.fn();
const mockNorthEast = jest.fn();
const mockSouthWest = jest.fn();
const zipCodeGeocodeResult = {
results: [
{
geometry: {
bounds:
{
getCenter: mockGetCenter,
getNorthEast: mockNorthEast,
getSouthWest: mockSouthWest
}
}
}
]
};
mockGeocode.mockImplementation(() => Promise.resolve(zipCodeGeocodeResult));
// Act
await wrapper.vm.createMapWithMarkersForAddresses(addresses);
// Assert
expect(mockMap).toBeCalledTimes(1);
expect(mockAdvancedMarkerElement).toBeCalledTimes(0);
expect(mockGeocoder).toBeCalledTimes(1);
expect(mockExtend).toBeCalledTimes(2);
expect(mockFitBounds).toBeCalledTimes(1);
expect(mockGetCenter).toBeCalledTimes(1);
expect(mockNorthEast).toBeCalledTimes(1);
expect(mockSouthWest).toBeCalledTimes(1);
}
);
});
});
});

View file

@ -0,0 +1,93 @@
<template>
<div>
<div id="map" />
</div>
</template>
<script>
export default {
name: 'google-map',
props: {
addresses: Array,
zipCode: String
},
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?.length > 0
? result.results[0].geometry?.location
: null))
.filter((loc) => loc != null);
},
async getBoundsFromAddress(address) {
await this.setGeocoder();
const result = await this.geocoder.geocode({ address });
return result?.results?.length > 0
? result.results[0].geometry?.bounds
: null;
},
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>

View file

@ -152,7 +152,7 @@ describe('Shop list button', () => {
expect(buttonLabelSubCopy.classes().length).toBe(4);
expect(buttonLabelSubCopy.classes()).toContain('m-0');
expect(buttonLabelSubCopy.classes()).toContain('caption');
expect(buttonLabelSubCopy.classes()).toContain('ms-1');
expect(buttonLabelSubCopy.classes()).toContain('ms-2');
expect(buttonLabelSubCopy.classes()).toContain(textPosition);
});
test('when "textPosition" prop not provided', async () => {
@ -176,7 +176,7 @@ describe('Shop list button', () => {
expect(buttonLabelSubCopy.classes().length).toBe(3);
expect(buttonLabelSubCopy.classes()).toContain('m-0');
expect(buttonLabelSubCopy.classes()).toContain('caption');
expect(buttonLabelSubCopy.classes()).toContain('ms-1');
expect(buttonLabelSubCopy.classes()).toContain('ms-2');
});
});
describe('availability indicator block with expected when displayAvailabilityIndicators true', () => {

View file

@ -17,7 +17,7 @@
</span>
<span
id="buttonLabelSubCopySpan"
class="m-0 caption ms-1"
class="m-0 caption ms-2"
:class="textPosition">{{ buttonLabelSubCopy }}
</span>
<div

View file

@ -6,6 +6,7 @@ Object {
"displayAvailabilityIndicators": false,
},
"filter": "",
"mapZipCode": "12663",
"providers": Array [],
"rules": Object {
"filter": "option-required",

View file

@ -44,7 +44,7 @@ function getMountedComponent(mainInitialState = {}, initialData = {}, methodToRu
fetchCmsContentForPage.mockImplementation(() => Promise.resolve());
const wrapper = shallowMount(tpaSearch, mountOptions);
wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => cmsContent);
wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => {});
wrapper.vm.setCmsContent = jest.fn();
return { wrapper };
}
@ -149,16 +149,31 @@ describe('TPA search page', () => {
// Assert
expect(providerSelectionForm.exists()).toBeTruthy();
});
test('map', () => {
test('map', async () => {
// Arrange
const wrapper = shallowMount(tpaSearch, getMountOptions());
const zipCode = '12345';
const { wrapper } = getMountedComponent();
await wrapper.setData({
providers: [{
address: {
streetAddress: '123 Lane Ave',
city: 'New York',
state: 'NY',
zipCode: '99230'
}
}],
mapZipCode: zipCode
});
const expectedAddresses = ['123 Lane Ave, New York, NY 99230'];
// Act
const map = wrapper.find('#map');
const map = wrapper.findComponent('#map');
// Assert
expect(map.exists()).toBeTruthy();
expect(map.classes()).toContain('mb-4');
expect(map.props().addresses).toEqual(expectedAddresses);
expect(map.props().zipCode).toBe(zipCode);
});
test('search radius filter', () => {
// Arrange
@ -173,9 +188,32 @@ describe('TPA search page', () => {
expect(searchRadiusFilter.props().cmsWidgetName).toBe(expectedWidgetName);
expect(searchRadiusFilter.props().validationRules).toBe(globalRules.OPTION_REQUIRED);
});
test('select provider question', () => {
test('select provider question', async () => {
// Arrange
const wrapper = shallowMount(tpaSearch, getMountOptions());
await wrapper.setData({
providers: [{
name: 'Shop Auto Glass',
phoneNumber: '740-555-1234',
distanceInMiles: 1.2345,
providerNumber: '232209',
address: {
streetAddress: '123 Lane Ave',
city: 'New York',
state: 'NY',
zipCode: '99230'
}
}]
});
const expectedAnswers = [{
buttonLabel: 'Shop Auto Glass',
buttonLabelSubCopy: '1.2 mi',
buttonBodyCopy: '123 Lane Ave, New York, NY 99230<br>740-555-1234',
value: '232209'
}];
const expectedAdditionalButtonData = {
displayAvailabilityIndicators: false
};
// Act
const selectProviderQuestion = wrapper.findComponent('#selectProviderQuestion');
@ -188,6 +226,8 @@ describe('TPA search page', () => {
expect(selectProviderQuestion.props().textPosition).toBe('text-start');
expect(selectProviderQuestion.props().isRequired).toBeTruthy();
expect(selectProviderQuestion.props().validationRules).toBe(globalRules.OPTION_REQUIRED);
expect(selectProviderQuestion.props().answers).toEqual(expectedAnswers);
expect(selectProviderQuestion.props().additionalButtonData).toEqual(expectedAdditionalButtonData);
});
test.each([[], null, undefined])(
'no network providers alert when providers length is 0, undefined, or null',
@ -447,12 +487,143 @@ describe('TPA search page', () => {
expect(result).toEqual(expected);
});
});
describe('providerAddresses', () => {
test.each([null, undefined, []])(
'returns empty list when providers empty',
async (providers) => {
// Arrange
const wrapper = shallowMount(tpaSearch, getMountOptions());
await wrapper.setData({ providers });
// Act
const result = wrapper.vm.providerAddresses;
// Assert
expect(result).toEqual([]);
}
);
test('returns expected when providers not empty', async () => {
// Arrange
const wrapper = shallowMount(tpaSearch, getMountOptions());
await wrapper.setData({
providers: [
{
name: 'Shop Auto Glass',
phoneNumber: '740-555-1234',
distanceInMiles: 1.2345,
providerNumber: '232209',
address: {
streetAddress: '123 Lane Ave',
city: 'New York',
state: 'NY',
zipCode: '99230'
}
},
{
name: 'Safelite AutoGlass',
phoneNumber: '676-555-0099',
distanceInMiles: 123.9452,
providerNumber: '123456',
address: {
streetAddress: '2003 Coral Ln',
city: 'Birmingham',
state: 'AL',
zipCode: '43001'
}
}
]
});
const expectedResult = [
'123 Lane Ave, New York, NY 99230',
'2003 Coral Ln, Birmingham, AL 43001'
];
// Act
const result = wrapper.vm.providerAddresses;
// Assert
expect(result).toEqual(expectedResult);
});
});
describe('providerButtonData', () => {
test.each([null, undefined, []])(
'returns empty list when providers empty',
async (providers) => {
// Arrange
const wrapper = shallowMount(tpaSearch, getMountOptions());
await wrapper.setData({ providers });
// Act
const result = wrapper.vm.providerButtonData;
// Assert
expect(result).toEqual([]);
}
);
test('returns expected when providers not empty', async () => {
// Arrange
const wrapper = shallowMount(tpaSearch, getMountOptions());
await wrapper.setData({
providers: [
{
name: 'Shop Auto Glass',
phoneNumber: '740-555-1234',
distanceInMiles: 1.2345,
providerNumber: '232209',
address: {
streetAddress: '123 Lane Ave',
city: 'New York',
state: 'NY',
zipCode: '99230'
}
},
{
name: 'Safelite AutoGlass',
phoneNumber: '676-555-0099',
distanceInMiles: 123.9452,
providerNumber: '123456',
address: {
streetAddress: '2003 Coral Ln',
city: 'Birmingham',
state: 'AL',
zipCode: '43001'
}
}
]
});
const expectedResult = [
{
buttonLabel: 'Shop Auto Glass',
buttonLabelSubCopy: '1.2 mi',
buttonBodyCopy: '123 Lane Ave, New York, NY 99230<br>740-555-1234',
value: '232209'
},
{
buttonLabel: 'Safelite AutoGlass',
buttonLabelSubCopy: '123.9 mi',
buttonBodyCopy: '2003 Coral Ln, Birmingham, AL 43001<br>676-555-0099',
value: '123456'
}
];
// Act
const result = wrapper.vm.providerButtonData;
// Assert
expect(result).toEqual(expectedResult);
});
});
});
describe('watch', () => {
test('on filter calls getTpaProviders and sets providers', async () => {
// Arrange
const { wrapper } = getMountedComponent();
const providers = { data: ['some data', 'some more data'] };
const zipCode = '78220';
const initialMapZipCode = '88800';
const { wrapper } = getMountedComponent({}, {
zipCode,
mapZipCode: initialMapZipCode
});
const providers = { data: { shopProviders: ['some data', 'some more data']} };
useMainStore().getTpaProviders = jest.fn().mockImplementationOnce(() => (providers));
const newFilter = 'new filter';
@ -461,7 +632,8 @@ describe('TPA search page', () => {
// Assert
expect(useMainStore().getTpaProviders).toBeCalled();
expect(wrapper.vm.providers).toEqual(providers.data);
expect(wrapper.vm.providers).toEqual(providers.data.shopProviders);
expect(wrapper.vm.mapZipCode).toBe(zipCode);
});
describe('on providers', () => {
test.each([null, undefined, []])(
@ -481,7 +653,7 @@ describe('TPA search page', () => {
// Arrange
const { wrapper } = getMountedComponent();
const value = 'some value';
const newProviders = [{ value }];
const newProviders = [{ providerNumber: value }];
// Act
wrapper.vm.$options.watch.providers.call(wrapper.vm, newProviders);
@ -503,7 +675,35 @@ describe('TPA search page', () => {
});
});
describe('method', () => {
describe('getProviders', () => {
test.each([
['address', 'city', 'ST', 'zip', 'address, city, ST zip'],
['address', null, 'ST', 'zip', 'address, ST zip'],
['address', 'city', null, 'zip', 'address, city zip'],
['address', 'city', 'ST', null, 'address, city, ST'],
[null, null, null, null, '']
])(
'getProviderAddress returns expected',
(streetAddress, city, state, zipCode, expected) => {
// Arrange
const { wrapper } = getMountedComponent();
useMainStore().getTpaProviders = jest.fn().mockImplementationOnce(() => (newProviders));
const provider = {
address: {
streetAddress,
city,
state,
zipCode
}
};
// Act
const result = wrapper.vm.getProviderAddress(provider);
// Assert
expect(result).toEqual(expected);
}
);
describe('getProviderButtonData', () => {
test.each([null, undefined, {}])(
'returns empty list when getTpaProviders returns no data',
async (newProviders) => {
@ -512,7 +712,7 @@ describe('TPA search page', () => {
useMainStore().getTpaProviders = jest.fn().mockImplementationOnce(() => (newProviders));
// Act
const result = await wrapper.vm.getProviders();
const result = await wrapper.vm.getProviderButtonData();
// Assert
expect(result).toEqual([]);
@ -521,14 +721,18 @@ describe('TPA search page', () => {
test('returns expected when getTpaProviders returns data', async () => {
// Arrange
const { wrapper } = getMountedComponent();
const providers = { data: [{ name: 'foo' }] };
const providers = {
data: {
shopProviders: [{ name: 'foo' }]
}
};
useMainStore().getTpaProviders = jest.fn().mockImplementationOnce(() => (providers));
// Act
const result = await wrapper.vm.getProviders();
const result = await wrapper.vm.getProviderButtonData();
// Assert
expect(result).toEqual(providers.data);
expect(result).toEqual(providers.data.shopProviders);
});
});
test('doNotSeeMyShopLinkClick invokes navigate method', () => {
@ -546,7 +750,12 @@ describe('TPA search page', () => {
'sets providers to empty list when no data returned from getProviders',
async (newProviders) => {
// Arrange
const { wrapper } = getMountedComponent();
const zipCode = '28331';
const initialMapZipCode = '10022';
const { wrapper } = getMountedComponent({}, {
zipCode,
mapZipCode: initialMapZipCode
});
useMainStore().getTpaProviders = jest.fn().mockImplementationOnce(() => (newProviders));
// Act
@ -554,19 +763,28 @@ describe('TPA search page', () => {
// Assert
expect(wrapper.vm.providers).toEqual([]);
expect(wrapper.vm.mapZipCode).toEqual(zipCode);
}
);
test('returns value from getProviders', async () => {
// Arrange
const { wrapper } = getMountedComponent();
const newProviders = { data: [{ name: 'foo' }] };
useMainStore().getTpaProviders = jest.fn().mockImplementationOnce(() => (newProviders));
const zipCode = '12990';
const initialMapZipCode = '00981';
const { wrapper } = getMountedComponent({}, {
zipCode,
mapZipCode: initialMapZipCode
});
const shopProviders = [{ name: 'foo' }];
useMainStore().getTpaProviders = jest.fn().mockImplementationOnce(() => ({
data: { shopProviders }
}));
// Act
await wrapper.vm.searchClick();
// Assert
expect(wrapper.vm.providers).toEqual(newProviders.data);
expect(wrapper.vm.providers).toEqual(shopProviders);
expect(wrapper.vm.mapZipCode).toEqual(zipCode);
});
});
test('backButtonAction invokes navigate method', () => {
@ -613,32 +831,162 @@ describe('TPA search page', () => {
expect(result).toBeNull();
});
});
test.each([undefined, null, 'some value', ''])(
'setFilter sets value of filter',
(filter) => {
describe('getShopButtonDataFromProvider', () => {
test.each([null, undefined, {}])(
'returns empty list given parameter with no provider',
(provider) => {
// Arrange
const wrapper = shallowMount(tpaSearch, getMountOptions());
const expectedResult = {
buttonLabel: '',
buttonLabelSubCopy: '',
buttonBodyCopy: '<br>',
value: ''
};
// Act
const result = wrapper.vm.getShopButtonDataFromProvider(provider);
// Assert
expect(result).toEqual(expectedResult);
}
);
test('returns expected given provider', () => {
// Arrange
const { wrapper } = getMountedComponent();
const wrapper = shallowMount(tpaSearch, getMountOptions());
const provider = {
name: 'Shop Auto Glass',
phoneNumber: '740-555-1234',
distanceInMiles: 1.2345,
providerNumber: '232209',
address: {
streetAddress: '123 Lane Ave',
city: 'New York',
state: 'NY',
zipCode: '99230'
}
};
const expectedResult = {
buttonLabel: 'Shop Auto Glass',
buttonLabelSubCopy: '1.2 mi',
buttonBodyCopy: '123 Lane Ave, New York, NY 99230<br>740-555-1234',
value: '232209'
};
// Act
wrapper.vm.setFilter(filter);
const result = wrapper.vm.getShopButtonDataFromProvider(provider);
// Assert
expect(wrapper.vm.filter).toEqual(filter);
}
);
test.each([undefined, null, ['some value'], []])(
'setProviders sets value of providers',
(providers) => {
expect(result).toEqual(expectedResult);
});
describe('returns result with button label', () => {
test.each(['', undefined, null])(
'equal to empty string when name is empty',
(name) => {
// Arrange
const wrapper = shallowMount(tpaSearch, getMountOptions());
const provider = { name };
// Act
const result = wrapper.vm.getShopButtonDataFromProvider(provider);
// Assert
expect(result.buttonLabel).toEqual('');
}
);
test('returns provider name when exists', () => {
// Arrange
const wrapper = shallowMount(tpaSearch, getMountOptions());
const name = 'Some Shop Name';
const provider = { name };
// Act
const result = wrapper.vm.getShopButtonDataFromProvider(provider);
// Assert
expect(result.buttonLabel).toEqual(name);
});
});
describe('returns result with button label sub copy', () => {
test.each([null, undefined, 'random string'])(
'as empty string when distanceInMiles is not a number',
(distanceInMiles) => {
// Arrange
const wrapper = shallowMount(tpaSearch, getMountOptions());
const provider = { distanceInMiles };
// Act
const result = wrapper.vm.getShopButtonDataFromProvider(provider);
// Assert
expect(result.buttonLabelSubCopy).toEqual('');
}
);
test.each([[0, 0], [0, 0.000], [-7.89, -7.9], [109, 109], [1.234, 1.2], [2.99, 3]])(
'displays expected distance value',
(distanceInMiles, displayedDistance) => {
// Arrange
const wrapper = shallowMount(tpaSearch, getMountOptions());
const provider = { distanceInMiles };
const expectedSubCopy = `${displayedDistance} mi`;
// Act
const result = wrapper.vm.getShopButtonDataFromProvider(provider);
// Assert
expect(result.buttonLabelSubCopy).toEqual(expectedSubCopy);
}
);
});
test('returns result with button body copy showing expected address and phone number', () => {
// Arrange
const { wrapper } = getMountedComponent();
const wrapper = shallowMount(tpaSearch, getMountOptions());
const provider = {
phoneNumber: '123-555-0099',
address: {
streetAddress: '434 Up Down Blvd',
city: 'San Francisco',
state: 'CA',
zipCode: '90887'
}
};
const expected = '434 Up Down Blvd, San Francisco, CA 90887<br>123-555-0099';
// Act
wrapper.vm.setProviders(providers);
const result = wrapper.vm.getShopButtonDataFromProvider(provider);
// Assert
expect(wrapper.vm.providers).toEqual(providers);
}
);
expect(result.buttonBodyCopy).toEqual(expected);
});
describe('returns result with value', () => {
test.each(['', undefined, null])(
'equal to empty string when providerNumber is empty',
(providerNumber) => {
// Arrange
const wrapper = shallowMount(tpaSearch, getMountOptions());
const provider = { providerNumber };
// Act
const result = wrapper.vm.getShopButtonDataFromProvider(provider);
// Assert
expect(result.value).toEqual('');
}
);
test('returns provider number when exists', () => {
// Arrange
const wrapper = shallowMount(tpaSearch, getMountOptions());
const providerNumber = '123391';
const provider = { providerNumber };
// Act
const result = wrapper.vm.getShopButtonDataFromProvider(provider);
// Assert
expect(result.value).toEqual(providerNumber);
});
});
});
});
describe('before route enter', () => {
test('when providers exist at 25 mile radius, filter is set to "25 miles" and providers set to expected', async () => {
@ -646,8 +994,9 @@ describe('TPA search page', () => {
const { wrapper } = getMountedComponent();
const zipCode = '18394';
useMainStore().order.customer.address.zipCode = zipCode;
const providers = { data: [{ name: 'provider' }] };
useMainStore().getTpaProviders = jest.fn().mockImplementation((_, radius) => (radius === 25 ? providers : []));
const providers = [{ name: 'provider' }];
useMainStore().getTpaProviders = jest.fn().mockImplementation((_, radius) =>
({ data: { shopProviders: radius === 25 ? providers : [] } }));
const expectedFilter = '25 miles';
// Act
@ -658,6 +1007,9 @@ describe('TPA search page', () => {
(c) => c(wrapper.vm)
);
await wrapper.vm.$nextTick();
await wrapper.vm.$nextTick();
// Assert
expect(wrapper.vm.filter).toBe(expectedFilter);
expect(wrapper.vm.providers).toEqual(providers);
@ -667,8 +1019,9 @@ describe('TPA search page', () => {
const { wrapper } = getMountedComponent();
const zipCode = '18394';
useMainStore().order.customer.address.zipCode = zipCode;
const providers = { data: [{ name: 'provider' }] };
useMainStore().getTpaProviders = jest.fn().mockImplementation((_, radius) => (radius === 50 ? providers : []));
const providers = [{ name: 'provider' }];
useMainStore().getTpaProviders = jest.fn().mockImplementation((_, radius) =>
({ data: { shopProviders: radius === 50 ? providers : [] } }));
const expectedFilter = '50 miles';
// Act
@ -679,6 +1032,9 @@ describe('TPA search page', () => {
(c) => c(wrapper.vm)
);
await wrapper.vm.$nextTick();
await wrapper.vm.$nextTick();
// Assert
expect(wrapper.vm.filter).toBe(expectedFilter);
expect(wrapper.vm.providers).toEqual(providers);
@ -690,8 +1046,9 @@ describe('TPA search page', () => {
const { wrapper } = getMountedComponent();
const zipCode = '18394';
useMainStore().order.customer.address.zipCode = zipCode;
const providers = { data: [{ name: 'provider' }] };
useMainStore().getTpaProviders = jest.fn().mockImplementation((_, radius) => (radius === 100 ? providers : []));
const providers = [{ name: 'provider' }];
useMainStore().getTpaProviders = jest.fn().mockImplementation((_, radius) =>
({ data: { shopProviders: radius === 100 ? providers : [] } }));
const expectedFilter = '100 miles';
// Act
@ -702,6 +1059,9 @@ describe('TPA search page', () => {
(c) => c(wrapper.vm)
);
await wrapper.vm.$nextTick();
await wrapper.vm.$nextTick();
// Assert
expect(wrapper.vm.filter).toBe(expectedFilter);
expect(wrapper.vm.providers).toEqual(providers);
@ -725,6 +1085,9 @@ describe('TPA search page', () => {
(c) => c(wrapper.vm)
);
await wrapper.vm.$nextTick();
await wrapper.vm.$nextTick();
// Assert
expect(wrapper.vm.filter).toBe(expectedFilter);
expect(wrapper.vm.providers).toEqual([]);

View file

@ -1,5 +1,6 @@
<template>
<div>
<div class="page-container-grouped-styles">
<div class="fade-on-route-transition">
<siteHeader
ref="siteHeader"
:cmsWidgetName="widget.siteHeader" />
@ -36,18 +37,17 @@
</div>
</div>
</Form>
<googleMap
id="map"
class="mb-4"
:addresses="providerAddresses"
:zipCode="mapZipCode"></googleMap>
<Form
id="providerSelectionForm"
v-slot="{ meta }"
@submit="onSubmit"
@invalidSubmit="onInvalidSubmit">
<div class="container-fluid pb-2">
<p
id="map"
class="mb-4">
Placeholder for Map
</p>
<div class="mx-5">
<dropdownQuestion
id="searchRadiusFilter"
@ -65,7 +65,7 @@
buttonTypeString="shopListButton"
:buttonTypeObject="shopListButton"
class="radioQuestion"
:answers="providers"
:answers="providerButtonData"
groupName="chooseShop"
textPosition="text-start"
isRequired
@ -98,6 +98,7 @@
</div>
</Form>
</div>
</div>
</template>
<script>
// Components
@ -105,11 +106,12 @@ 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 shopListButton from '@/iss-components/shop-list-button/shop-list-button.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';
// Supporting files
import { fetchCmsContentForPage } from '@/helpers/cms-content-helper';
@ -118,7 +120,6 @@ 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 routerParams from '@/router/router-constants/router-params';
export default {
name: 'tpa-search',
@ -129,6 +130,7 @@ export default {
buttonQuestion,
textLink,
alert,
googleMap,
siteFooter,
// eslint-disable-next-line vue/no-reserved-component-names
Form
@ -144,34 +146,16 @@ export default {
];
const resultMap = await settleAllPromises(promiseResultMap);
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 ?? []).length === 0) {
radius = '50 miles';
providers = tpaProvidersRadius50;
if ((tpaProvidersRadius50?.data ?? []).length === 0) {
radius = '100 miles';
providers = tpaProvidersRadius100;
}
}
next((vm) => {
next(async (vm) => {
vm.setCmsContent(resultMap.cmsContent);
vm.setFilter(radius);
vm.setProviders(providers);
await vm.setInitialFilterAndProviders();
});
},
data() {
const { zipCode } = useMainStore().order.customer.address;
return {
zipCode,
mapZipCode: zipCode,
filter: '',
providers: [],
selectedProviderNumber: '',
@ -242,24 +226,79 @@ export default {
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)) ?? [];
}
},
watch: {
async filter() {
// 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) {
this.selectedProviderNumber = newProviders?.length === 1 ?? false
? newProviders[0].value
? newProviders[0].providerNumber
: '';
}
},
methods:
{
async getProviders() {
getProviderAddress(provider) {
const city = provider?.address?.city ?? '';
const state = provider?.address?.state ?? '';
const zipCode = provider?.address?.zipCode ?? '';
let addressLine2 = '';
if (city) {
addressLine2 = city;
if (state || zipCode) {
if (state) {
addressLine2 += ',';
}
addressLine2 += ' ';
}
}
if (state) {
addressLine2 += `${state}`;
if (zipCode) {
addressLine2 += ' ';
}
}
if (zipCode) {
addressLine2 += `${zipCode}`;
}
return provider?.address?.streetAddress
? `${provider.address.streetAddress}, ${addressLine2}`
: 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);
return getTpaProvidersResult?.data ?? [];
return getTpaProvidersResult?.data?.shopProviders ?? [];
},
doNotSeeMyShopLinkClick() {
this.$router.navigate(
@ -268,7 +307,8 @@ export default {
);
},
async searchClick() {
this.providers = await this.getProviders();
this.providers = await this.getProviderButtonData();
this.mapZipCode = this.zipCode;
},
backButtonAction() {
this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route);
@ -287,11 +327,20 @@ export default {
return null;
}
},
setFilter(filter) {
this.filter = filter;
},
setProviders(providers) {
this.providers = providers;
getShopButtonDataFromProvider(provider) {
const cellNumber = provider?.phoneNumber ?? '';
const distance = provider?.distanceInMiles !== null && !Number.isNaN(parseFloat(provider?.distanceInMiles))
? +provider.distanceInMiles.toFixed(1)
: null;
return {
buttonLabel: provider?.name ?? '',
buttonLabelSubCopy: distance === null
? ''
: `${distance} mi`,
buttonBodyCopy: `${this.getProviderAddress(provider)}<br>${cellNumber ?? ''}`,
value: provider?.providerNumber ?? ''
};
}
}
};

View file

@ -848,38 +848,76 @@ export const useMainStore = defineStore({
});
},
// TODO what info is returned here
// TODO update with real endpoint
getTpaProviders(zipCode, radius) {
return new Promise((resolve, reject) => {
if (radius === 25) {
resolve({});
resolve({
data: {
mobileProviderNumber: null,
shopProviders: null
}
});
} else if (radius === 50) {
resolve({
data: [
{
buttonLabel: 'USA Auto Glass',
buttonLabelSubCopy: '1.5 mi',
buttonBodyCopy: '760 Dearborn Park Ln, Worthington, OH 43085<br>614-123-5555',
value: '000123'
}
]
data: {
mobileProviderNumber: null,
shopProviders: [
{
name: 'USA Auto Glass',
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 {
resolve({
data: [
{
buttonLabel: 'USA Auto Glass',
buttonLabelSubCopy: '1.5 mi',
buttonBodyCopy: '760 Dearborn Park Ln, Worthington, OH 43085<br>614-123-5555',
value: '000123'
},
{
buttonLabel: 'USA Auto Glass',
buttonLabelSubCopy: '1.5 mi',
buttonBodyCopy: '760 Dearborn Park Ln, Worthington, OH 43085<br>614-123-5555',
value: '000123'
}
]
data: {
mobileProviderNumber: null,
shopProviders: [
{
name: 'USA Auto Glass',
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'
},
{
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'
}
]
}
});
}
});

View file

@ -9,6 +9,8 @@ process.env.VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY =
"(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src='https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);})(window,document,'script','dataLayer','GTM-KKNWZ3');";
process.env.VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC =
'https://www.googletagmanager.com/ns.html?id=GTM-KKNWZ3&gtm_auth=amlAYNhxUxuskQo7jmjadg&gtm_preview=env-38&gtm_cookies_win=x';
process.env.VUE_APP_GOOGLE_MAPS_API_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.googleapis.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: 'AIzaSyCuLhQcDdZTTb4JzpUFms1OCch2dk5lHF0', v: 'weekly'});";
module.exports = {
publicPath: '/',

View file

@ -5,6 +5,7 @@ process.env.VUE_APP_GOOGLE_PLACES_API_KEY = "__VUE_APP_GOOGLE_PLACES_API_KEY__";
// GA & GTM
process.env.VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY = "__VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY__";
process.env.VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC = "__VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC__"
process.env.VUE_APP_GOOGLE_MAPS_API_SCRIPT = "__VUE_APP_GOOGLE_MAPS_API_SCRIPT__";
module.exports = {
publicPath: "/",