diff --git a/src/digital-components/sierra-webchat/sierra-webchat.vue b/src/digital-components/sierra-webchat/sierra-webchat.vue index 2a114616a..8210e9075 100644 --- a/src/digital-components/sierra-webchat/sierra-webchat.vue +++ b/src/digital-components/sierra-webchat/sierra-webchat.vue @@ -94,8 +94,16 @@ export default { FirstName: transfer.data.first_name, LastName: transfer.data.last_name, Email: transfer.data.email, - Subject: transfer.data.chat_summary, // for now passing in chat_summery in Subject. But this will be changed in future. + Subject: transfer.data.chat_summary, }; + window.embedded_svc.settings.extraPrechatFormDetails = [ + { + label: "Scarlett AI Summary", + value: transfer.data.chat_summary, + transcriptFields: ["Scarlett_AI_Summary__c"], + displayToAgent: true, + }, + ]; if ( window.embedded_svc.liveAgentAPI && typeof window.embedded_svc.liveAgentAPI.startChat === "function" diff --git a/src/fmg-components/cart/cart.vue b/src/fmg-components/cart/cart.vue index 2a715bc93..ef3e0300c 100644 --- a/src/fmg-components/cart/cart.vue +++ b/src/fmg-components/cart/cart.vue @@ -117,6 +117,10 @@ {{ amountPaidText }} {{ getLineItemAmount(amountPaid) }} +
+ {{ donationCartItemName }} + {{ getLineItemAmount(donationCartItem.subTotal) }} +
{{ amountDueText }} {{ getLineItemAmount(amountDue, showCoverageAsPending) }} @@ -472,10 +476,6 @@ export default { }); } - if (this.donationCartItem) { - cartItems.push(this.donationCartItem); - } - return cartItems; }, }, @@ -1309,12 +1309,15 @@ export default { .sub-total, .sales-tax, .amount-due, - .amount-paid { - font-weight: 500; + .amount-paid, + .donation-amount { + font-family: + UrbanistSemibold, AvertaSemibold; //Okay to remove AvertaSemibold after 2025.06.19 merge/release color: $black; } .sub-total { border-top: 1px solid $green; + background-color: $green-100; } .service-type, .deductible { diff --git a/src/layouts/estimate/estimate.vue b/src/layouts/estimate/estimate.vue index 811cd1a86..2818d2ecb 100644 --- a/src/layouts/estimate/estimate.vue +++ b/src/layouts/estimate/estimate.vue @@ -62,7 +62,7 @@ import { saveSession } from "@/helpers/heritage-integration/order-helper.js"; import baseMixin from "@/mixins/base-mixin.js"; import { queryStrings } from "@/constants/query-strings"; import { nextTick } from "vue"; -import { consumeQueryFromStash } from "@/router/methods/helpers/querystring-stash"; +import { peekQueryFromStash } from "@/router/methods/helpers/querystring-stash"; // Define Validation Rules defineRule("zip-required", required(errorMessages.SERVICE_ZIP_REQUIRED)); @@ -100,7 +100,7 @@ export default { } const zip = - consumeQueryFromStash(queryStrings.ZIP_CODE) ?? + peekQueryFromStash(queryStrings.ZIP_CODE) ?? store.getters.order.serviceLocation.zipCode; var vinByAddressPromise; diff --git a/src/router/methods/helpers/querystring-stash.js b/src/router/methods/helpers/querystring-stash.js index 96b206572..b000f8feb 100644 --- a/src/router/methods/helpers/querystring-stash.js +++ b/src/router/methods/helpers/querystring-stash.js @@ -1,4 +1,5 @@ import { reactive } from "vue"; +import { debugLog } from "@/helpers/debug-log-helper"; const queryStash = reactive({ queries: [], @@ -27,10 +28,18 @@ export function stashAllQueries(toRoute) { } function getStashedQuery(key) { + debugLog(`Fetching querystring with key:`, key); const match = queryStash.queries.find( (entry) => entry?.key?.toLowerCase() === key?.toLowerCase() ); + if (match) { + debugLog(`Found result:`, match.value); + debugLog(`Already consumed:`, match.used); + } else { + debugLog(`Found no result`); + } + return match; } diff --git a/src/router/methods/route-logic/payment.js b/src/router/methods/route-logic/payment.js new file mode 100644 index 000000000..73f2b5cac --- /dev/null +++ b/src/router/methods/route-logic/payment.js @@ -0,0 +1,14 @@ +import store from "@/store"; +import { storeActions } from "@/constants/store-actions"; +import { paymentMethods } from "@/constants/payment-method-constants"; +import { debugLog } from "@/helpers/debug-log-helper"; + +export async function paymentBeforeEnter(to, from) { + const piaType = store.getters.order?.payment?.piaType; + debugLog(`Entering payment with type =`, piaType); + + if (piaType === paymentMethods.PAYPAL) { + debugLog(`Changing to payment type =`, paymentMethods.CREDIT_CARD); + await store.dispatch(storeActions.SAVE_PAYMENT_METHOD_CHOICE, paymentMethods.CREDIT_CARD); + } +} diff --git a/src/router/methods/routes.js b/src/router/methods/routes.js index 19c802aae..c029ce2cb 100644 --- a/src/router/methods/routes.js +++ b/src/router/methods/routes.js @@ -11,6 +11,7 @@ import { autoRouteBeforeEnter } from "@/router/methods/route-logic/auto-route"; import { errorBeforeEnter } from "@/router/methods/route-logic/error"; import { restartBeforeEnter } from "@/router/methods/route-logic/restart"; import { paymentMethodBeforeEnter } from "@/router/methods/route-logic/payment-method"; +import { paymentBeforeEnter } from "@/router/methods/route-logic/payment"; export const routes = [ // Non-virtual pages. @@ -32,7 +33,7 @@ export const routes = [ createRoute(routeData.SCHEDULE), createRoute(routeData.CUSTOMER_DETAILS), createRoute(routeData.PAYMENT_METHOD, paymentMethodBeforeEnter), - createRoute(routeData.PAYMENT), + createRoute(routeData.PAYMENT, paymentBeforeEnter), createRoute(routeData.PAYMENT_PIA_RETURN), createRoute(routeData.CONFIRMATION), createRoute(routeData.RETURN_USER), diff --git a/src/store/index.js b/src/store/index.js index c3ad4996e..073a50e23 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -1072,33 +1072,6 @@ export const getters = { }, }; -function getNonFalseValuesOfPropertyInArrayOfObjects(array, propertyName) { - return (array ?? []).map((x) => x[propertyName]).filter((x) => x); -} - -function getTimeSlotsAdditionalEventData( - provisionalTriggers, - zipCode, - firstAvailableAppointmentDateString, - shopAppointmentType -) { - var numberOfDays = null; - if (firstAvailableAppointmentDateString) - numberOfDays = getDateDifferenceInDays( - new Date().toISOString().split("T")[0], - firstAvailableAppointmentDateString - ); - - if (shopAppointmentType) - return `FirstAvailableAppointment:${numberOfDays},Zip:${zipCode},ShopAppointmentType:${shopAppointmentType},ProvisionalTriggers:${provisionalTriggers.join( - "," - )}`; - else - return `FirstAvailableAppointment:${numberOfDays},Zip:${zipCode},ProvisionalTriggers:${provisionalTriggers.join( - "," - )}`; -} - // Export Actions export const actions = { // Vehicle API Actions @@ -2059,20 +2032,31 @@ export const actions = { }, }; - return globalMethods.callHttpClient({ + let hasCalled = timeSlotCallFlags.shop; + if (!hasCalled) { + timeSlotCallFlags.shop = true; + } + + const options = { method: endpoints.GetShopTimeSlots.method, endpoint: endpoints.GetShopTimeSlots.url, payload: payload, logApiCall: true, pageNameToLog: pageNameToLog, - additionalSuccessEventDataHandler: (response) => + }; + + // 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, shopAppointmentType - ), - }); + ); + } + + return globalMethods.callHttpClient(options); }, getMobileTimeSlots(context, { payload: { startDate, endDate }, pageNameToLog }) { @@ -2116,19 +2100,30 @@ export const actions = { zipCode: order.serviceLocation.zipCode, }; - return globalMethods.callHttpClient({ + let hasCalled = timeSlotCallFlags.mobile; + if (!hasCalled) { + timeSlotCallFlags.mobile = true; + } + + const options = { method: endpoints.GetMobileTimeSlots.method, endpoint: endpoints.GetMobileTimeSlots.url, payload: payload, logApiCall: true, pageNameToLog: pageNameToLog, - additionalSuccessEventDataHandler: (response) => + }; + + // 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 }) { @@ -3457,100 +3452,6 @@ export default createStore({ actions, }); -// Private Functions - -function getHasRecalibrationPart(state) { - return containsRecalParts(state.order.lineItems); -} - -function sortArrayOfObjectsByPropertyValue(arrayOfObjects, propertyName) { - if (!arrayOfObjects) return null; - - return arrayOfObjects.sort((a, b) => { - if (a[propertyName] < b[propertyName]) return -1; - else if (a[propertyName] > b[propertyName]) return 1; - else return 0; - }); -} - -function convertGlassPieceNamingForApi(glassArray) { - if (!glassArray || glassArray.length === 0) return []; - - // check if array already converted. (likely when a session has been saved previously and then reloaded) - if (glassArray[0].location !== undefined) { - return glassArray; - } - - const converted = []; - glassArray.forEach((glass) => { - converted.push({ - location: glass.glassLocation, - name: glass.glassName, - }); - }); - return converted; -} - -function convertResultsForApi(resultsArray) { - if (!resultsArray) return []; - const converted = []; - resultsArray.forEach((answer) => { - converted.push({ - location: answer.glassLocation, - name: answer.glassName, - result: answer.result, - }); - }); - return converted; -} - -function convertGlassPieceNamingFromApi(glassArray) { - glassArray.forEach((glass) => { - glass.glassLocation = glass.glassPiece.location; - glass.glassName = glass.glassPiece.name; - delete glass.glassPiece; - return glass; - }); - return glassArray; -} - -function addPricesToLineItems(lineItems, pricingLineItems) { - lineItems.forEach((lineItem) => { - const lineItemIndex = pricingLineItems.findIndex( - (pricingLineItem) => pricingLineItem.partNumber === lineItem.partNumber - ); - - if (lineItem.childParts) { - addPricesToLineItems(lineItem.childParts, pricingLineItems); - } - - const pricedLineItem = pricingLineItems.splice(lineItemIndex, 1)[0]; - lineItem.laborAmount = pricedLineItem.laborAmount; - lineItem.sellingPrice = pricedLineItem.sellingPrice; - lineItem.kitPrice = pricedLineItem.kitPrice; - lineItem.salesTax = pricedLineItem.salesTax; - }); - - return lineItems; -} - -function addTaxesToPricedLineItems(pricedLineItems, taxingLineItems = []) { - pricedLineItems.forEach((pricedLineItem) => { - const lineItemIndex = taxingLineItems.findIndex( - (taxingLineItem) => taxingLineItem.partNumber === pricedLineItem.partNumber - ); - - if (pricedLineItem.childParts) { - addTaxesToPricedLineItems(pricedLineItem.childParts, taxingLineItems); - } - - const taxedLineItem = taxingLineItems.splice(lineItemIndex, 1)[0]; - pricedLineItem.salesTax = taxedLineItem?.salesTax ?? 0; - }); - - return pricedLineItems; -} - export function mapTaxedLineItemsToStoreFormat(availableLineItems, storeLineItems) { // clone the lineItems array because what we're passing in is referencing the store directly const lineItems = deepClone(storeLineItems); @@ -3658,6 +3559,127 @@ export function getArrayOfAllLineItemsAndChildParts(lineItems) { return consolidatedLineItemsArray; } +// Private Functions + +function getNonFalseValuesOfPropertyInArrayOfObjects(array, propertyName) { + return (array ?? []).map((x) => x[propertyName]).filter((x) => x); +} + +function getTimeSlotsAdditionalEventData( + provisionalTriggers, + zipCode, + firstAvailableAppointmentDateString, + shopAppointmentType +) { + var numberOfDays = null; + if (firstAvailableAppointmentDateString) + numberOfDays = getDateDifferenceInDays( + new Date().toISOString().split("T")[0], + firstAvailableAppointmentDateString + ); + + if (shopAppointmentType) + return `FirstAvailableAppointment:${numberOfDays},Zip:${zipCode},ShopAppointmentType:${shopAppointmentType},ProvisionalTriggers:${provisionalTriggers.join( + "," + )}`; + else + return `FirstAvailableAppointment:${numberOfDays},Zip:${zipCode},ProvisionalTriggers:${provisionalTriggers.join( + "," + )}`; +} + +function getHasRecalibrationPart(state) { + return containsRecalParts(state.order.lineItems); +} + +function sortArrayOfObjectsByPropertyValue(arrayOfObjects, propertyName) { + if (!arrayOfObjects) return null; + + return arrayOfObjects.sort((a, b) => { + if (a[propertyName] < b[propertyName]) return -1; + else if (a[propertyName] > b[propertyName]) return 1; + else return 0; + }); +} + +function convertGlassPieceNamingForApi(glassArray) { + if (!glassArray || glassArray.length === 0) return []; + + // check if array already converted. (likely when a session has been saved previously and then reloaded) + if (glassArray[0].location !== undefined) { + return glassArray; + } + + const converted = []; + glassArray.forEach((glass) => { + converted.push({ + location: glass.glassLocation, + name: glass.glassName, + }); + }); + return converted; +} + +function convertResultsForApi(resultsArray) { + if (!resultsArray) return []; + const converted = []; + resultsArray.forEach((answer) => { + converted.push({ + location: answer.glassLocation, + name: answer.glassName, + result: answer.result, + }); + }); + return converted; +} + +function convertGlassPieceNamingFromApi(glassArray) { + glassArray.forEach((glass) => { + glass.glassLocation = glass.glassPiece.location; + glass.glassName = glass.glassPiece.name; + delete glass.glassPiece; + return glass; + }); + return glassArray; +} + +function addPricesToLineItems(lineItems, pricingLineItems) { + lineItems.forEach((lineItem) => { + const lineItemIndex = pricingLineItems.findIndex( + (pricingLineItem) => pricingLineItem.partNumber === lineItem.partNumber + ); + + if (lineItem.childParts) { + addPricesToLineItems(lineItem.childParts, pricingLineItems); + } + + const pricedLineItem = pricingLineItems.splice(lineItemIndex, 1)[0]; + lineItem.laborAmount = pricedLineItem.laborAmount; + lineItem.sellingPrice = pricedLineItem.sellingPrice; + lineItem.kitPrice = pricedLineItem.kitPrice; + lineItem.salesTax = pricedLineItem.salesTax; + }); + + return lineItems; +} + +function addTaxesToPricedLineItems(pricedLineItems, taxingLineItems = []) { + pricedLineItems.forEach((pricedLineItem) => { + const lineItemIndex = taxingLineItems.findIndex( + (taxingLineItem) => taxingLineItem.partNumber === pricedLineItem.partNumber + ); + + if (pricedLineItem.childParts) { + addTaxesToPricedLineItems(pricedLineItem.childParts, taxingLineItems); + } + + const taxedLineItem = taxingLineItems.splice(lineItemIndex, 1)[0]; + pricedLineItem.salesTax = taxedLineItem?.salesTax ?? 0; + }); + + return pricedLineItems; +} + function getFlattenedArrayOfLineItemsWithChildParts(lineItems, childPartRecursiveCall = false) { let flattenedArray = []; lineItems?.forEach((lineItem) => { @@ -3966,3 +3988,8 @@ function getExternalParameterDefaultState() { function saveExternalParameterState(externalParameterState) { window.sessionStorage.setItem("externalParameterState", JSON.stringify(externalParameterState)); } + +const timeSlotCallFlags = { + shop: false, + mobile: false, +}; diff --git a/src/styles/ux-variables.scss b/src/styles/ux-variables.scss index 1f17a6cf1..9acab9499 100644 --- a/src/styles/ux-variables.scss +++ b/src/styles/ux-variables.scss @@ -121,8 +121,8 @@ $body-color: $gray-600; //Fonts $font-family-sans-serif: AvertaRegular, Arial, Helvetica, sans-serif; -$font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", - monospace; +$font-family-monospace: + SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; // stylelint-enable value-keyword-case $font-family-base: $font-family-sans-serif; $font-family-code: $font-family-monospace; @@ -170,8 +170,7 @@ $spacers: ( /* 16px */ 5: $spacer * 1.5, /* 24px */ 6: $spacer * 2, /* 32px */ 7: $spacer * 2.5, - /* 40px */ 8: $spacer * 3, - /* 48px */ + /* 40px */ 8: $spacer * 3 /* 48px */, ); //Enable negative spacing (does NOT work on padding)