diff --git a/.eslintrc.js b/.eslintrc.js index 3ced6a6d..65c1caa3 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -56,4 +56,4 @@ module.exports = { } } } -}; +}; \ No newline at end of file diff --git a/azure-pipelines.yml b/azure-pipelines.yml index bcb4ec5f..0ad8e535 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -18,7 +18,7 @@ pr: resources: containers: - container: node - image: ghcr.io/safelite/node-build:18 + image: ghcr.io/safelite/node-build:20 endpoint: GitHub Docker - container: awscli image: ghcr.io/safelite/awscli2-build:3.9 diff --git a/src/constants/cart-item-type.js b/src/constants/cart-item-type.js index b3e1ecf4..017e2951 100644 --- a/src/constants/cart-item-type.js +++ b/src/constants/cart-item-type.js @@ -1,7 +1,8 @@ const cartItemType = Object.freeze({ RECYCLE_FEE: 'RECYCLE FEE', MOBILE_FEE: 'MOBILE FEE', - VAP: 'VAP' + VAP: 'VAP', + WARRANTY: 'WARRANTY' }); export default cartItemType; diff --git a/src/constants/endpoints.js b/src/constants/endpoints.js index c55410da..45bceb0a 100644 --- a/src/constants/endpoints.js +++ b/src/constants/endpoints.js @@ -81,7 +81,16 @@ const endpoints = Object.freeze({ method: 'POST' }, GetProviders: { - url: `${LOCATION_BASE_URL}/providers`, + url: ( + zipCode, + damageType, + radius, + parentAccountNumber, + safeliteOnly, + carId, + recalPartNumber, + isBigTruck + ) => `${LOCATION_BASE_URL}/providers/${zipCode}/${damageType}/${radius}/${parentAccountNumber}/${safeliteOnly}/${carId}/${recalPartNumber != null ? `${recalPartNumber}/` : ''}${isBigTruck}`, method: 'GET' }, GetCapabilityQuestions: { diff --git a/src/constants/part-type-strings.js b/src/constants/part-type-strings.js index 4876b999..babee9a8 100644 --- a/src/constants/part-type-strings.js +++ b/src/constants/part-type-strings.js @@ -7,7 +7,9 @@ RECALIBRATION: 'RECALIBRATION', REPLACE_FEE: 'REPLACE FEE', MOBILE_FEE: 'MOBILE FEE', - REPAIR_FEE: 'REPAIR FEE' + REPAIR_FEE: 'REPAIR FEE', + WARRANTY: 'WARRANTY', + MOLDING: 'MOULDING' }); export default partTypeStrings; diff --git a/src/helpers/damage-helper.js b/src/helpers/damage-helper.js index 5e886326..34bfc890 100644 --- a/src/helpers/damage-helper.js +++ b/src/helpers/damage-helper.js @@ -71,6 +71,26 @@ export async function isGlassAvailableForCarId(carId) { } } +export function getGlassList(glassPieces) { + let names = ''; + switch (glassPieces.length) + { + case 0: + break; + case 1: + names = glassPieces[0]; + break; + case 2: + names = glassPieces.join(' and '); + break; + default: + names = glassPieces.slice(0, -1).join(', ') + ', and ' + glassPieces.slice(-1); + break; + } + + return names.toLowerCase(); +} + /** * Commented code are copied directly from DigitalConsumer.FixMyGlass * and have not been adjusted for ISS. diff --git a/src/iss-components/cart-dropdown/__snapshots__/cart-dropdown.spec.js.snap b/src/iss-components/cart-dropdown/__snapshots__/cart-dropdown.spec.js.snap index 8f41a38c..2f061236 100644 --- a/src/iss-components/cart-dropdown/__snapshots__/cart-dropdown.spec.js.snap +++ b/src/iss-components/cart-dropdown/__snapshots__/cart-dropdown.spec.js.snap @@ -7,19 +7,21 @@ Object { "MOBILE_FEE": "MOBILE FEE", "RECYCLE_FEE": "RECYCLE FEE", "VAP": "VAP", + "WARRANTY": "WARRANTY", }, - "isExpanded": false, "widget": Object { "amountDue": "AmountDueTextWidget", "amountPaid": "AmountPaidTextWidget", "basePrice": "BasePriceWidget", "deductible": "DeductibleWidget", + "guaranteeText": "GuaranteeCartItemTextWidget", "mobileFee": "MobileServiceWidget", "recycleFee": "RecycleFeeWidget", "salesTax": "SalesTaxWidget", "servicePackage": "ServicePackageTitle", "subtotal": "SubtotalWidget", "vapsItemDescriptions": "VapsItemDescriptions", + "warrantyText": "WarrantyCartItemTextWidget", }, } `; diff --git a/src/iss-components/cart-dropdown/cart-dropdown.spec.js b/src/iss-components/cart-dropdown/cart-dropdown.spec.js index bab952b4..69fbabb9 100644 --- a/src/iss-components/cart-dropdown/cart-dropdown.spec.js +++ b/src/iss-components/cart-dropdown/cart-dropdown.spec.js @@ -73,47 +73,10 @@ describe('cart-dropdown component', () => { expect(wrapper.vm.$data).toMatchSnapshot(); }); describe('displays', () => { - test('cart dropdown head', () => { - // Arrange - const reference = '#cart-dropdown-head'; - const { wrapper } = getMountedComponent(cartDropdown); - - // Act - const head = wrapper.find(reference); - - // Assert - expect(head.exists()).toBeTruthy(); - }); - test('cart dropdown head is visible', () => { - // Arrange - const reference = '#cart-dropdown-head'; - const { wrapper } = getMountedComponent(cartDropdown, {}, { showDropdownHeader: true }); - - // Act - const head = wrapper.find(reference); - - // Assert - expect(head.exists()).toBeTruthy(); - expect(head.classes()).toContain('cart-toggle'); - }); - test('cart dropdown head is not visible', () => { - // Arrange - const reference = '#cart-dropdown-head'; - const { wrapper } = getMountedComponent(cartDropdown, {}, { showDropdownHeader: false }); - - // Act - const head = wrapper.find(reference); - - // Assert - expect(head.exists()).toBeTruthy(); - expect(head.classes()).not.toContain('cart-toggle'); - }); test('cart table', () => { // Arrange - const reference = '#cart-table'; - const isExpanded = true; - const initialData = { isExpanded }; - const { wrapper } = getMountedComponent({}, {}, initialData); + const reference = '.cart-table'; + const { wrapper } = getMountedComponent({}, {}, {}); // Act const cartTable = wrapper.find(reference); @@ -121,11 +84,9 @@ describe('cart-dropdown component', () => { // Assert expect(cartTable.exists()).toBeTruthy(); }); - test('cart deductible when showDeductibleLineItem is true', () => { + test('cart deductible when showDeductibleCartItem is true', () => { // Arrange - const reference = '#cart-deductible'; - const isExpanded = true; - const initialData = { isExpanded }; + const reference = '#deductible-label'; const storeData = { order: { insuranceCoverage: { @@ -134,7 +95,7 @@ describe('cart-dropdown component', () => { } } }; - const { wrapper } = getMountedComponent(storeData, {}, initialData); + const { wrapper } = getMountedComponent(storeData, {}, {}); // Act const cartDeductible = wrapper.find(reference); @@ -144,9 +105,7 @@ describe('cart-dropdown component', () => { }); test('cart base price when showDeductibleLineItem is false', () => { // Arrange - const reference = '#cart-base-price'; - const isExpanded = true; - const initialData = { isExpanded }; + const reference = '#base-price-label'; const storeData = { order: { currentDeductible: 0, @@ -160,7 +119,7 @@ describe('cart-dropdown component', () => { isClaimRegistrationRequired: true } }; - const { wrapper } = getMountedComponent(storeData, {}, initialData); + const { wrapper } = getMountedComponent(storeData, {}, {}); // Act const cartBasePrice = wrapper.find(reference); @@ -168,25 +127,32 @@ describe('cart-dropdown component', () => { // Assert expect(cartBasePrice.exists()).toBeTruthy(); }); - test('service package', () => { - // Arrange - const reference = '#cart-service-package'; - const isExpanded = true; - const initialData = { isExpanded }; - const { wrapper } = getMountedComponent({}, {}, initialData); - - // Act - const servicePackage = wrapper.find(reference); - - // Assert - expect(servicePackage.exists()).toBeTruthy(); - }); test('non service package cart items', () => { // Arrange - const reference = '#non-packaged-cart-items'; - const isExpanded = true; - const initialData = { isExpanded }; - const { wrapper } = getMountedComponent({}, {}, initialData); + const reference = '.non-packaged.cart-item'; + const part1 = { partType: 'apple' }; + const part2 = { partType: 'banana' }; + const part3 = { partType: 'orange' }; + const mobileFee = { partType: 'mobile' }; + const storeData = { + order: { + currentDeductible: 0, + lineItems: { + glassParts: [part1], + supportingItems: [part2, part3], + otherParts: [], + feeItems: [mobileFee] + }, + insuranceCoverage: { + coverageStatus: coverageStatuses.VERIFIED, + coverageType: coverageType.ITAC + } + }, + issConfig: { + isClaimRegistrationRequired: true + } + }; + const { wrapper } = getMountedComponent(storeData, {}, {}); // Act const nonPackagedCartItems = wrapper.find(reference); @@ -220,9 +186,7 @@ describe('cart-dropdown component', () => { test('cart footer', () => { // Arrange const reference = '#cart-footer'; - const isExpanded = true; - const initialData = { isExpanded }; - const { wrapper } = getMountedComponent({}, {}, initialData); + const { wrapper } = getMountedComponent({}, {}, {}); // Act const cartFooter = wrapper.find(reference); @@ -233,9 +197,7 @@ describe('cart-dropdown component', () => { test('subtotal', () => { // Arrange const reference = '#cart-subtotal'; - const isExpanded = true; - const initialData = { isExpanded }; - const { wrapper } = getMountedComponent({}, {}, initialData); + const { wrapper } = getMountedComponent({}, {}, {}); // Act const subtotal = wrapper.find(reference); @@ -246,9 +208,7 @@ describe('cart-dropdown component', () => { test('tax', () => { // Arrange const reference = '#cart-sales-tax'; - const isExpanded = true; - const initialData = { isExpanded }; - const { wrapper } = getMountedComponent({}, {}, initialData); + const { wrapper } = getMountedComponent({}, {}, {}); // Act const salesTax = wrapper.find(reference); @@ -280,9 +240,7 @@ describe('cart-dropdown component', () => { test('bottom amount due', () => { // Arrange const reference = '#bottom-amount-due'; - const isExpanded = true; - const initialData = { isExpanded }; - const { wrapper } = getMountedComponent({}, {}, initialData); + const { wrapper } = getMountedComponent({}, {}, {}); // Act const bottomAmountDue = wrapper.find(reference); @@ -295,8 +253,6 @@ describe('cart-dropdown component', () => { test('cart deductible when showDeductibleLineItem is false', () => { // Arrange const reference = '#cart-deductible'; - const isExpanded = true; - const initialData = { isExpanded }; const storeData = { order: { currentDeductible: 0, @@ -310,7 +266,7 @@ describe('cart-dropdown component', () => { isClaimRegistrationRequired: true } }; - const { wrapper } = getMountedComponent(storeData, {}, initialData); + const { wrapper } = getMountedComponent(storeData, {}, {}); // Act const cartDeductible = wrapper.find(reference); @@ -321,8 +277,6 @@ describe('cart-dropdown component', () => { test('cart base price when showDeductibleLineItem is true', () => { // Arrange const reference = '#cart-base-price'; - const isExpanded = true; - const initialData = { isExpanded }; const storeData = { order: { insuranceCoverage: { @@ -331,7 +285,7 @@ describe('cart-dropdown component', () => { } } }; - const { wrapper } = getMountedComponent(storeData, {}, initialData); + const { wrapper } = getMountedComponent(storeData, {}, {}); // Act const cartBasePrice = wrapper.find(reference); @@ -1223,91 +1177,6 @@ describe('cart-dropdown component', () => { expect(result.name).toBe(expectedName); }); }); - describe('packagePrice', () => { - test('returns 0 when servicePackageCartItems is empty', () => { - // Arrange - getPriceOfLineItems.mockImplementation(() => 123); - getPackageContents.mockImplementationOnce(() => []); - const storeData = { - order: { - lineItems: { - vaps: [ - { - partType: partTypeStrings.REAR_WIPER - } - ] - } - } - }; - const { wrapper } = getMountedComponent(storeData); - - // Act - const result = wrapper.vm.packagePrice; - - // Assert - expect(result).toBe(0); - }); - test('returns expected when servicePackageCartItems not empty', () => { - // Arrange - const rearPrice = 101; - const frontPrice = 9; - getPriceOfLineItems.mockImplementation((lineItems) => { - if (lineItems.some((item) => item?.partType === partTypeStrings.FRONT_WIPER)) { - return frontPrice; - } - if (lineItems.some((item) => item?.partType === partTypeStrings.REAR_WIPER)) { - return rearPrice; - } - return 1; - }); - getPackageContents.mockImplementationOnce(() => [partTypeStrings.FRONT_WIPER, partTypeStrings.REAR_WIPER]); - const storeData = { - order: { - lineItems: { - vaps: [ - { partType: partTypeStrings.FRONT_WIPER }, - { partType: partTypeStrings.REAR_WIPER } - ] - } - } - }; - const { wrapper } = getMountedComponent(storeData); - const expectedPrice = 110; - - // Act - const result = wrapper.vm.packagePrice; - - // Assert - expect(result).toBe(expectedPrice); - }); - test('returns expected when servicePackageCartItems has one item', () => { - // Arrange - const frontPrice = 9; - getPriceOfLineItems.mockImplementation((lineItems) => { - if (lineItems.some((item) => item?.partType === partTypeStrings.FRONT_WIPER)) { - return frontPrice; - } - return 1; - }); - getPackageContents.mockImplementationOnce(() => [partTypeStrings.FRONT_WIPER, partTypeStrings.REAR_WIPER]); - const storeData = { - order: { - lineItems: { - vaps: [ - { partType: partTypeStrings.FRONT_WIPER } - ] - } - } - }; - const { wrapper } = getMountedComponent(storeData); - - // Act - const result = wrapper.vm.packagePrice; - - // Assert - expect(result).toBe(frontPrice); - }); - }); }); describe('method', () => { const dollarAmount = '$84.00'; diff --git a/src/iss-components/cart-dropdown/cart-dropdown.vue b/src/iss-components/cart-dropdown/cart-dropdown.vue index 34788563..da404697 100644 --- a/src/iss-components/cart-dropdown/cart-dropdown.vue +++ b/src/iss-components/cart-dropdown/cart-dropdown.vue @@ -1,156 +1,113 @@ @@ -176,28 +133,26 @@ import { isOrderUnverified } from '@/helpers/cart-helper'; import { getPriceOfLineItems, getTaxOfLineItems } from '@/helpers/price-calculator'; +import { processIfStatements } from '@/helpers/cms-content-helper'; const VERIFYING_COVERAGE = 'Verifying coverage'; const RECYCLING_MODAL_REF_NAME = 'RecycleModal'; export default { - name: 'cart-dropdown', + name: 'confirmation-cart', components: { contentGroupModal, textBlock, textLink }, props: { - showAsPaid: Boolean, readOnly: Boolean, + showAsPaid: Boolean, recyclingModalCmsWidgetName: String, - showDropdownHeader: Boolean, - isInitiallyExpanded: Boolean, submittedOrder: Object }, data() { return { - isExpanded: this.isInitiallyExpanded, widget: { amountDue: 'AmountDueTextWidget', amountPaid: 'AmountPaidTextWidget', @@ -208,7 +163,9 @@ export default { recycleFee: 'RecycleFeeWidget', mobileFee: 'MobileServiceWidget', servicePackage: 'ServicePackageTitle', - vapsItemDescriptions: 'VapsItemDescriptions' + vapsItemDescriptions: 'VapsItemDescriptions', + warrantyText: 'WarrantyCartItemTextWidget', + guaranteeText: 'GuaranteeCartItemTextWidget' }, cartItemType, RECYCLING_MODAL_REF_NAME @@ -273,7 +230,7 @@ export default { return result; }, vehicleDamage() { - return this.submittedOrder ? this.submittedOrder.damage : useMainStore().damage; + return this.cartOrder.damage; }, servicePackageTier() { const { glassToReplace, isRepair } = this.vehicleDamage; @@ -333,6 +290,9 @@ export default { if (this.mobileFeeCartItem && !isMobileFeeHidden) { items.push(this.mobileFeeCartItem); } + if (this.isITAC || this.isNoComp) { + items.push(this.warrantyCartItem); + } return items; }, frontWipersCartItem() { @@ -350,12 +310,6 @@ export default { ...this.nonServicePackageCartItems ]; }, - packagePrice() { - return this.servicePackageCartItems?.reduce( - (accumulator, cartItem) => accumulator + (cartItem?.subTotal ?? 0), - 0 - ) ?? 0; - }, amountDueLabel() { return this.getCmsContent(this.widget.amountDue, widgetFields.TEXT_BLOCK_WIDGET.TEXT); }, @@ -366,7 +320,12 @@ export default { return this.getCmsContent(this.widget.deductible, widgetFields.TEXT_BLOCK_WIDGET.TEXT); }, basePriceLabel() { - return this.getCmsContent(this.widget.basePrice, widgetFields.TEXT_BLOCK_WIDGET.TEXT); + const content = this.getCmsContent(this.widget.basePrice, widgetFields.TEXT_BLOCK_WIDGET.TEXT); + return processIfStatements( + content, + 'custom', + this.getCustomValueFromString + ); }, subtotalLabel() { return this.getCmsContent(this.widget.subtotal, widgetFields.TEXT_BLOCK_WIDGET.TEXT); @@ -409,15 +368,29 @@ export default { partTypeStrings.MOBILE_FEE ) : null; + }, + warrantyCartItem() { + const label = this.cartOrder.damage.glassToReplace?.length > 0 + ? this.getCmsContent(this.widget.warrantyText, widgetFields.TEXT_BLOCK_WIDGET.TEXT) + : this.getCmsContent(this.widget.guaranteeText, widgetFields.TEXT_BLOCK_WIDGET.TEXT); + return { + name: label, + cartItemType: cartItemType.WARRANTY, + partType: partTypeStrings.WARRANTY, + subTotal: 0, + salesTax: 0 + } + }, + hasMoldingPart() { + return this.cartOrder.lineItems.glassParts?.some((glassPart) => { + return glassPart.childParts?.some((childPart) => childPart.partType === partTypeStrings.MOLDING); + }); } }, methods: { openModal(modalName) { this.$refs[modalName].openModal(); }, - toggleIsExpanded() { - this.isExpanded = !this.isExpanded; - }, getDisplayed(amount) { return this.isUnverified ? VERIFYING_COVERAGE @@ -461,6 +434,16 @@ export default { const vapsTypeDescription = vapsItemDescriptions?.find((entry) => entry?.Name === vapsPartType); return vapsTypeDescription?.Text ?? ''; }, + getCustomValueFromString(str) { + switch (str) { + case 'isRepair': + return this.cartOrder.damage.isRepair; + case 'hasMoldingPart': + return this.hasMoldingPart; + default: + return null; + } + }, formatAmountInDollars } }; @@ -471,45 +454,38 @@ export default { $RECYCLING_MODAL_REF_NAME: 'RecycleModal'; -.force-no-top-margin { - margin-top: 0 !important; -} - -.color-black { - color: $black; -} - -.color-green { - color: $green; -} - -.color-darker-gray { - color: $darker-gray; -} - .cart-table { - max-height: 0; - transition: all 350ms ease-in; - overflow: hidden; - visibility: hidden; - margin-top: 1rem; - - :deep(a) { - padding-bottom: 0 !important; - line-height: 1.625rem !important; - text-underline-offset: auto !important; - } - - .non-packaged-cart-item:nth-child(odd){ - background-color: $gray-100; - } + max-height: 50rem; + transition: all 150ms ease-in; + min-width: fit-content; .cart-item { - padding: 0.25rem 1.5rem; - } + display: flex; + flex-direction: column; + padding: .5rem 0rem; + width: 100%; + min-width: fit-content; + &:nth-child(odd) { + background-color: #f4f4f4; + } + + &:last-child { + border-bottom: 1px solid #d4d6d8; + } - .cart-gray { - background-color: $gray-100; + .price-row { + display: flex; + justify-content: space-between; + } + + span, a { + white-space: nowrap; + padding: 0rem 1rem; + } + + a:hover { + color: $heritage-blue-secondary; + } } .cart-footer { @@ -520,13 +496,16 @@ $RECYCLING_MODAL_REF_NAME: 'RecycleModal'; background-color: $green-150; font-weight: $font-weight-bold; color: $black; - padding: 0.25rem 1.5rem; + >div { + padding: .5rem 1rem; + } } .bottom-amount-due { color: $white; + font-weight: 600; background-color: $green; - padding: 0.5rem 1.5rem; + padding: .5rem 1rem; } } @@ -544,39 +523,6 @@ $RECYCLING_MODAL_REF_NAME: 'RecycleModal'; text-align: center; } -.cart-header { - &.cart-toggle { - font-weight: $font-weight-bold; - - &:after { - content: ""; - transition: all 0.5s ease; - background-image: url($svg-payment-method-review-toggle); - background-repeat: no-repeat; - background-position: right center; - width: 1rem; - height: 0.5625rem; - display: inline-flex; - position: relative; - right: 0.75rem; - margin: 0.5rem 0 0.5rem 1rem; - cursor: pointer; - } - &.expanded:after { - transform: rotate(180deg); - } - a { - text-decoration: none; - } - } - &.expanded + .cart-table { - max-height: 50rem; - transition: all 150ms ease-in; - overflow: hidden; - visibility: visible; - } -} - :deep(#recycle-fee-label a){ line-height: initial; } diff --git a/src/layouts/order-confirmation/add-to-calendar/add-to-calendar.spec.js b/src/layouts/order-confirmation/add-to-calendar/add-to-calendar.spec.js index 92914c3e..285cf934 100644 --- a/src/layouts/order-confirmation/add-to-calendar/add-to-calendar.spec.js +++ b/src/layouts/order-confirmation/add-to-calendar/add-to-calendar.spec.js @@ -2,11 +2,10 @@ import calendarOptions from '@/constants/calendar-options'; // Supporting Files -import { shallowMount, mount } from '@vue/test-utils'; +import { shallowMount } from '@vue/test-utils'; import { getMountOptions } from '@/helpers/unit-test-helper.js'; import { useMainStore } from '@/store'; import addToCalendar from '@/layouts/order-confirmation/add-to-calendar/add-to-calendar.vue'; -import { createTestingPinia } from '@pinia/testing'; const appointmentText = 'Appointment content'; function setupMocks({ customMountOptions }) { @@ -21,49 +20,6 @@ function setupMocks({ customMountOptions }) { } ]; const wrapper = shallowMount(addToCalendar, mountOptions); - wrapper.vm.$refs.calendarModalQuestion.openModal = jest.fn(); - return { wrapper }; -} - -const calendarModalQuestionStub = { - render: () => {} -}; - -const mockMixin = { - methods: { - getCmsContent: jest.fn().mockImplementation(() => appointmentText) - } -}; - -function getMountedComponent(mainInitialState = {}, initialData = {}, methodToRun = () => {}, mixin = mockMixin) { - const mountOptions = getMountOptions({ - router: { - navigate: jest.fn(), - navigateToExternalUrl: jest.fn() - } - }); - - mountOptions.global.stubs = { - calendarModalQuestion: calendarModalQuestionStub - }; - - const testingPinia = createTestingPinia({ - initialState: { - main: mainInitialState - } - }); - useMainStore(testingPinia); - methodToRun(); - - mountOptions.global.mixins[0].methods.getSettingValue = jest.fn(() => 'false'); - mountOptions.global.plugins = [testingPinia]; - mountOptions.mixins = [mixin]; - mountOptions.data = () => ( - initialData - ); - - const wrapper = shallowMount(addToCalendar, mountOptions); - wrapper.vm.$refs.calendarModalQuestion.openModal = jest.fn(); return { wrapper }; } @@ -119,51 +75,8 @@ afterEach(() => { jest.clearAllMocks(); }); -const sessionStorage = { - schedule: { - date: '2019-01-01', - startTime: '09:00', - endTime: '10:00', - routeCode: '000' - }, - lineItems: { - glassParts: [ - { - partNumber: 'ABC123' - } - ], - supportingItems: [] - }, - serviceLocation: { - address: '', - address2: null, - city: '', - state: 'AZ', - appointmentType: 'Inshop', - zipCode: '12345', - zipCodeCtu: '01234', - provider: { - providerNumber: '123', - address: { - streetAddress: 'test1', - city: 'test', - state: 'AZ', - zipCode: '12345', - zipCodeCtu: '01234' - } - } - }, - damage: { - isRepair: false - }, - referralNumber: '1234567', - payment: { - isInsurance: true - } -}; - describe('Add-to-calendar methods...', () => { - test('Add-to-calendar should trigger openModal method', () => { + test('Add-to-calendar should trigger open options', async () => { // Arrange const { wrapper } = setupMocks({ customMountOptions: { @@ -183,10 +96,11 @@ describe('Add-to-calendar methods...', () => { }); // Act - wrapper.vm.openCalendarModal(); + wrapper.vm.openCalendarOptions(); + await wrapper.vm.$nextTick(); // Assert - expect(wrapper.vm.$refs.calendarModalQuestion.openModal).toBeCalled(); + expect(wrapper.vm.$refs.calendarOptions.open).toEqual(true); }); test('getCalendarData should return expected model value.', () => { diff --git a/src/layouts/order-confirmation/add-to-calendar/add-to-calendar.vue b/src/layouts/order-confirmation/add-to-calendar/add-to-calendar.vue index 502337a6..284a34e6 100644 --- a/src/layouts/order-confirmation/add-to-calendar/add-to-calendar.vue +++ b/src/layouts/order-confirmation/add-to-calendar/add-to-calendar.vue @@ -1,7 +1,7 @@ - diff --git a/src/layouts/order-confirmation/add-to-calendar/calendar-modal-question/calendar-modal-question.spec.js b/src/layouts/order-confirmation/add-to-calendar/calendar-modal-question/calendar-modal-question.spec.js deleted file mode 100644 index 51b43508..00000000 --- a/src/layouts/order-confirmation/add-to-calendar/calendar-modal-question/calendar-modal-question.spec.js +++ /dev/null @@ -1,41 +0,0 @@ -import { shallowMount } from '@vue/test-utils'; -import calendarModalQuestion from '@/layouts/order-confirmation/add-to-calendar/calendar-modal-question/calendar-modal-question.vue'; - -describe('calendar-modal-question.vue', () => { - test('When selected calendar option is changed, a correctly selectedCalendarOption should be emitted', async () => { - // Arrange - const url = 'iCal url'; - const name = 'iCal'; - const icon = 'iCal.svg'; - const expectedEmit = [ - [ - { - url, - name, - icon - } - ] - ]; - - const wrapper = shallowMount(calendarModalQuestion, { - propsData: { - calendarOptionsData: [ - { - url, - name, - icon - } - ] - } - }); - - wrapper.vm.$refs.calendarOptions.closeModal = jest.fn(); - wrapper.vm.selectedCalendarOption = name; - - // Act - wrapper.vm.setSelectedCalendarOption(); - - // Assert - expect(wrapper.emitted()['update:modelValue']).toEqual(expectedEmit); - }); -}); diff --git a/src/layouts/order-confirmation/add-to-calendar/calendar-modal-question/calendar-modal-question.vue b/src/layouts/order-confirmation/add-to-calendar/calendar-modal-question/calendar-modal-question.vue deleted file mode 100644 index 702abf1b..00000000 --- a/src/layouts/order-confirmation/add-to-calendar/calendar-modal-question/calendar-modal-question.vue +++ /dev/null @@ -1,180 +0,0 @@ - - - diff --git a/src/layouts/order-confirmation/add-to-calendar/calendar-option-selection/calendar-option-button/calendar-option-button.vue b/src/layouts/order-confirmation/add-to-calendar/calendar-option-selection/calendar-option-button/calendar-option-button.vue new file mode 100644 index 00000000..760e3a7a --- /dev/null +++ b/src/layouts/order-confirmation/add-to-calendar/calendar-option-selection/calendar-option-button/calendar-option-button.vue @@ -0,0 +1,41 @@ + + + diff --git a/src/layouts/order-confirmation/add-to-calendar/calendar-option-selection/calendar-option-selection.spec.js b/src/layouts/order-confirmation/add-to-calendar/calendar-option-selection/calendar-option-selection.spec.js new file mode 100644 index 00000000..a833b369 --- /dev/null +++ b/src/layouts/order-confirmation/add-to-calendar/calendar-option-selection/calendar-option-selection.spec.js @@ -0,0 +1,42 @@ +import { shallowMount } from '@vue/test-utils'; +import calendarOptionSelection from '@/layouts/order-confirmation/add-to-calendar/calendar-option-selection/calendar-option-selection.vue'; + +describe('calendar-option-selection.vue', () => { + test('When selected calendar option is changed, a correctly selectedCalendarOption should be emitted', async () => { + // Arrange + const url = 'iCal url'; + const name = 'iCal'; + const icon = 'iCal.svg'; + const url2 = 'Google url'; + const name2 = 'Google'; + const icon2 = 'Google.svg'; + const expectedEmit = [ + [ + 1 + ] + ]; + + const wrapper = shallowMount(calendarOptionSelection, { + propsData: { + calendarOptionsData: [ + { + url, + name, + icon + }, + { + url: url2, + name: name2, + icon: icon2 + } + ] + } + }); + + // Act + wrapper.vm.setSelectedCalendarOption(1); + + // Assert + expect(wrapper.emitted()['calendar-option-selected']).toEqual(expectedEmit); + }); +}); diff --git a/src/layouts/order-confirmation/add-to-calendar/calendar-option-selection/calendar-option-selection.vue b/src/layouts/order-confirmation/add-to-calendar/calendar-option-selection/calendar-option-selection.vue new file mode 100644 index 00000000..1c95c555 --- /dev/null +++ b/src/layouts/order-confirmation/add-to-calendar/calendar-option-selection/calendar-option-selection.vue @@ -0,0 +1,53 @@ + + + + diff --git a/src/layouts/order-confirmation/order-confirmation.spec.js b/src/layouts/order-confirmation/order-confirmation.spec.js index 6a06885a..be64fdc8 100644 --- a/src/layouts/order-confirmation/order-confirmation.spec.js +++ b/src/layouts/order-confirmation/order-confirmation.spec.js @@ -206,6 +206,7 @@ describe('OrderConfirmation.vue', () => { useMainStore().getSubmittedOrder = jest.fn().mockImplementation(() => sessionStorage); }; const { wrapper } = getMountedComponent(initialStore, {}, mockStoreActions); + console.log(wrapper.vm.$refs); // Act const arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid(); @@ -363,7 +364,7 @@ describe('OrderConfirmation.vue', () => { expect(actualEmail).toBe(emailAddress); }); }); - describe('confirmationEmailText', () => { + describe('orderConfirmationUpdateAppointmentText', () => { const greaterId = '>'; const lessId = '<'; test.each([ @@ -383,7 +384,7 @@ describe('OrderConfirmation.vue', () => { const { wrapper } = getMountedComponent(initialStore, {}, mockStoreActions); // Act - const text = wrapper.vm.confirmationEmailText; + const text = wrapper.vm.orderConfirmationUpdateAppointmentText; // Assert expect(text).toBe(expected); @@ -391,13 +392,13 @@ describe('OrderConfirmation.vue', () => { }); describe('appointmentTimeText', () => { test.each([ - ['Between 9 AM - 10 AM', '09:00', '10:00'], - ['Between 1 PM - 5 PM', '13:00', '17:00'], - ['Between 8:30 AM - 3 PM', '08:30', '15:00'], - ['Between 1 AM - 2 AM', '01:00', '02:00'], - ['Between 12 AM - 2 AM', '00:00', '02:00'], - ['Between 12 PM - 2 AM', '12:00', '02:00'], - ['Between 11 PM - 12 AM', '23:00', '00:00'] + ['Your appointment is at 9 AM - 10 AM', '09:00', '10:00'], + ['Your appointment is at 1 PM - 5 PM', '13:00', '17:00'], + ['Your appointment is at 8:30 AM - 3 PM', '08:30', '15:00'], + ['Your appointment is at 1 AM - 2 AM', '01:00', '02:00'], + ['Your appointment is at 12 AM - 2 AM', '00:00', '02:00'], + ['Your appointment is at 12 PM - 2 AM', '12:00', '02:00'], + ['Your appointment is at 11 PM - 12 AM', '23:00', '00:00'] ])('should return Mobile time in expected format "%p" when start time "%p" and end time "%p"', (expected, startTime, endTime) => { // Arrange const order = deepClone(sessionStorage); @@ -438,12 +439,12 @@ describe('OrderConfirmation.vue', () => { expect(testValue).toEqual('Drop off before 9:30 AM'); }); test.each([ - ['at 9:00 AM', '09:00', '10:00'], - ['at 1:00 PM', '13:00', '17:00'], - ['at 8:30 AM', '08:30', '15:00'], - ['at 1:00 AM', '01:00', '02:00'], - ['at 12:00 AM', '00:00', '02:00'], - ['at 12:00 PM', '12:00', '02:00'] + ['Your appointment is at 9:00 AM', '09:00', '10:00'], + ['Your appointment is at 1:00 PM', '13:00', '17:00'], + ['Your appointment is at 8:30 AM', '08:30', '15:00'], + ['Your appointment is at 1:00 AM', '01:00', '02:00'], + ['Your appointment is at 12:00 AM', '00:00', '02:00'], + ['Your appointment is at 12:00 PM', '12:00', '02:00'] ])('should return In Shop time in expected format "%p" when start time "%p"', (expected, startTime, endTime) => { // Arrange const order = deepClone(sessionStorage); @@ -479,8 +480,8 @@ describe('OrderConfirmation.vue', () => { } } }; - const expectedServiceLocation = '123 Service Rd., Box 4,
Columbus, OH 12345'; - const expectedShopLocation = '304 Provider Ln.,
Pittsburg, PA 16001'; + const expectedServiceLocation = '123 Service Rd., Box 4, Columbus, OH 12345'; + const expectedShopLocation = '304 Provider Ln., Pittsburg, PA 16001'; test.each([ AppointmentTypeStrings.MOBILE, AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP @@ -554,7 +555,7 @@ describe('OrderConfirmation.vue', () => { useMainStore().getSubmittedOrder = jest.fn().mockImplementation(() => order); }; const { wrapper } = getMountedComponent({}, {}, mockStoreActions); - const expected = '123 Service Rd., Box 4,
Columbus, OH 12345'; + const expected = '123 Service Rd., Box 4, Columbus, OH 12345'; // Act const text = wrapper.vm.serviceLocationFullAddress; @@ -568,7 +569,7 @@ describe('OrderConfirmation.vue', () => { useMainStore().getSubmittedOrder = jest.fn().mockImplementation(() => order); }; const { wrapper } = getMountedComponent({}, {}, mockStoreActions); - const expected = ', Box 4,
Columbus, OH 12345'; + const expected = ', Box 4, Columbus, OH 12345'; // Act const text = wrapper.vm.serviceLocationFullAddress; @@ -582,7 +583,7 @@ describe('OrderConfirmation.vue', () => { useMainStore().getSubmittedOrder = jest.fn().mockImplementation(() => order); }; const { wrapper } = getMountedComponent({}, {}, mockStoreActions); - const expected = '123 Service Rd.,
Columbus, OH 12345'; + const expected = '123 Service Rd., Columbus, OH 12345'; // Act const text = wrapper.vm.serviceLocationFullAddress; @@ -596,7 +597,7 @@ describe('OrderConfirmation.vue', () => { useMainStore().getSubmittedOrder = jest.fn().mockImplementation(() => order); }; const { wrapper } = getMountedComponent({}, {}, mockStoreActions); - const expected = '123 Service Rd., Box 4,
, OH 12345'; + const expected = '123 Service Rd., Box 4, , OH 12345'; // Act const text = wrapper.vm.serviceLocationFullAddress; @@ -610,7 +611,7 @@ describe('OrderConfirmation.vue', () => { useMainStore().getSubmittedOrder = jest.fn().mockImplementation(() => order); }; const { wrapper } = getMountedComponent({}, {}, mockStoreActions); - const expected = '123 Service Rd., Box 4,
Columbus, 12345'; + const expected = '123 Service Rd., Box 4, Columbus, 12345'; // Act const text = wrapper.vm.serviceLocationFullAddress; @@ -624,7 +625,7 @@ describe('OrderConfirmation.vue', () => { useMainStore().getSubmittedOrder = jest.fn().mockImplementation(() => order); }; const { wrapper } = getMountedComponent({}, {}, mockStoreActions); - const expected = '123 Service Rd., Box 4,
Columbus, OH '; + const expected = '123 Service Rd., Box 4, Columbus, OH '; // Act const text = wrapper.vm.serviceLocationFullAddress; @@ -642,7 +643,7 @@ describe('OrderConfirmation.vue', () => { useMainStore().getSubmittedOrder = jest.fn().mockImplementation(() => order); }; const { wrapper } = getMountedComponent({}, {}, mockStoreActions); - const expected = ',
, '; + const expected = ', , '; // Act const text = wrapper.vm.serviceLocationFullAddress; @@ -675,7 +676,7 @@ describe('OrderConfirmation.vue', () => { useMainStore().getSubmittedOrder = jest.fn().mockImplementation(() => order); }; const { wrapper } = getMountedComponent({}, {}, mockStoreActions); - const expected = '304 Provider Ln.,
Pittsburg, PA 16001'; + const expected = '304 Provider Ln., Pittsburg, PA 16001'; // Act const text = wrapper.vm.providerFullAddress; @@ -703,7 +704,7 @@ describe('OrderConfirmation.vue', () => { useMainStore().getSubmittedOrder = jest.fn().mockImplementation(() => order); }; const { wrapper } = getMountedComponent({}, {}, mockStoreActions); - const expected = '304 Provider Ln.,
, PA 16001'; + const expected = '304 Provider Ln., , PA 16001'; // Act const text = wrapper.vm.providerFullAddress; @@ -717,7 +718,7 @@ describe('OrderConfirmation.vue', () => { useMainStore().getSubmittedOrder = jest.fn().mockImplementation(() => order); }; const { wrapper } = getMountedComponent({}, {}, mockStoreActions); - const expected = '304 Provider Ln.,
Pittsburg, 16001'; + const expected = '304 Provider Ln., Pittsburg, 16001'; // Act const text = wrapper.vm.providerFullAddress; @@ -731,7 +732,7 @@ describe('OrderConfirmation.vue', () => { useMainStore().getSubmittedOrder = jest.fn().mockImplementation(() => order); }; const { wrapper } = getMountedComponent({}, {}, mockStoreActions); - const expected = '304 Provider Ln.,
Pittsburg, PA '; + const expected = '304 Provider Ln., Pittsburg, PA '; // Act const text = wrapper.vm.providerFullAddress; @@ -956,9 +957,9 @@ describe('OrderConfirmation.vue', () => { describe('Methods', () => { describe('formatDate', () => { test.each([ - ['Wednesday, April 22', '2020-04-22'], - ['Thursday, February 1', '2018-02-01'], - ['Wednesday, December 30', '2026-12-30'] + ['Wednesday, April 22, 2020', '2020-04-22'], + ['Thursday, February 1, 2018', '2018-02-01'], + ['Wednesday, December 30, 2026', '2026-12-30'] ])('returns %p given %p', (expected, date) => { // Arrange const order = deepClone(sessionStorage); diff --git a/src/layouts/order-confirmation/order-confirmation.vue b/src/layouts/order-confirmation/order-confirmation.vue index c3514995..63bd6cda 100644 --- a/src/layouts/order-confirmation/order-confirmation.vue +++ b/src/layouts/order-confirmation/order-confirmation.vue @@ -8,21 +8,34 @@
-
-
-
+
+
+
-
-
-

{{ formatDate(schedule.date) }}

-

- {{ appointmentTimeText }} -

-
+
+ + +
+
+ {{ serviceDescriptionText }} +
+
+ {{ workOrderTitle }} + {{ workOrderNumber }} +
+
+ {{ appointmentWordingText }} + {{ formatDate(schedule.date) }} + {{ appointmentTimeText }} + {{ appointmentLocation }} -
-
-
-
- +
+ {{ wipersTitle }} + {{ description }} +
+
+ {{ rainRepelTitle }} + {{ rainRepelBody }}
-
+
+ {{ contactDetailsTitle }} + {{ contactDetailsBody }} +
+
+
+ {{ cartHeaderText }} +
+ +
@@ -103,6 +131,9 @@ import { toTitleCase } from '@/helpers/text-helper.js'; import { AppointmentTypeStrings } from '@/constants/schedule-constants'; import applicationConfig from '@/constants/application-config'; import widgetFields from '@/constants/cms-widget-fields.js'; +import { getGlassList } from '@/helpers/damage-helper'; +import partTypeStrings from '@/constants/part-type-strings'; +import coverageType from '@/constants/coverage-type'; export default { name: 'order-confirmation', @@ -144,18 +175,21 @@ export default { serviceLocation, schedule, customer, + contactInfo, payment, customerPortalLoginToken, damage, - referralNumber + referralNumber, + hasRecalibrationPart } = this.submittedOrder; - const { issConfig, hasRecalibrationPart } = this.mainStore; + const { issConfig } = this.mainStore; return { vehicle, customerEmail: customer?.emailAddress, payment, schedule, serviceLocation, + contactInfo, appointmentType: serviceLocation.appointmentType, providerAddress: serviceLocation?.provider?.address, customerPortalLoginToken, @@ -164,12 +198,23 @@ export default { isRepair: damage.isRepair, hasRecalibrationPart, referralNumber: referralNumber?.toString(), + isNoComp: this.submittedOrder.insuranceCoverage.coverageType === coverageType.NO_COMP, widgets: { siteHeader: 'SiteHeaderWidget', emailConfirmation: 'EmailConfirmationWordingWidget', + emailConfirmationNoComp: 'EmailConfirmationNoCompWordingWidget', orderConfirmation: 'OrderConfirmationContent', + serviceDescription: 'ServiceDescriptionTextWidget', + workOrderNumberTitle: 'WorkOrderNumberTextWidget', mobile: 'MobileWordingWidget', dropOffAndInShop: 'DropOffAndInShopWordingWidget', + wipersText: 'WipersTextWidget', + rainRepel: 'RainRepelWidget', + smsUpdates: 'SmsUpdatesWidget', + contactDetails: 'ContactDetailsWidget', + vapsItemDescriptions: 'VapsItemDescriptions', + payAtAppointment: 'PayAtAppointmentTextWidget', + orderDetails: 'OrderDetailsTextWidget', siteFooter: 'SiteFooterWidget' } }; @@ -182,18 +227,29 @@ export default { vehicleYear: this.vehicle?.year, vehicleMake: this.vehicle?.make, vehicleModel: this.vehicle?.model, + serviceType: this.serviceTypeText, address: this.appointmentLocation, inShopDuration: this.inShopAppointmentDuration, email: this.customerEmail, + phone: this.contactInfo.servicePhone, + workOrderNumber: this.workOrderNumber, CUSTOMER_PORTAL_URL: applicationConfig.CUSTOMER_PORTAL_URL, CUSTOMER_PORTAL_LOGIN_TOKEN: this.customerPortalLoginToken }; }, - confirmationEmailText() { - const content = this.getCmsContentWithCustomValues( - this.widgets.emailConfirmation, - widgetFields.CONTENT_GROUP_WIDGET.BODY_TEXT - ); + orderConfirmationUpdateAppointmentText() { + let content = ''; + if(this.isNoComp) { + content = this.getCmsContentWithCustomValues( + this.widgets.emailConfirmationNoComp, + widgetFields.CONTENT_GROUP_WIDGET.BODY_TEXT + ); + } else { + content = this.getCmsContentWithCustomValues( + this.widgets.emailConfirmation, + widgetFields.CONTENT_GROUP_WIDGET.BODY_TEXT + ); + } return content ?.replaceAll('<', '<') ?.replaceAll('>', '>'); @@ -210,10 +266,51 @@ export default { widgetFields.CONTENT_GROUP_WIDGET.IMAGE ); }, + orderConfirmationSubheaderText() { + return this.getCmsContent( + this.widgets.orderConfirmation, + widgetFields.CONTENT_GROUP_WIDGET.SUBHEADER_TEXT + ); + }, + serviceDescriptionText() { + const content = this.getCmsContentWithCustomValues( + this.widgets.serviceDescription, + widgetFields.TEXT_BLOCK_WIDGET.TEXT + ); + return content; + }, + serviceTypeText() { + const glassPieces = this.submittedOrder.lineItems.glassParts?.map((part) => part.partType.toLowerCase()) ?? []; + let glassList = getGlassList(glassPieces); + let workType = ''; + if (this.isRepair) { + workType = 'repair'; + } else { + workType = 'replacement'; + if (this.hasRecalibrationPart) { + if (glassList === 'windshield') { + workType = 'replacement and recalibration'; + } + else { + glassList = glassList.replace('windshield', 'windshield replacement, recalibration'); + } + } + } + return `${glassList} ${workType}`; + }, + workOrderTitle() { + return this.getCmsContent( + this.widgets.workOrderNumberTitle, + widgetFields.TEXT_BLOCK_WIDGET.TEXT + ); + }, + workOrderNumber() { + return this.submittedOrder.workOrderNumber; + }, mobileWordingText() { return this.getCmsContentWithCustomValues( this.widgets.mobile, - widgetFields.CONTENT_GROUP_WIDGET.BODY_TEXT + widgetFields.CONTENT_GROUP_WIDGET.HEADER_TEXT ); }, mobileWordingText2() { @@ -225,7 +322,7 @@ export default { nonMobileWordingText() { return this.getCmsContentWithCustomValues( this.widgets.dropOffAndInShop, - widgetFields.CONTENT_GROUP_WIDGET.BODY_TEXT + widgetFields.CONTENT_GROUP_WIDGET.HEADER_TEXT ); }, nonMobileWordingText2() { @@ -241,12 +338,12 @@ export default { } if (this.isInShopAppointment) { const formattedStartTime = get12HourTimeFormat(startTime); - return `at ${formattedStartTime}`; + return `Your appointment is at ${formattedStartTime}`; } if (this.isMobileAppointment) { const mobileStartTime = get12HourTimeMobileFormat(startTime); const mobileEndTime = get12HourTimeMobileFormat(endTime); - return `Between ${mobileStartTime} - ${mobileEndTime}`; + return `Your appointment is at ${mobileStartTime} - ${mobileEndTime}`; } return null; }, @@ -261,12 +358,12 @@ export default { }, serviceLocationFullAddress() { const { address, address2, city, state, zipCode } = this.serviceLocation; - return `${address ?? ''}, ${address2 ? `${address2},` : ''}
${city ?? ''}, ${state ?? ''} ${zipCode ?? ''}`; + return `${address ?? ''}, ${address2 ? `${address2},` : ''} ${city ?? ''}, ${state ?? ''} ${zipCode ?? ''}`; }, providerFullAddress() { const { streetAddress, city, state, zipCode } = this.providerAddress; return streetAddress - ? `${toTitleCase(streetAddress)},
${toTitleCase(city)}, ${state ?? ''} ${zipCode ?? ''}` + ? `${toTitleCase(streetAddress)}, ${toTitleCase(city)}, ${state ?? ''} ${zipCode ?? ''}` : ''; }, appointmentWordingText() { @@ -318,6 +415,84 @@ export default { // settleTenderAmount always shows 0 via localhost or dev. // Temporarily set return true to see cart in localhost or dev environment return false; + }, + displayWipers() { + const hasWiperPart = this.submittedOrder.lineItems.vaps.some((part) => part.partType.toLowerCase().includes('wiper')); + return hasWiperPart; + }, + wipersTitle() { + return this.getCmsContent( + this.widgets.wipersText, + widgetFields.TEXT_BLOCK_WIDGET.TEXT + ); + }, + wipersBody() { + const wiperTypesOnOrder = []; + + const hasFrontWiper = this.submittedOrder.lineItems.vaps.some((part) => part.partType === partTypeStrings.FRONT_WIPER); + const hasRearWiper = this.submittedOrder.lineItems.vaps.some((part) => part.partType === partTypeStrings.REAR_WIPER); + + if (hasFrontWiper) { + wiperTypesOnOrder.push(partTypeStrings.FRONT_WIPER); + } + if (hasRearWiper) { + wiperTypesOnOrder.push(partTypeStrings.REAR_WIPER); + } + + const wiperDescriptions = wiperTypesOnOrder.map((type) => { + return this.getCmsContentForVapsType(type); + }); + + return wiperDescriptions; + }, + displayRainRepel() { + return this.submittedOrder.lineItems.vaps.some((part) => part.partType === partTypeStrings.RAIN_DEFENSE); + }, + rainRepelTitle() { + return this.getCmsContent( + this.widgets.rainRepel, + widgetFields.CONTENT_GROUP_WIDGET.HEADER_TEXT + ); + }, + rainRepelBody() { + return this.getCmsContent( + this.widgets.rainRepel, + widgetFields.CONTENT_GROUP_WIDGET.BODY_TEXT + ); + }, + displaySMSPhone() { + return this.contactInfo.requestTextUpdates && this.contactInfo.servicePhone; + }, + smsTitle() { + return this.getCmsContent( + this.widgets.smsUpdates, + widgetFields.TEXT_BLOCK_WIDGET.TEXT + ); + }, + contactDetailsTitle() { + return this.getCmsContent( + this.widgets.contactDetails, + widgetFields.CONTENT_GROUP_WIDGET.HEADER_TEXT + ); + }, + contactDetailsBody() { + return this.getCmsContentWithCustomValues( + this.widgets.contactDetails, + widgetFields.CONTENT_GROUP_WIDGET.BODY_TEXT + ); + }, + cartHeaderText() { + if (this.payment.isPayInAdvance) { + return this.getCmsContent( + this.widgets.orderDetails, + widgetFields.TEXT_BLOCK_WIDGET.TEXT + ); + } else { + return this.getCmsContent( + this.widgets.payAtAppointment, + widgetFields.TEXT_BLOCK_WIDGET.TEXT + ); + } } }, mounted() { @@ -411,68 +586,138 @@ export default { return dateObject.toLocaleDateString('en-us', { weekday: 'long', month: 'long', - day: 'numeric' + day: 'numeric', + year: 'numeric' }); + }, + getCmsContentForVapsType(vapsPartType) { + const vapsItemDescriptions = this.getCmsContent( + this.widgets.vapsItemDescriptions, + widgetFields.INPUT_QUESTION_WIDGET.ANSWERS + ); + + if (!vapsItemDescriptions) { + return ''; + } + + const vapsTypeDescription = vapsItemDescriptions?.find((entry) => entry?.Name === vapsPartType); + return vapsTypeDescription?.Text ?? ''; } } }; diff --git a/src/layouts/payment-method/payment-method.vue b/src/layouts/payment-method/payment-method.vue index 195909ec..aaaa32e5 100644 --- a/src/layouts/payment-method/payment-method.vue +++ b/src/layouts/payment-method/payment-method.vue @@ -24,8 +24,6 @@
diff --git a/src/layouts/payment-page/payment-page.vue b/src/layouts/payment-page/payment-page.vue index 7dd6b673..f4c86f11 100644 --- a/src/layouts/payment-page/payment-page.vue +++ b/src/layouts/payment-page/payment-page.vue @@ -42,8 +42,7 @@
diff --git a/src/layouts/schedule-page/schedule-page.vue b/src/layouts/schedule-page/schedule-page.vue index 1f45b9d0..45382ced 100644 --- a/src/layouts/schedule-page/schedule-page.vue +++ b/src/layouts/schedule-page/schedule-page.vue @@ -237,7 +237,7 @@ export default { const mobileFeePartPromise = getPricedMobileFeePart(serviceZipCode); const serviceabilityDetailsPromise = getServiceabilityDetails(serviceZipCode); const getGlassFeesPromise = useMainStore().getGlassFees(); - const providersPromise = useMainStore().getProviders(serviceZipCode); + const providersPromise = useMainStore().getSafeliteProviders(serviceZipCode); const premiumFeePromise = useMainStore().getMobilePremiumFee(); const premiumFeeWithPricePromise = premiumFeePromise.then((result) => { diff --git a/src/layouts/schedule-page/service-location/service-location.spec.js b/src/layouts/schedule-page/service-location/service-location.spec.js index 20e0596b..6f8d960d 100644 --- a/src/layouts/schedule-page/service-location/service-location.spec.js +++ b/src/layouts/schedule-page/service-location/service-location.spec.js @@ -147,7 +147,7 @@ function setupMocks({ appointmentType = AppointmentTypeStrings.IN_SHOP }) { } } })]; - useMainStore().getProviders = jest.fn().mockImplementation(() => mockProviders()); + useMainStore().getSafeliteProviders = jest.fn().mockImplementation(() => mockProviders()); const wrapper = mount(serviceLocation, mountOptions); wrapper.vm.$router.navigateWithSpinner = jest.fn(); diff --git a/src/layouts/schedule-page/service-location/service-location.vue b/src/layouts/schedule-page/service-location/service-location.vue index e48dddcc..75a5aa40 100644 --- a/src/layouts/schedule-page/service-location/service-location.vue +++ b/src/layouts/schedule-page/service-location/service-location.vue @@ -243,6 +243,10 @@ export default { return this.isGlassServiceableInshop; }, isServiceableMobile() { + if (this.isBigTruck) { + return false; + } + if (this.isRecalibrationServiceableMobile !== null) { return ( this.isGlassServiceableMobile @@ -553,7 +557,7 @@ export default { } }); - const providersPromise = useMainStore().getProviders(this.mobileZipCode); + const providersPromise = useMainStore().getSafeliteProviders(this.mobileZipCode); await providersPromise.then((result) => { const providers = result.data; if (providers) { diff --git a/src/layouts/schedule-page/service-location/shop-address/shop-address.vue b/src/layouts/schedule-page/service-location/shop-address/shop-address.vue index fb013177..934bd75f 100644 --- a/src/layouts/schedule-page/service-location/shop-address/shop-address.vue +++ b/src/layouts/schedule-page/service-location/shop-address/shop-address.vue @@ -314,7 +314,7 @@ export default { } }, async getNearbyShops(radiusInMiles) { - const result = await useMainStore().getProviders(this.internalZipcode, radiusInMiles); + const result = await useMainStore().getSafeliteProviders(this.internalZipcode, radiusInMiles); return result.data.shopProviders; }, getFullProviderAddress(provider) { diff --git a/src/store/index.js b/src/store/index.js index 63f8a623..6759f16f 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -299,6 +299,11 @@ export const useMainStore = defineStore({ const nonWindshieldItems = damage.glassToReplace?.filter((glass) => glass.glassLocation !== damageLocationsSelected.WINDSHIELD); return !!nonWindshieldItems?.length; }, + hasMoldingPart: (s) => { + return s.order.lineItems.glassParts?.some((glassPart) => { + return glassPart.childParts?.some((childPart) => childPart.partType === partTypeStrings.MOLDING); + }); + }, isMobileAppointment: (state) => state.order.serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE || state.order.serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP, isDropOffAppointment: (state) => state.order.serviceLocation.appointmentType === AppointmentTypeStrings.DROP_OFF, @@ -1029,46 +1034,35 @@ export const useMainStore = defineStore({ }); }, - getProviders(serviceZipCode, shopRadiusInMiles = 100) { - const { carId, isBigTruck } = this.order.vehicle; - const damageType = this.damage.isRepair ? 'Repair' : 'Replace'; - const { parentAccountNumber } = this.order; - const partsWithRecal = getTopLevelGlassPartsWithRecal(this.order.lineItems.glassParts); - const windshieldPartWithRecal = partsWithRecal?.length > 0 ? partsWithRecal[0] : null; - const safeliteOnly = true; - - let url = `${endpoints.GetProviders.url}/${serviceZipCode}/${damageType}/${shopRadiusInMiles}/${parentAccountNumber}/${safeliteOnly}/${carId}`; - if (windshieldPartWithRecal) { - url += `/${windshieldPartWithRecal.partNumber}`; - } - url += `/${isBigTruck}`; - return globalMethods.callHttpClient({ - method: endpoints.GetProviders.method, - endpoint: url - }); + getSafeliteProviders(zipCode, radius = 100) { + return this.getProviders(zipCode, radius, true) }, getTpaProviders(zipCode, radius) { + return this.getProviders(zipCode, radius, false) + }, + + getProviders(zipcode, radius, safeliteOnly) { const { carId, isBigTruck } = this.order.vehicle; const damageType = this.damage.isRepair ? 'Repair' : 'Replace'; const { parentAccountNumber } = this.order; const partsWithRecal = getTopLevelGlassPartsWithRecal(this.order.lineItems.glassParts); const windshieldPartWithRecal = partsWithRecal?.length > 0 ? partsWithRecal[0] : null; - const safeliteOnly = false; - - let url = `${endpoints.GetProviders.url}/${zipCode}/${damageType}/${radius}/${parentAccountNumber}/${safeliteOnly}/${carId}`; - if (windshieldPartWithRecal) { - url += `/${windshieldPartWithRecal.partNumber}`; - } - url += `/${isBigTruck}`; - - return new Promise((resolve, reject) => { - globalMethods.callHttpClient({ - method: endpoints.GetProviders.method, - endpoint: url - }).then((response) => resolve(response), (error) => reject(error)); + return globalMethods.callHttpClient({ + method: endpoints.GetProviders.method, + endpoint: endpoints.GetProviders.url( + zipcode, + damageType, + radius, + parentAccountNumber, + safeliteOnly, + carId, + windshieldPartWithRecal?.partNumber, + isBigTruck + ) }); }, + getCarrierAccountInfo() { return new Promise((resolve, reject) => { globalMethods.callHttpClient({ @@ -2897,12 +2891,13 @@ export const useMainStore = defineStore({ await this.getCarrierAccountInfo(); const submittedOrder = this.order; const { experiments } = this.applicationUser; - const { issConfig } = this; + const { issConfig, hasRecalibrationPart } = this; submittedOrder.isUnverified = this.isUnverified; submittedOrder.isVerified = this.isVerified; submittedOrder.submitType = submitType; submittedOrder.payment.isPayInAdvance = this.isPayInAdvance; + submittedOrder.hasRecalibrationPart = hasRecalibrationPart; // set to sessionStorage window.sessionStorage.setItem(webStorageConstants.SUBMITTED_ORDER, JSON.stringify(submittedOrder)); diff --git a/src/store/store.spec.js b/src/store/store.spec.js index 79df6796..4f4173d8 100644 --- a/src/store/store.spec.js +++ b/src/store/store.spec.js @@ -1983,16 +1983,16 @@ describe('Store', () => { const radius = getRandomInt(5, 100); const isBigTruck = false; store.order.vehicle.isBigTruck = isBigTruck; - const expectedEndpoint = [ - endpoints.GetProviders.url, + const expectedEndpoint = endpoints.GetProviders.url( zipCode, damageType, radius, parentAccountNumber, 'false', carId, + null, isBigTruck - ].join('/'); + ); // Act await store.getTpaProviders(zipCode, radius);