From 0431cae39f37cb29dc59af5e8d78bee39c414d2c Mon Sep 17 00:00:00 2001 From: Leah Schumann Date: Tue, 23 May 2023 07:04:51 -0400 Subject: [PATCH] WIP --- .../service-location-helper.js | 75 +++--- .../service-location-helper.spec.js | 214 +++++++++++++++++- src/store/index.js | 57 ++++- 3 files changed, 300 insertions(+), 46 deletions(-) diff --git a/src/layouts/service-location/helpers/service-location-helper/service-location-helper.js b/src/layouts/service-location/helpers/service-location-helper/service-location-helper.js index 2864941be..820992727 100644 --- a/src/layouts/service-location/helpers/service-location-helper/service-location-helper.js +++ b/src/layouts/service-location/helpers/service-location-helper/service-location-helper.js @@ -1,4 +1,5 @@ import { storeActions } from "@/constants/store-actions"; +import store from "@/store"; import baseMixin from "@/mixins/base-mixin.js"; export async function getPricedMobileFeePart(serviceZipCode) { @@ -43,48 +44,42 @@ export async function getServiceabilityDetails(serviceZipCode, lineItems) { return Promise.resolve(serviceabilityDetails); } -export async function getShopTimeSlots(providerNumber, startDate, endDate) { +export async function getAvailabilityRating(providerNumber, startDate, endDate, appointmentType) { // For a given shop provider number and date range, get the appointment time slots available - /* - Request - { - "providerNumber": "string", - "startDate": "2023-05-19T18:32:02.026Z", - "endDate": "2023-05-19T18:32:02.026Z", - "shopAppointmentType": "string", - "applicationName": "string", - "parentAccountNumber": 0, - "carId": "string", - "partNumbers": [ - "string" - ], - "glassPieces": [ - { - "name": "string", - "location": "string" + const shopTimeSlots = await baseMixin.methods.dispatchStoreAction( + storeActions.GET_SHOP_TIME_SLOTS, + { + providerNumber: providerNumber, + startDate: startDate, + endDate: endDate, + shopAppointmentType: appointmentType, + }, + false + ); + + // Rate the availability for the shop + let numberOfAppointmentsPerDay = [] + for (let i = 0; i < shopTimeSlots.days.length; i++) { + numberOfAppointmentsPerDay.push(shopTimeSlots.days[i].timeSlots.length); + } + + const dateRange = 7; + const minimumNumberOfAppointmentsPerDay = 1; + const numberOfDaysToEvaluate = 2; + + let daysWithMinimalAppointmentsCount = 0; + for (let i = 0; i < dateRange; i++) { + if (numberOfAppointmentsPerDay[i] >= minimumNumberOfAppointmentsPerDay) { + daysWithMinimalAppointmentsCount++; + if (daysWithMinimalAppointmentsCount >= numberOfDaysToEvaluate) { + break; } - ], - "eon": "string", - "paymentType": "string", - "coverage": { - "status": "string", - "deductible": 0, - "additionalAuthFlag": "string" - }, - "partSelection": { - "hasAnsweredPartQuestions": true, - "hasAnsweredMoldingQuestions": true, - "hasAnsweredCapabilityQuestions": true, - "hasManuallySelectedParts": true - }, - "vehicle": { - "year": 0, - "make": "string", - "model": "string", - "style": "string", - "vin": "string" } } - - */ + + const isGoodAvailability = daysWithMinimalAppointmentsCount >= numberOfDaysToEvaluate; + + const shopStatus = isGoodAvailability ? "Good" : "Low" + + return shopStatus; } diff --git a/src/layouts/service-location/helpers/service-location-helper/service-location-helper.spec.js b/src/layouts/service-location/helpers/service-location-helper/service-location-helper.spec.js index 755b0d870..686de0164 100644 --- a/src/layouts/service-location/helpers/service-location-helper/service-location-helper.spec.js +++ b/src/layouts/service-location/helpers/service-location-helper/service-location-helper.spec.js @@ -1,10 +1,183 @@ -import { getPricedMobileFeePart, getServiceabilityDetails } from "./service-location-helper"; +import { getPricedMobileFeePart, getServiceabilityDetails, getAvailabilityRating } from "./service-location-helper"; import { storeActions } from "@/constants/store-actions"; +jest.mock("@/store", () => ({ + getters: { + order: { + vehicle: { + year: null, + make: null, + model: null, + style: null, + carId: null, + category: null, + vin: null, + imageUrl: null, + imageVifNumber: null, + imageColor: null, + registration: { + licensePlate: null, + address: null, + city: null, + state: null, + zipCode: null, + firstName: null, + lastName: null, + }, + }, + serviceLocation: { + address: null, + city: null, + state: null, + zipCode: null, + zipCodeCtu: null, + appointmentType: null, + provider: { + providerNumber: null, + address: { + streetAddress: null, + city: null, + state: null, + zip: null, + }, + }, + }, + customer: { + emailAddress: null, + }, + damage: { + isRepair: null, + numberOfChips: null, + glassToReplace: null, + partQuestionAnswers: null, + moldingQuestionAnswers: null, + capabilityQuestionAnswers: null, + }, + lineItems: { + glassParts: null, + supportingItems: null, + vaps: null, + serverData: null, + }, + payment: { + isInsurance: null, + insuranceCoverage: { + isVerified: null, + coverageStatus: null, + }, + parentAccountNumber: 0, + }, + schedule: { + date: null, + startTime: null, + endTime: null, + routeCode: null, + }, + referralNumber: null, + referralDate: null, + referralCorrelationId: null, + eon: null, + }, + }, + +})); + const mockStoreActionGetMobileFeePart = storeActions.GET_MOBILE_FEE_PART; const mockStoreActionPriceOrderItemsAndSaveServerData = storeActions.PRICE_ORDER_ITEMS_AND_SAVE_SERVER_DATA; const mockStoreActionGetServiceabilityDetails = storeActions.GET_SERVICEABILITY_DETAILS; +const mockStoreActionGetShopTimeSlots = storeActions.GET_SHOP_TIME_SLOTS; + +const mockGetShopTimeSlotsGoodAvailability = { + estimatedServiceMinutesMinimum: 0, + estimatedServiceMinutesMaximimum: 0, + days: [ + { + date: "string", + timeSlots: [ + { + id: "string", + startTime: "", + endTime: "", + offerPremium: true + }, + ] + }, + { + date: "string", + timeSlots: [] + }, + { + date: "string", + timeSlots: [] + }, + { + date: "string", + timeSlots: [] + }, + { + date: "string", + timeSlots: [] + }, + { + date: "string", + timeSlots: [] + }, + { + date: "string", + timeSlots: [ + { + id: "string", + startTime: "", + endTime: "", + offerPremium: true + }, + ] + }, + ] +} + +const mockGetShopTimeSlotsLowAvailability = { + estimatedServiceMinutesMinimum: 0, + estimatedServiceMinutesMaximimum: 0, + days: [ + { + date: "string", + timeSlots: [] + }, + { + date: "string", + timeSlots: [] + }, + { + date: "string", + timeSlots: [] + }, + { + date: "string", + timeSlots: [] + }, + { + date: "string", + timeSlots: [] + }, + { + date: "string", + timeSlots: [] + }, + { + date: "string", + timeSlots: [ + { + id: "string", + startTime: "", + endTime: "", + offerPremium: true + }, + ] + }, + ] +} jest.mock("@/mixins/base-mixin.js", () => ({ methods: { @@ -18,7 +191,7 @@ jest.mock("@/mixins/base-mixin.js", () => ({ }); }), - dispatchStoreAction: jest.fn().mockImplementation((actionName) => { + dispatchStoreAction: jest.fn().mockImplementation((actionName, request) => { if (actionName === mockStoreActionGetMobileFeePart) { return Promise.resolve({ data: { @@ -50,6 +223,15 @@ jest.mock("@/mixins/base-mixin.js", () => ({ isRecalibrationServiceableMobile: true, }); } + + if (actionName === mockStoreActionGetShopTimeSlots) { + if (request.providerNumber == "0000001") { + return Promise.resolve(mockGetShopTimeSlotsGoodAvailability); + } + + return Promise.resolve(mockGetShopTimeSlotsLowAvailability); + + } }), }, })); @@ -124,4 +306,32 @@ describe("service-location-helper.js", () => { expect(result).toEqual(expected); }); }); + + describe("getAvailabilityRating", () => { + it("Should return a 'Good' rating", async () => { + // Arrange + const providerNumber = "0000001"; + + const expected = "Good"; + + // Act + const result = await getAvailabilityRating(providerNumber); + + // Assert + expect(result).toEqual(expected); + }); + + it("Should return a 'Low' rating", async () => { + // Arrange + const providerNumber = "0000000"; + + const expected = "Low"; + + // Act + const result = await getAvailabilityRating(providerNumber); + + // Assert + expect(result).toEqual(expected); + }); + }); }); diff --git a/src/store/index.js b/src/store/index.js index 9d06df11a..c1e82b0f0 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -551,6 +551,7 @@ export const actions = { payload: {}, }); }, + lookupVehicleByYmms(context, { year, make, model, style }) { return globalMethods.callHttpClient({ method: endpoints.LookupVehicleByYmms.method, @@ -558,6 +559,7 @@ export const actions = { payload: {}, }); }, + lookupVehicleByVin(context, { vin }) { return globalMethods.callHttpClient({ method: endpoints.LookupVehicleByVin.method, @@ -567,6 +569,7 @@ export const actions = { }, }); }, + lookupVinByPlate(context, { licensePlate, licenseState }) { return globalMethods.callHttpClient({ method: endpoints.LookupVinByPlate.method, @@ -577,6 +580,7 @@ export const actions = { }, }); }, + lookupVinByAddress( context, { licenseLastName, licenseStreetAddress, licenseZip, licenseState } @@ -592,6 +596,7 @@ export const actions = { }, }); }, + lookupVinByImage(context, image) { const data = new FormData(); data.append("vinImage", image); @@ -602,6 +607,7 @@ export const actions = { isFormData: true, }); }, + isVinByAddressPermissible(context, zip) { return globalMethods.callHttpClient({ method: endpoints.IsVinByAddressPermissible.method, @@ -609,6 +615,7 @@ export const actions = { payload: {}, }); }, + getVehicleMakes(context, { year }) { return globalMethods.callHttpClient({ method: endpoints.GetVehicleMakes.method, @@ -616,6 +623,7 @@ export const actions = { payload: {}, }); }, + getVehicleModels(context, { year, make }) { return globalMethods.callHttpClient({ method: endpoints.GetVehicleModels.method, @@ -623,6 +631,7 @@ export const actions = { payload: {}, }); }, + getVehicleStyles(context, { year, make, model }) { return globalMethods.callHttpClient({ method: endpoints.GetVehicleStyles.method, @@ -630,6 +639,7 @@ export const actions = { payload: {}, }); }, + setVehicle(context, { year, make, model, style }) { return globalMethods .callHttpClient({ @@ -652,6 +662,7 @@ export const actions = { return response; }); }, + getDamageOptions(context, { carId }) { return globalMethods.callHttpClient({ methods: endpoints.GetDamageOptions.method, @@ -659,6 +670,7 @@ export const actions = { payload: {}, }); }, + validateZip(context, { zip }) { return globalMethods.callHttpClient({ methods: endpoints.ValidateZip.method, @@ -673,18 +685,22 @@ export const actions = { context.commit(storeMutations.UPDATE_SUPPORTING_ITEMS, null); context.commit(storeMutations.UPDATE_VAPS, null); }, + resetRegistrationAndDependencies(context) { context.commit(storeMutations.RESET_REGISTRATION_STATE); context.commit(storeMutations.RESET_GLASS_PARTS_STATE); context.commit(storeMutations.UPDATE_SUPPORTING_ITEMS, null); }, + resetPartsAndDependencies(context) { context.commit(storeMutations.RESET_GLASS_PARTS_STATE); context.commit(storeMutations.UPDATE_SUPPORTING_ITEMS, null); }, + resetState(context) { context.commit(storeMutations.RESET_STATE); }, + resetSaveSessionPromise(context) { context.commit(storeMutations.RESET_SAVE_SESSION_PROMISE); }, @@ -699,12 +715,14 @@ export const actions = { }, }); }, + getHomepageName(context) { return globalMethods.callHttpClient({ method: endpoints.GetHomepageInfo.method, endpoint: endpoints.GetHomepageInfo.url(applicationConfig.APPLICATION_ABBREVIATION), }); }, + getPageData(context, { pageName }) { return globalMethods.callHttpClient({ method: endpoints.GetPageData.method, @@ -771,6 +789,7 @@ export const actions = { context.commit(storeMutations.UPDATE_SAVED_SESSION_ID, savedSessionId); context.commit(storeMutations.UPDATE_CRM_CUSTOMER_ID, crmCustomerId); }, + logPageView( context, { @@ -812,6 +831,7 @@ export const actions = { } ); }, + logCustomEvent( context, { @@ -1116,15 +1136,19 @@ export const actions = { }); }, - getShopTimeSlots( - context, - { startDate = "2023-01-01", endDate = "2023-05-01", shopAppointmentType = "" } + getShopTimeSlots(context, + { + providerNumber, + startDate = "2023-01-01", + endDate = "2023-05-01", + shopAppointmentType = "" + } ) { const order = context.state.order; const mockArray = []; var payload = { - providerNumber: order.providerNumber, + providerNumber: providerNumber ?? order.providerNumber, startDate: startDate, endDate: endDate, shopAppointmentType: shopAppointmentType, @@ -1150,6 +1174,7 @@ export const actions = { // logApiCall: false, // }); }, + getMobileEarlyBirdFee(context) { const damageType = context.getters.damage.isRepair ? "Repair" : "Replace"; const paymentType = context.getters.order.payment.isInsurance ? "Insurance" : "Cash"; @@ -1162,6 +1187,7 @@ export const actions = { // endpoint: `${endpoints.GetMobileEarlyBirdFee.url}/${paymentType}/${damageType}`, // }); }, + // Session API Actions saveSession(context) { const vehicle = context.getters.vehicle; @@ -1262,6 +1288,7 @@ export const actions = { }, }); }, + loadSession( context, { @@ -1339,6 +1366,7 @@ export const actions = { context.commit(storeMutations.UPDATE_YEAR, year); } }, + saveVehicleMake(context, make) { //Reset dependent state when changing if (context.state.order.vehicle.make !== make) { @@ -1359,6 +1387,7 @@ export const actions = { context.commit(storeMutations.UPDATE_MAKE, make); } }, + saveVehicleModel(context, model) { //Reset dependent state when changing if (context.state.order.vehicle.model !== model) { @@ -1378,6 +1407,7 @@ export const actions = { context.commit(storeMutations.UPDATE_MODEL, model); } }, + saveVehicleStyle(context, style) { //Reset dependent state when changing if (context.state.order.vehicle.style !== style) { @@ -1396,6 +1426,7 @@ export const actions = { context.commit(storeMutations.UPDATE_STYLE, style); } }, + saveVehicleDamage( context, { isWindshieldRepair, selectedGlassToReplace, selectedWindshieldChipCount } @@ -1453,6 +1484,7 @@ export const actions = { context.commit(storeMutations.UPDATE_REGISTRATION, registrationInfo); } }, + saveRegistrationLicensePlateLookup( context, { isSelectedGlassAvailableForVehicle, vehicleInfo, registrationInfo } @@ -1474,6 +1506,7 @@ export const actions = { context.commit(storeMutations.UPDATE_REGISTRATION, registrationInfo); } }, + saveRegistrationAddressLookup( context, { isSelectedGlassAvailableForVehicle, vehicleInfo, registrationInfo } @@ -1499,6 +1532,7 @@ export const actions = { context.commit(storeMutations.UPDATE_REGISTRATION, registrationInfo); } }, + savePartQuestionAnswers(context, partQuestionAnswersArray) { // if part question answers have changed, reset subsequent question answers const sortedPreviousResultsArray = sortArrayOfObjectsByPropertyValue( @@ -1537,6 +1571,7 @@ export const actions = { //Save new values context.commit(storeMutations.UPDATE_PART_QUESTION_ANSWERS, partQuestionAnswersArray); }, + resetMoldingAndCapabilityQuestionAnswersIfNeeded(context, matchedParts) { const partsOrQuestionsDataToCompareWith = context.getters.pageData(fmgPageValues.MOLDING_QUESTIONS)?.partsOrQuestions ?? @@ -1576,6 +1611,7 @@ export const actions = { }); } }, + saveMoldingQuestionAnswers(context, moldingQuestionAnswers) { const sortedPreviousResultsArray = sortArrayOfObjectsByPropertyValue( context.getters.damage.moldingQuestionAnswers, @@ -1604,6 +1640,7 @@ export const actions = { //Save new values context.commit(storeMutations.UPDATE_MOLDING_QUESTION_ANSWERS, moldingQuestionAnswers); }, + saveCapabilityQuestionAnswers(context, capabilityQuestionAnswers) { const sortedPreviousResultsArray = sortArrayOfObjectsByPropertyValue( context.getters.damage.capabilityQuestionAnswers, @@ -1630,18 +1667,23 @@ export const actions = { capabilityQuestionAnswers ); }, + savePaymentType(context, isInsurance) { context.commit(storeMutations.UPDATE_IS_INSURANCE, isInsurance); }, + saveParentAccountNumber(context, parentAccountNumber) { context.commit(storeMutations.UPDATE_PARENT_ACCT_NUMBER, parentAccountNumber); }, + saveSupportingItems(context, supportingItems) { context.commit(storeMutations.UPDATE_SUPPORTING_ITEMS, supportingItems); }, + saveVaps(context, vaps) { context.commit(storeMutations.UPDATE_VAPS, vaps); }, + // Price order actions async priceOrderItemsAndSaveServerData( context, @@ -1694,16 +1736,20 @@ export const actions = { return availableLineItems; }, + // Misc order actions saveSchedule(context, scheduleInfo) { context.commit(storeMutations.UPDATE_SCHEDULE, scheduleInfo); }, + saveServiceLocation(context, serviceLocationInfo) { context.commit(storeMutations.UPDATE_SERVICE_LOCATION, serviceLocationInfo); }, + saveEmail(context, email) { context.commit(storeMutations.UPDATE_CUSTOMER_EMAIL_ADDRESS, email); }, + saveVin(context, { isSelectedGlassAvailableForVehicle, vehicleInfo }) { //Reset dependent state when changing if (vehicleInfo.vin !== context.state.order.vehicle.vin) { @@ -1716,12 +1762,15 @@ export const actions = { context.commit(storeMutations.UPDATE_VEHICLE, vehicleInfo); } }, + saveGlassParts(context, parts) { context.commit(storeMutations.UPDATE_GLASS_PARTS, parts); }, + clearVin(context) { context.commit(storeMutations.UPDATE_VEHICLE_VIN, null); }, + isVinOptionalVehicle(context) { switch (context.state.order.vehicle.make.toLowerCase()) { case "mercedes benz":