CASH-2916 | Consume new v2 endpoints for scheduling
Providers are now grouped for in-shop They can accept ranges up to 180 but that is not needed yet
This commit is contained in:
parent
d5483f0a14
commit
87c1baf381
4 changed files with 234 additions and 32 deletions
|
|
@ -112,10 +112,18 @@ const endpoints = {
|
|||
url: "/schedule/api/v1/schedule/shop-time-slots",
|
||||
method: "POST",
|
||||
},
|
||||
GetShopTimeSlotsV2: {
|
||||
url: "/schedule/api/v2/schedule/shop-time-slots",
|
||||
method: "POST",
|
||||
},
|
||||
GetMobileTimeSlots: {
|
||||
url: "/schedule/api/v1/schedule/mobile-time-slots",
|
||||
method: "POST",
|
||||
},
|
||||
GetMobileTimeSlotsV2: {
|
||||
url: "/schedule/api/v2/schedule/mobile-time-slots",
|
||||
method: "POST",
|
||||
},
|
||||
GetMobilePremiumFee: {
|
||||
url: "/parts/api/v1/parts/mobile-premium-fee",
|
||||
method: "GET",
|
||||
|
|
|
|||
|
|
@ -35,7 +35,9 @@ const storeActions = {
|
|||
GET_PRICING_BY_DAY_PART: "getPricingByDayPart",
|
||||
GET_SERVICEABILITY_DETAILS: "getServiceabilityDetails",
|
||||
GET_SHOP_TIME_SLOTS: "getShopTimeSlots",
|
||||
GET_SHOP_TIME_SLOTS_V2: "getShopTimeSlotsV2",
|
||||
GET_MOBILE_TIME_SLOTS: "getMobileTimeSlots",
|
||||
GET_MOBILE_TIME_SLOTS_V2: "getMobileTimeSlotsV2",
|
||||
GET_PROVIDERS: "getProviders",
|
||||
GET_MOBILE_PREMIUM_FEE: "getMobilePremiumFee",
|
||||
SAVE_SESSION: "saveSession",
|
||||
|
|
|
|||
|
|
@ -111,16 +111,46 @@ function appendDays(entry, newSlots) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns a promise for inshop time slots for a single provider.
|
||||
* @param {{ startDate: string, endDate: string, providerNumber: string, pageNameToLog: string }} params
|
||||
* Assigns time slots from a v2 multi-provider response onto in-shop provider entries.
|
||||
* @param {Array<{ provider: { providerNumber: string }, timeSlots: any }>} entries
|
||||
* @param {{ providerTimeSlots?: Array<{ providerNumber: string, days: any[], provisionalTriggers?: string[] }> } | null | undefined} multiProviderResponse
|
||||
*/
|
||||
function fetchInshopTimeSlots({ startDate, endDate, providerNumber, pageNameToLog }) {
|
||||
return store.dispatch("getShopTimeSlots", {
|
||||
function assignInshopTimeSlotsFromV2Response(entries, multiProviderResponse) {
|
||||
const providerTimeSlots = multiProviderResponse?.providerTimeSlots ?? [];
|
||||
entries.forEach((entry) => {
|
||||
entry.timeSlots =
|
||||
providerTimeSlots.find(
|
||||
(pts) => pts.providerNumber === entry.provider.providerNumber
|
||||
) ?? null;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends days from a v2 multi-provider response onto existing in-shop provider entries.
|
||||
* @param {Array<{ provider: { providerNumber: string }, timeSlots: any }>} entries
|
||||
* @param {{ providerTimeSlots?: Array<{ providerNumber: string, days: any[] }> } | null | undefined} multiProviderResponse
|
||||
*/
|
||||
function appendInshopTimeSlotsFromV2Response(entries, multiProviderResponse) {
|
||||
const providerTimeSlots = multiProviderResponse?.providerTimeSlots ?? [];
|
||||
entries.forEach((entry) => {
|
||||
const newSlots = providerTimeSlots.find(
|
||||
(pts) => pts.providerNumber === entry.provider.providerNumber
|
||||
);
|
||||
appendDays(entry, newSlots);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a promise for inshop time slots for multiple providers.
|
||||
* @param {{ startDate: string, endDate: string, providerNumbers: string[], pageNameToLog: string }} params
|
||||
*/
|
||||
function fetchInshopTimeSlots({ startDate, endDate, providerNumbers, pageNameToLog }) {
|
||||
return store.dispatch("getShopTimeSlotsV2", {
|
||||
payload: {
|
||||
startDate,
|
||||
endDate,
|
||||
shopAppointmentType: "InshopOrDropoff",
|
||||
providerNumber,
|
||||
providerNumbers,
|
||||
},
|
||||
pageNameToLog,
|
||||
});
|
||||
|
|
@ -131,8 +161,12 @@ function fetchInshopTimeSlots({ startDate, endDate, providerNumber, pageNameToLo
|
|||
* @param {{ startDate: string, endDate: string, zipCode: string, pageNameToLog: string }} params
|
||||
*/
|
||||
function fetchMobileTimeSlots({ startDate, endDate, zipCode, pageNameToLog }) {
|
||||
return store.dispatch("getMobileTimeSlots", {
|
||||
payload: { startDate, endDate, zipCode },
|
||||
return store.dispatch("getMobileTimeSlotsV2", {
|
||||
payload: {
|
||||
startDate,
|
||||
endDate,
|
||||
zipCode,
|
||||
},
|
||||
pageNameToLog,
|
||||
});
|
||||
}
|
||||
|
|
@ -176,16 +210,21 @@ export default {
|
|||
: null;
|
||||
const startDate = toDateString(0);
|
||||
const endDate = toDateString(SCHEDULE_FETCH_DAYS - 1);
|
||||
const providerNumbers = providers.map((provider) => provider.providerNumber);
|
||||
const timeSlotsPromiseResultMap = [
|
||||
...providers.map((provider, i) => ({
|
||||
resultKey: `inshopTimeSlots_${i}`,
|
||||
promise: fetchInshopTimeSlots({
|
||||
startDate,
|
||||
endDate,
|
||||
providerNumber: provider.providerNumber,
|
||||
pageNameToLog: to.name,
|
||||
}),
|
||||
})),
|
||||
...(providerNumbers.length
|
||||
? [
|
||||
{
|
||||
resultKey: "inshopTimeSlots",
|
||||
promise: fetchInshopTimeSlots({
|
||||
startDate,
|
||||
endDate,
|
||||
providerNumbers,
|
||||
pageNameToLog: to.name,
|
||||
}),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
// Get the mobile time slots if a mobile provider number is available
|
||||
...(mobileProviderNumber
|
||||
? [
|
||||
|
|
@ -202,9 +241,10 @@ export default {
|
|||
: []),
|
||||
];
|
||||
const timeSlotsResultMap = await settleAllPromises(timeSlotsPromiseResultMap);
|
||||
vm.inShopProvidersAndTimeslots.forEach((entry, i) => {
|
||||
entry.timeSlots = timeSlotsResultMap[`inshopTimeSlots_${i}`] ?? null;
|
||||
});
|
||||
assignInshopTimeSlotsFromV2Response(
|
||||
vm.inShopProvidersAndTimeslots,
|
||||
timeSlotsResultMap.inshopTimeSlots
|
||||
);
|
||||
if (vm.mobileProviderAndTimeSlot) {
|
||||
vm.mobileProviderAndTimeSlot.timeSlots = timeSlotsResultMap.mobileTimeSlots ?? null;
|
||||
}
|
||||
|
|
@ -332,17 +372,24 @@ export default {
|
|||
|
||||
const newStartDate = toDateString(1, this.datePickerEndDate);
|
||||
const newEndDate = toDateString(SCHEDULE_FETCH_DAYS, this.datePickerEndDate);
|
||||
const providerNumbers = this.inShopProvidersAndTimeslots.map(
|
||||
({ provider }) => provider.providerNumber
|
||||
);
|
||||
|
||||
const promiseResultMap = [
|
||||
...this.inShopProvidersAndTimeslots.map(({ provider }, i) => ({
|
||||
resultKey: `inshopTimeSlots_${i}`,
|
||||
promise: fetchInshopTimeSlots({
|
||||
startDate: newStartDate,
|
||||
endDate: newEndDate,
|
||||
providerNumber: provider.providerNumber,
|
||||
pageNameToLog: this.pageName,
|
||||
}),
|
||||
})),
|
||||
...(providerNumbers.length
|
||||
? [
|
||||
{
|
||||
resultKey: "inshopTimeSlots",
|
||||
promise: fetchInshopTimeSlots({
|
||||
startDate: newStartDate,
|
||||
endDate: newEndDate,
|
||||
providerNumbers,
|
||||
pageNameToLog: this.pageName,
|
||||
}),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(this.mobileProviderAndTimeSlot
|
||||
? [
|
||||
{
|
||||
|
|
@ -360,10 +407,10 @@ export default {
|
|||
|
||||
const resultMap = await settleAllPromises(promiseResultMap);
|
||||
|
||||
// Provider order is fixed after beforeRouteEnter, so index keys are stable.
|
||||
this.inShopProvidersAndTimeslots.forEach((entry, index) => {
|
||||
appendDays(entry, resultMap[`inshopTimeSlots_${index}`]);
|
||||
});
|
||||
appendInshopTimeSlotsFromV2Response(
|
||||
this.inShopProvidersAndTimeslots,
|
||||
resultMap.inshopTimeSlots
|
||||
);
|
||||
|
||||
if (this.mobileProviderAndTimeSlot) {
|
||||
appendDays(this.mobileProviderAndTimeSlot, resultMap.mobileTimeSlots);
|
||||
|
|
|
|||
|
|
@ -2543,6 +2543,149 @@ export const actions = {
|
|||
return globalMethods.callHttpClient(options);
|
||||
},
|
||||
|
||||
getShopTimeSlotsV2(
|
||||
context,
|
||||
{ payload: { startDate, endDate, shopAppointmentType, providerNumbers }, pageNameToLog }
|
||||
) {
|
||||
const order = context.state.order;
|
||||
const vehicle = context.state.order.vehicle;
|
||||
const payment = context.state.order.payment;
|
||||
|
||||
const lineItems = getFlattenedLineItemsWithGlassPartTag(order.lineItems);
|
||||
|
||||
const glassPieces = order.damage.glassToReplace
|
||||
? convertGlassPieceToBackEndCompatibleFormat(order.damage.glassToReplace)
|
||||
: [];
|
||||
var payload = {
|
||||
providerNumbers: providerNumbers,
|
||||
startDate: startDate,
|
||||
endDate: endDate,
|
||||
shopAppointmentType: shopAppointmentType,
|
||||
applicationName: applicationConfig.APPLICATION_NAME,
|
||||
parentAccountNumber: payment.parentAccountNumber,
|
||||
carId: vehicle.carId,
|
||||
lineItems: lineItems,
|
||||
glassPieces: glassPieces,
|
||||
eon: order.eon,
|
||||
billToAccountNumber: context.getters.payment.billToAccountNumber,
|
||||
coverage: {
|
||||
status: payment.insuranceCoverage.coverageStatus,
|
||||
deductible: order.policy.currentDeductible,
|
||||
additionalAuthFlag: order.policy.additionalAuthFlag,
|
||||
},
|
||||
partSelection: {
|
||||
hasAnsweredPartQuestions: !!order.damage.partQuestionAnswers?.length,
|
||||
hasAnsweredMoldingQuestions: !!order.damage.moldingQuestionAnswers?.length,
|
||||
hasAnsweredCapabilityQuestions: !!order.damage.capabilityQuestionAnswers?.length,
|
||||
hasManuallySelectedParts:
|
||||
!!context.state.applicationUser.pageData["vehicle-parts"]?.partsOrQuestions
|
||||
.length,
|
||||
},
|
||||
vehicle: {
|
||||
year: vehicle.year,
|
||||
make: vehicle.make,
|
||||
model: vehicle.model,
|
||||
style: vehicle.style,
|
||||
vin: vehicle.vin ?? "",
|
||||
},
|
||||
};
|
||||
|
||||
let hasCalled = timeSlotCallFlags.shopV2;
|
||||
if (!hasCalled) {
|
||||
timeSlotCallFlags.shopV2 = true;
|
||||
}
|
||||
|
||||
const options = {
|
||||
method: endpoints.GetShopTimeSlotsV2.method,
|
||||
endpoint: endpoints.GetShopTimeSlotsV2.url,
|
||||
payload: payload,
|
||||
logApiCall: true,
|
||||
pageNameToLog: pageNameToLog,
|
||||
};
|
||||
|
||||
// Only set handler if this is the very first call in this session
|
||||
if (!hasCalled) {
|
||||
options.additionalSuccessEventDataHandler = (response) => {
|
||||
const firstProviderTimeSlots = response.data.providerTimeSlots?.[0];
|
||||
return getTimeSlotsAdditionalEventData(
|
||||
firstProviderTimeSlots?.provisionalTriggers,
|
||||
order.serviceLocation.zipCode,
|
||||
firstProviderTimeSlots?.days?.[0]?.date,
|
||||
shopAppointmentType
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
return globalMethods.callHttpClient(options);
|
||||
},
|
||||
|
||||
getMobileTimeSlotsV2(context, { payload: { startDate, endDate, zipCode }, pageNameToLog }) {
|
||||
const order = context.state.order;
|
||||
const vehicle = context.state.order.vehicle;
|
||||
const payment = context.state.order.payment;
|
||||
const lineItems = getFlattenedLineItemsWithGlassPartTag(order.lineItems);
|
||||
const glassPieces = order.damage.glassToReplace
|
||||
? convertGlassPieceToBackEndCompatibleFormat(order.damage.glassToReplace)
|
||||
: [];
|
||||
var payload = {
|
||||
startDate: startDate,
|
||||
endDate: endDate,
|
||||
applicationName: applicationConfig.APPLICATION_NAME,
|
||||
parentAccountNumber: payment.parentAccountNumber,
|
||||
carId: vehicle.carId,
|
||||
lineItems: lineItems,
|
||||
glassPieces: glassPieces,
|
||||
eon: order.eon,
|
||||
billToAccountNumber: context.getters.payment.billToAccountNumber,
|
||||
coverage: {
|
||||
status: payment.insuranceCoverage.coverageStatus,
|
||||
deductible: order.policy.currentDeductible,
|
||||
additionalAuthFlag: order.policy.additionalAuthFlag,
|
||||
},
|
||||
partSelection: {
|
||||
hasAnsweredPartQuestions: !!order.damage.partQuestionAnswers?.length,
|
||||
hasAnsweredMoldingQuestions: !!order.damage.moldingQuestionAnswers?.length,
|
||||
hasAnsweredCapabilityQuestions: !!order.damage.capabilityQuestionAnswers?.length,
|
||||
hasManuallySelectedParts:
|
||||
!!context.state.applicationUser.pageData["vehicle-parts"]?.partsOrQuestions
|
||||
.length,
|
||||
},
|
||||
vehicle: {
|
||||
year: vehicle.year,
|
||||
make: vehicle.make,
|
||||
model: vehicle.model,
|
||||
style: vehicle.style,
|
||||
vin: vehicle.vin ?? "",
|
||||
},
|
||||
zipCode: zipCode ?? order.serviceLocation.zipCode,
|
||||
};
|
||||
|
||||
let hasCalled = timeSlotCallFlags.mobileV2;
|
||||
if (!hasCalled) {
|
||||
timeSlotCallFlags.mobileV2 = true;
|
||||
}
|
||||
|
||||
const options = {
|
||||
method: endpoints.GetMobileTimeSlotsV2.method,
|
||||
endpoint: endpoints.GetMobileTimeSlotsV2.url,
|
||||
payload: payload,
|
||||
logApiCall: true,
|
||||
pageNameToLog: pageNameToLog,
|
||||
};
|
||||
|
||||
// Only set handler if this is the very first call in this session
|
||||
if (!hasCalled) {
|
||||
options.additionalSuccessEventDataHandler = (response) =>
|
||||
getTimeSlotsAdditionalEventData(
|
||||
response.data.provisionalTriggers,
|
||||
order.serviceLocation.zipCode,
|
||||
response.data.days?.[0]?.date
|
||||
);
|
||||
}
|
||||
|
||||
return globalMethods.callHttpClient(options);
|
||||
},
|
||||
|
||||
getMobilePremiumFee(context, { pageNameToLog }) {
|
||||
const damageType = context.getters.damage.isRepair ? "Repair" : "Replace";
|
||||
const paymentType = context.getters.order.payment.isInsurance ? "Insurance" : "Cash";
|
||||
|
|
@ -4530,6 +4673,8 @@ function saveExternalParameterState(externalParameterState) {
|
|||
const timeSlotCallFlags = {
|
||||
shop: false,
|
||||
mobile: false,
|
||||
shopV2: false,
|
||||
mobileV2: false,
|
||||
};
|
||||
|
||||
function checkIfMiscParts(partsOrQuestions) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue