Refactoring to include loaders and improve speed
This commit is contained in:
parent
32e12ec672
commit
79e6cdd553
5 changed files with 274 additions and 155 deletions
|
|
@ -67,10 +67,6 @@ const endpoints = Object.freeze({
|
|||
url: '/location/api/v1/location/providers',
|
||||
method: 'GET'
|
||||
},
|
||||
// GetTpaProviders: {
|
||||
// url: '/location/api/v1/location/providers',
|
||||
// method: 'GET'
|
||||
// },
|
||||
GetCapabilityQuestions: {
|
||||
url: '/parts/api/v1/parts/capability-questions',
|
||||
method: 'GET'
|
||||
|
|
|
|||
|
|
@ -37,66 +37,86 @@
|
|||
</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">
|
||||
<div class="mx-5">
|
||||
<dropdownQuestion
|
||||
id="searchRadiusFilter"
|
||||
v-model="filter"
|
||||
:cmsWidgetName="widget.filterByQuestion"
|
||||
inputId="filterByQuestionField"
|
||||
:options="filterOptions"
|
||||
disableAutoFill
|
||||
:validationRules="rules.filter" />
|
||||
<loader
|
||||
v-if="initialLoading"
|
||||
ref="providersLoader"
|
||||
loaderPosition="center"
|
||||
loaderColor="blue"
|
||||
:width="4"
|
||||
:height="4"
|
||||
class="loader" />
|
||||
<div v-else>
|
||||
<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">
|
||||
<div class="mx-5">
|
||||
<dropdownQuestion
|
||||
id="searchRadiusFilter"
|
||||
v-model="filter"
|
||||
:cmsWidgetName="widget.filterByQuestion"
|
||||
inputId="filterByQuestionField"
|
||||
:options="filterOptions"
|
||||
disableAutoFill
|
||||
:validationRules="rules.filter" />
|
||||
|
||||
<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="#!"
|
||||
:text="shopNotListedModalLink"
|
||||
@clickEvent="doNotSeeMyShopLinkClick" />
|
||||
<loader
|
||||
v-if="reloadProviders"
|
||||
ref="providersLoader"
|
||||
loaderPosition="center"
|
||||
loaderColor="blue"
|
||||
:width="4"
|
||||
:height="4"
|
||||
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="#!"
|
||||
:text="shopNotListedModalLink"
|
||||
@clickEvent="doNotSeeMyShopLinkClick" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<siteFooter
|
||||
ref="siteFooter"
|
||||
:cmsWidgetName="widget.siteFooter"
|
||||
:isForwardActionDisabled="!meta.valid"
|
||||
@ForwardClicked="forwardButtonAction"
|
||||
@backClicked="backButtonAction" />
|
||||
</div>
|
||||
<siteFooter
|
||||
ref="siteFooter"
|
||||
:cmsWidgetName="widget.siteFooter"
|
||||
:isForwardActionDisabled="!meta.valid"
|
||||
@ForwardClicked="forwardButtonAction"
|
||||
@backClicked="backButtonAction" />
|
||||
</div>
|
||||
</Form>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -112,6 +132,7 @@ 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';
|
||||
|
|
@ -130,6 +151,7 @@ export default {
|
|||
buttonQuestion,
|
||||
textLink,
|
||||
alert,
|
||||
loader,
|
||||
googleMap,
|
||||
siteFooter,
|
||||
// eslint-disable-next-line vue/no-reserved-component-names
|
||||
|
|
@ -159,6 +181,8 @@ export default {
|
|||
filter: '',
|
||||
providers: [],
|
||||
selectedProviderNumber: '',
|
||||
initialLoading: true,
|
||||
reloadProviders: false,
|
||||
additionalButtonData: {
|
||||
displayAvailabilityIndicators: false
|
||||
},
|
||||
|
|
@ -237,8 +261,12 @@ export default {
|
|||
watch: {
|
||||
async filter() {
|
||||
// TODO right now this results in getProviders being called once more than it needs to be
|
||||
this.providers = await this.getProviderButtonData();
|
||||
this.mapZipCode = this.zipCode;
|
||||
if (!this.initialLoading) {
|
||||
this.reloadProviders = true;
|
||||
this.providers = await this.getProviderButtonData();
|
||||
this.mapZipCode = this.zipCode;
|
||||
this.reloadProviders = false;
|
||||
}
|
||||
},
|
||||
providers(newProviders) {
|
||||
this.selectedProviderNumber = newProviders?.length === 1 ?? false
|
||||
|
|
@ -279,22 +307,29 @@ export default {
|
|||
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';
|
||||
console.log('getTpaProviders calls');
|
||||
const tpaProvidersRadius25 = useMainStore().getTpaProviders(zipCode, radiusOptions[0]);
|
||||
const tpaProvidersRadius50 = useMainStore().getTpaProviders(zipCode, radiusOptions[1]);
|
||||
const tpaProvidersRadius100 = useMainStore().getTpaProviders(zipCode, radiusOptions[2]);
|
||||
console.log('after not awaited getTpaProviders calls');
|
||||
|
||||
let providers = tpaProvidersRadius25;
|
||||
if ((tpaProvidersRadius25?.data?.shopProviders ?? []).length === 0) {
|
||||
let radius = '25 miles';
|
||||
let providers = await tpaProvidersRadius25;
|
||||
console.log('after 25 awaited');
|
||||
if ((providers?.data?.shopProviders ?? []).length === 0) {
|
||||
radius = '50 miles';
|
||||
providers = tpaProvidersRadius50;
|
||||
if ((tpaProvidersRadius50?.data?.shopProviders ?? []).length === 0) {
|
||||
providers = await tpaProvidersRadius50;
|
||||
console.log('after 50 awaited');
|
||||
if ((providers?.data?.shopProviders ?? []).length === 0) {
|
||||
radius = '100 miles';
|
||||
providers = tpaProvidersRadius100;
|
||||
providers = await tpaProvidersRadius100;
|
||||
console.log('after 100 awaited');
|
||||
}
|
||||
}
|
||||
console.log('after if');
|
||||
this.filter = radius;
|
||||
this.providers = providers?.data?.shopProviders ?? [];
|
||||
this.initialLoading = false;
|
||||
},
|
||||
async getProviderButtonData() {
|
||||
const getTpaProvidersResult = await useMainStore().getTpaProviders(this.zipCode, this.radiusInMiles);
|
||||
|
|
@ -307,8 +342,10 @@ export default {
|
|||
);
|
||||
},
|
||||
async searchClick() {
|
||||
this.reloadProviders = true;
|
||||
this.providers = await this.getProviderButtonData();
|
||||
this.mapZipCode = this.zipCode;
|
||||
this.reloadProviders = false;
|
||||
},
|
||||
backButtonAction() {
|
||||
this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route);
|
||||
|
|
@ -334,7 +371,7 @@ export default {
|
|||
: null;
|
||||
|
||||
return {
|
||||
buttonLabel: provider?.name ?? '',
|
||||
buttonLabel: provider?.companyName ?? '',
|
||||
buttonLabelSubCopy: distance === null
|
||||
? ''
|
||||
: `${distance} mi`,
|
||||
|
|
|
|||
|
|
@ -859,85 +859,14 @@ export const useMainStore = defineStore({
|
|||
|
||||
getTpaProviders(zipCode, radius) {
|
||||
const damageType = this.damage.isRepair ? 'Repair' : 'Replace';
|
||||
const { accountNumber } = this.issConfig;
|
||||
const { parentAccountNumber } = this.issConfig;
|
||||
const safeliteOnly = false;
|
||||
return new Promise((resolve, reject) => {
|
||||
globalMethods.callHttpClient({
|
||||
method: endpoints.GetProviders.method,
|
||||
endpoint: `${endpoints.GetProviders.url}/${zipCode}/${damageType}/${radius}/${accountNumber}/${safeliteOnly}`
|
||||
endpoint: `${endpoints.GetProviders.url}/${zipCode}/${damageType}/${radius}/${parentAccountNumber}/${safeliteOnly}`
|
||||
}).then((response) => resolve(response), (error) => reject(error));
|
||||
});
|
||||
// return new Promise((resolve, reject) => {
|
||||
// if (radius === 25) {
|
||||
// resolve({
|
||||
// data: {
|
||||
// mobileProviderNumber: null,
|
||||
// shopProviders: null
|
||||
// }
|
||||
// });
|
||||
// } else if (radius === 50) {
|
||||
// resolve({
|
||||
// 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: {
|
||||
// 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'
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// });
|
||||
},
|
||||
|
||||
async getSupportingItems() {
|
||||
|
|
|
|||
|
|
@ -1961,4 +1961,145 @@ describe('Store', () => {
|
|||
expect(store.order.currentDeductible).toEqual(finalDeductible);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getTpaProviders method', () => {
|
||||
describe('successful method call', () => {
|
||||
it('calls getProviders api endpoint', async () => {
|
||||
// Arrange
|
||||
globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.resolve({}));
|
||||
|
||||
// Act
|
||||
await store.getCoveragePolicyInfo();
|
||||
|
||||
// Asserts
|
||||
expect(globalMethods.callHttpClient).toHaveBeenCalledWith(expect.objectContaining({
|
||||
method: endpoints.CoveragePolicyInfo.method,
|
||||
endpoint: endpoints.CoveragePolicyInfo.url
|
||||
}));
|
||||
});
|
||||
it('Returns expected response object', async () => {
|
||||
// Arrange
|
||||
const response = { ReferralNumber: getRandomString(6, 6) };
|
||||
globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.resolve(response));
|
||||
|
||||
// Act
|
||||
const result = store.getCoveragePolicyInfo();
|
||||
|
||||
// Asserts
|
||||
await expect(result).resolves.toBe(response);
|
||||
});
|
||||
it('calls api with expected data', async () => {
|
||||
// Arrange
|
||||
const accountNumber = getRandomString(6, 6);
|
||||
const policyNumber = getRandomString(6, 6);
|
||||
const dateOfLoss = getRandomString(6, 6);
|
||||
const zipCode = getRandomString(6, 6);
|
||||
const referralCorrelationId = getRandomString(6, 6);
|
||||
store.order.accountNumber = accountNumber;
|
||||
store.order.policy.policyNumber = policyNumber;
|
||||
store.order.policy.dateOfLoss = dateOfLoss;
|
||||
store.order.policy.policyZipCode = zipCode;
|
||||
store.order.referralCorrelationId = referralCorrelationId;
|
||||
globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.resolve({}));
|
||||
|
||||
// Act
|
||||
await store.getCoveragePolicyInfo();
|
||||
|
||||
// Asserts
|
||||
expect(globalMethods.callHttpClient).toHaveBeenCalledWith(expect.objectContaining({
|
||||
payload: expect.objectContaining({
|
||||
accountNumber,
|
||||
policyNumber,
|
||||
dateOfLoss,
|
||||
zipCode,
|
||||
referralCorrelationId
|
||||
})
|
||||
}));
|
||||
});
|
||||
it('null response by api => no exceptions thrown', async () => {
|
||||
// Arrange
|
||||
globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.resolve(null));
|
||||
|
||||
// Act
|
||||
await store.getCoveragePolicyInfo();
|
||||
|
||||
// Asserts
|
||||
expect(store.order.policy.policyLookupSuccessful).toBe(false);
|
||||
});
|
||||
it('no providers returned by api => promise resolves to empty list', async () => {
|
||||
// Arrange
|
||||
const responseNoPolicies = {
|
||||
data: {
|
||||
policies: []
|
||||
}
|
||||
};
|
||||
globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.resolve(responseNoPolicies));
|
||||
|
||||
// Act
|
||||
await store.getCoveragePolicyInfo();
|
||||
|
||||
// Asserts
|
||||
expect(store.order.policy.policyLookupSuccessful).toBe(false);
|
||||
});
|
||||
it('multiple providers returned by api => promise resolves to expected', async () => {
|
||||
// Arrange
|
||||
const insured = {
|
||||
address: getRandomString(6, 6),
|
||||
firstName: getRandomString(6, 6),
|
||||
lastName: getRandomString(6, 6),
|
||||
city: getRandomString(6, 6),
|
||||
state: getRandomString(6, 6),
|
||||
zipCode: getRandomString(6, 6)
|
||||
};
|
||||
const vehicles = [
|
||||
{ name: getRandomString(6, 6) },
|
||||
{ name: getRandomString(6, 6) }
|
||||
];
|
||||
const policy1 = {
|
||||
insureds: [insured],
|
||||
vehicles
|
||||
};
|
||||
const responseNoPolicies = {
|
||||
data: {
|
||||
policies: [policy1, {}]
|
||||
}
|
||||
};
|
||||
globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.resolve(responseNoPolicies));
|
||||
|
||||
// Act
|
||||
await store.getCoveragePolicyInfo();
|
||||
|
||||
// Asserts
|
||||
expect(store.order.policy.policyLookupSuccessful).toBe(true);
|
||||
|
||||
expect(store.order.customer.address.streetAddress).toBe(insured.address);
|
||||
expect(store.order.customer.address.city).toBe(insured.city);
|
||||
expect(store.order.customer.address.state).toBe(insured.state);
|
||||
expect(store.order.customer.address.zipCode).toBe(insured.zipCode);
|
||||
expect(store.order.customer.firstName).toBe(insured.firstName);
|
||||
expect(store.order.customer.lastName).toBe(insured.lastName);
|
||||
|
||||
expect(store.order.serviceLocation.zipCode).toBe(insured.zipCode);
|
||||
expect(store.order.policy.vehicles).toEqual(vehicles);
|
||||
});
|
||||
});
|
||||
it('api call throws exception => policyLookupSuccessful false', async () => {
|
||||
expect.assertions(3);
|
||||
const error = 'get coverage policy info error';
|
||||
globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.reject(error));
|
||||
|
||||
// Act
|
||||
await store.getCoveragePolicyInfo().catch((e) => {
|
||||
expect(e).toEqual(error);
|
||||
});
|
||||
|
||||
// Asserts
|
||||
expect(globalMethods.callHttpClient)
|
||||
.toHaveBeenCalledWith(expect.objectContaining({
|
||||
method: endpoints.CoveragePolicyInfo.method,
|
||||
endpoint: endpoints.CoveragePolicyInfo.url
|
||||
}));
|
||||
expect(store.order.policy.policyLookupSuccessful).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
<template>
|
||||
<div
|
||||
class="loader"
|
||||
role="alert"
|
||||
aria-label="Loading new page"
|
||||
:class="[loaderColor, loaderPosition]"></div>
|
||||
<div
|
||||
:style="cssProps"
|
||||
class="loader"
|
||||
role="alert"
|
||||
aria-label="Loading new page"
|
||||
:class="[loaderColor, loaderPosition]"></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
|
@ -18,6 +19,22 @@ export default {
|
|||
/* Position options: center, right, left (OPTIONAL, do NOT use on btn-* classes) */
|
||||
loaderPosition: {
|
||||
type: String
|
||||
},
|
||||
width: {
|
||||
type: Number,
|
||||
default: 1
|
||||
},
|
||||
height: {
|
||||
type: Number,
|
||||
default: 1
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
cssProps() {
|
||||
return {
|
||||
'--loader-width': `${this.width}rem`,
|
||||
'--loader-height': `${this.height}rem`
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -45,8 +62,8 @@ export default {
|
|||
mask: url(../../assets/img/icons/spinner.svg);
|
||||
mask-size: cover;
|
||||
position: relative;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
width: var(--loader-width);
|
||||
height: var(--loader-height);
|
||||
border-radius:50%;
|
||||
animation: rotation 1s infinite linear;
|
||||
@keyframes rotation {
|
||||
|
|
@ -57,8 +74,7 @@ export default {
|
|||
}
|
||||
//Spinner position
|
||||
&.center {
|
||||
right: 50%;
|
||||
transform: translateX(50%);
|
||||
justify-content: center;
|
||||
}
|
||||
&.right {
|
||||
right: 1rem;
|
||||
|
|
|
|||
Loading…
Reference in a new issue