diff --git a/src/helpers/date-helper.js b/src/helpers/date-helper.js index 1b062c85..be8161e8 100644 --- a/src/helpers/date-helper.js +++ b/src/helpers/date-helper.js @@ -72,3 +72,35 @@ export function sumDateString(dateString, daysToAdd) { date.setDate(date.getDate() + daysToAdd); return convertDateToDateString(date); } + +export function get12HourTimeMobileFormat(time) { + // Check correct time format and split into components + let timeString = time.toString().match(/^([01]\d|2[0-3])(:)([0-5]\d)?$/) || [time]; + + if (timeString.length > 1) { + // If time format correct + const min = timeString[3]; + timeString = timeString.slice(1); // Remove full string match value + if (Number(min) === 0) { + timeString = timeString.slice(0, 1); // Remove minute value + timeString[1] = +timeString[0] < 12 ? ' AM' : ' PM'; // Set AM/PM + } else { + timeString[5] = +timeString[0] < 12 ? ' AM' : ' PM'; // Set AM/PM + } + timeString[0] = +timeString[0] % 12 || 12; // Adjust hours + } + return timeString.join(''); // return adjusted time or original string +} + +export function get12HourTimeFormat(time) { + // Check correct time format and split into components + let timeString = time.toString().match(/^([01]\d|2[0-3])(:)([0-5]\d)?$/) || [time]; + + if (timeString.length > 1) { + // If time format correct + timeString = timeString.slice(1); // Remove full string match value + timeString[5] = +timeString[0] < 12 ? ' AM' : ' PM'; // Set AM/PM + timeString[0] = +timeString[0] % 12 || 12; // Adjust hours + } + return timeString.join(''); // return adjusted time or original string +} diff --git a/src/layouts/order-confirmation/order-confirmation.spec.js b/src/layouts/order-confirmation/order-confirmation.spec.js index fe981266..590b5c1b 100644 --- a/src/layouts/order-confirmation/order-confirmation.spec.js +++ b/src/layouts/order-confirmation/order-confirmation.spec.js @@ -14,10 +14,11 @@ jest.mock('@/helpers/layout-helper.js', () => jest.fn()); jest.mock('@/helpers/cms-content-helper', () => ({ fetchCmsContentForPage: jest.fn() })); +const wordingText = 'wording Text {custom:address}'; const mockMixin = { methods: { - getCmsContent: jest.fn().mockImplementation(() => ''), + getCmsContent: jest.fn().mockImplementation(() => wordingText), setCmsContent: jest.fn() } }; @@ -33,6 +34,32 @@ const headerStub = { render: () => {} }; +const initialStore = { + order: { + schedule: { + date: '2024-03-01', + startTime: '09:00', + endTime: '10:00' + }, + serviceLocation: { + address: '123 Test Way', + address2: '#1', + city: 'Mesa', + state: 'AZ', + zipCode: '12345', + appointmentType: 'Inshop', + provider: { + address: { + streetAddress: '123 Safelite Street', + city: 'Mesa', + state: 'AZ', + zipCode: '12345' + } + } + } + } +}; + function getMountedComponent(mainInitialState = {}, initialData = {}, methodToRun = () => {}) { const mountOptions = getMountOptions({ router: { @@ -75,7 +102,7 @@ describe('OrderConfirmation.vue', () => { describe('Rendering', () => { test('Should render Site Header', () => { // Arrange - const { wrapper } = getMountedComponent({}); + const { wrapper } = getMountedComponent(initialStore); // Act const siteHeader = wrapper.findComponent(headerStub); @@ -85,13 +112,21 @@ describe('OrderConfirmation.vue', () => { }); test('If Advanced flow, should display Site Footer', () => { // Arrange - const carrierReturnUrl = 'testURL'; - const initialStore = { + const testStore = { + order: { + schedule: { + date: '2019-01-01', + startTime: '09:00' + }, + serviceLocation: { + appointmentType: 'Inshop' + } + }, issConfig: { - successReturnURL: carrierReturnUrl + successReturnURL: 'testURL' } }; - const { wrapper } = getMountedComponent(initialStore); + const { wrapper } = getMountedComponent(testStore); // Act const siteFooter = wrapper.findComponent({ ref: 'siteFooter' }); @@ -101,7 +136,7 @@ describe('OrderConfirmation.vue', () => { }); test('If Essential flow, should not display Site Footer', () => { // Arrange - const { wrapper } = getMountedComponent(); + const { wrapper } = getMountedComponent(initialStore); // Act const siteFooter = wrapper.findComponent({ ref: 'siteFooter' }); @@ -114,12 +149,21 @@ describe('OrderConfirmation.vue', () => { test('If Advanced flow, forward button action navigates to carrier URL', () => { // Arrange const carrierReturnUrl = 'testURL'; - const initialStore = { + const testStore = { + order: { + schedule: { + date: '2019-01-01', + startTime: '09:00' + }, + serviceLocation: { + appointmentType: 'Inshop' + } + }, issConfig: { successReturnURL: carrierReturnUrl } }; - const { wrapper } = getMountedComponent(initialStore); + const { wrapper } = getMountedComponent(testStore); // Act wrapper.vm.forwardButtonAction(); @@ -128,4 +172,157 @@ describe('OrderConfirmation.vue', () => { expect(wrapper.vm.$router.navigateToExternalUrl).toHaveBeenCalledWith(carrierReturnUrl); }); }); + describe('Computed properties', () => { + test('appointmentDateFormatted should return date in expected format', () => { + // Arrange + const { wrapper } = getMountedComponent(initialStore); + + // Act + const testValue = wrapper.vm.appointmentDateFormatted; + + // Assert + expect(testValue).toEqual('Friday, March 1'); + }); + test('appointmentTimeFormatted should return Mobile time in expected format', () => { + // Arrange + const testStore = { + order: { + schedule: { + date: '2019-01-01', + startTime: '09:00', + endTime: '10:00' + }, + serviceLocation: { + appointmentType: 'Mobile' + } + } + }; + const { wrapper } = getMountedComponent(testStore); + + // Act + const testValue = wrapper.vm.appointmentTimeFormatted; + + // Assert + expect(testValue).toEqual('Between 9 AM - 10 AM'); + }); + test('appointmentTimeFormatted should return Drop Off time in expected format', () => { + // Arrange + const testStore = { + order: { + schedule: { + date: '2019-01-01', + startTime: '09:00', + endTime: '10:00' + }, + serviceLocation: { + appointmentType: 'Drop Off' + } + } + }; + const { wrapper } = getMountedComponent(testStore); + + // Act + const testValue = wrapper.vm.appointmentTimeFormatted; + + // Assert + expect(testValue).toEqual('Drop off before 9:30 AM'); + }); + test('appointmentTimeFormatted should return In Shop time in expected format', () => { + // Arrange + const { wrapper } = getMountedComponent(initialStore); + + // Act + const testValue = wrapper.vm.appointmentTimeFormatted; + + // Assert + expect(testValue).toEqual('at 9:00 AM'); + }); + test('appointmentWordingText should return Mobile text in expected format', () => { + // Arrange + const testStore = { + order: { + schedule: { + date: '2019-01-01', + startTime: '09:00', + endTime: '10:00' + }, + serviceLocation: { + address: '123 Test Way', + address2: '#1', + city: 'Mesa', + state: 'AZ', + zipCode: '12345', + appointmentType: 'Mobile' + } + } + }; + const { wrapper } = getMountedComponent(testStore); + + // Act + const testValue = wrapper.vm.appointmentWordingText; + + // Assert + expect(testValue).toEqual('wording Text
123 Test Way, #1,
Mesa, AZ 12345
'); + }); + test('appointmentWordingText should return Drop Off text in expected format', () => { + // Arrange + const testStore = { + order: { + schedule: { + date: '2019-01-01', + startTime: '09:00', + endTime: '10:00' + }, + serviceLocation: { + provider: { + address: { + streetAddress: '123 Safelite Street', + city: 'Mesa', + state: 'AZ', + zipCode: '12345' + } + }, + appointmentType: 'Drop Off' + } + } + }; + const { wrapper } = getMountedComponent(testStore); + + // Act + const testValue = wrapper.vm.appointmentWordingText; + + // Assert + expect(testValue).toEqual('wording Text
123 Safelite Street,
Mesa, AZ 12345
'); + }); + test('appointmentWordingText should return In Shop text in expected format', () => { + // Arrange + const { wrapper } = getMountedComponent(initialStore); + + // Act + const testValue = wrapper.vm.appointmentWordingText; + + // Assert + expect(testValue).toEqual('wording Text
123 Safelite Street,
Mesa, AZ 12345
'); + }); + test('serviceLocationFullAddress should return text in expected format', () => { + // Arrange + const { wrapper } = getMountedComponent(initialStore); + + // Act + const testValue = wrapper.vm.serviceLocationFullAddress; + + // Assert + expect(testValue).toEqual('
123 Test Way, #1,
Mesa, AZ 12345
'); + }); + test('providerFullAddress should return text in expected format', () => { + // Arrange + const { wrapper } = getMountedComponent(initialStore); + + // Act + const testValue = wrapper.vm.providerFullAddress; + + // Assert + expect(testValue).toEqual('
123 Safelite Street,
Mesa, AZ 12345
'); + }); + }); }); diff --git a/src/layouts/order-confirmation/order-confirmation.vue b/src/layouts/order-confirmation/order-confirmation.vue index 63b028eb..38f2dc9e 100644 --- a/src/layouts/order-confirmation/order-confirmation.vue +++ b/src/layouts/order-confirmation/order-confirmation.vue @@ -8,7 +8,28 @@
-

Placeholder for order confirmation page

+
+ + +
+
+
+ + +
+
+

{{ appointmentDateFormatted }}

+

{{ appointmentTimeFormatted }}

+
+
+
+
// Components import siteHeader from '@/iss-components/site-header/site-header.vue'; +import vehicleBanner from '@/iss-components/vehicle-banner/vehicle-banner.vue'; import siteFooter from '@/iss-components/site-footer/site-footer.vue'; // Supporting files import { fetchCmsContentForPage } from '@/helpers/cms-content-helper'; @@ -32,11 +54,14 @@ import settleAllPromises from '@/helpers/layout-helper'; import { Form } from 'vee-validate'; import BaseFormMixin from '@/mixins/base-form-mixin.js'; import { useMainStore } from '@/store'; +import { get12HourTimeFormat, get12HourTimeMobileFormat, convertDateStringToDate } from '@/helpers/date-helper.js'; +import { toTitleCase } from '@/helpers/text-helper.js'; export default { name: 'order-confirmation', components: { siteHeader, + vehicleBanner, siteFooter, // eslint-disable-next-line vue/no-reserved-component-names Form @@ -67,6 +92,82 @@ export default { }, carrierUrl() { return this.mainStore.issConfig.successReturnURL; + }, + orderConfirmationHeaderText() { + return this.getCmsContent('OrderConfirmationContent', 'HeaderText'); + }, + orderConfirmationImage() { + return this.getCmsContent('OrderConfirmationContent', 'Image'); + }, + appointmentType() { + return this.mainStore.order.serviceLocation.appointmentType.toUpperCase(); + }, + appointmentDate() { + return this.mainStore.order.schedule.date; + }, + appointmentStartTime() { + return this.mainStore.order.schedule.startTime; + }, + appointmentEndTime() { + return this.mainStore.order.schedule.endTime; + }, + appointmentDateFormatted() { + // This conversion ensures we don't get get GMT induced date changes + const dateObject = convertDateStringToDate(this.appointmentDate); + // Ex: Tuesday, April 22 + return dateObject.toLocaleDateString('en-us', { + weekday: 'long', + month: 'long', + day: 'numeric' + }); + }, + appointmentTimeFormatted() { + const formattedTime = this.formatAppointmentTime(this.appointmentType); + return formattedTime; + }, + mobileWordingText() { + return this.getCmsContent('MobileWordingWidget', 'BodyText'); + }, + dropOffAndInShopWordingText() { + return this.getCmsContent('DropOffAndInShopWordingWidget', 'BodyText'); + }, + serviceLocationAddress() { + return this.mainStore.order.serviceLocation.address; + }, + serviceLocationAddress2() { + return this.mainStore.order.serviceLocation.address2; + }, + serviceLocationCity() { + return this.mainStore.order.serviceLocation.city; + }, + serviceLocationState() { + return this.mainStore.order.serviceLocation.state; + }, + serviceLocationZipCode() { + return this.mainStore.order.serviceLocation.zipCode; + }, + serviceLocationFullAddress() { + // eslint-disable-next-line max-len + return `
${this.serviceLocationAddress}, ${this.serviceLocationAddress2 ? `${this.serviceLocationAddress2},` : ''}
${this.serviceLocationCity}, ${this.serviceLocationState} ${this.serviceLocationZipCode}
`; + }, + providerAddress() { + return toTitleCase(this.mainStore.order.serviceLocation.provider.address.streetAddress); + }, + providerCity() { + return toTitleCase(this.mainStore.order.serviceLocation.provider.address.city); + }, + providerState() { + return this.mainStore.order.serviceLocation.provider.address.state; + }, + providerZipCode() { + return this.mainStore.order.serviceLocation.provider.address.zipCode; + }, + providerFullAddress() { + // eslint-disable-next-line max-len + return `
${this.providerAddress},
${this.providerCity}, ${this.providerState} ${this.providerZipCode}
`; + }, + appointmentWordingText() { + return this.formatWordingText(this.appointmentType); } }, mounted() { @@ -77,6 +178,40 @@ export default { methods: { forwardButtonAction() { this.$router.navigateToExternalUrl(this.carrierUrl); + }, + formatAppointmentTime(appointmentType) { + switch (appointmentType) { + case 'MOBILE': + // eslint-disable-next-line max-len + return `Between ${get12HourTimeMobileFormat(this.appointmentStartTime)} - ${get12HourTimeMobileFormat(this.appointmentEndTime)}`; + case 'DROP OFF': + return 'Drop off before 9:30 AM'; + case 'INSHOP': + return `at ${get12HourTimeFormat(this.appointmentStartTime)}`; + default: + return null; + } + }, + formatWordingText(appointmentType) { + switch (appointmentType) { + case 'MOBILE': + return this.mobileWordingText?.replaceAll( + '{custom:address}', + this.serviceLocationFullAddress + ); + case 'DROP OFF': + return this.dropOffAndInShopWordingText?.replaceAll( + '{custom:address}', + this.providerFullAddress + ); + case 'INSHOP': + return this.dropOffAndInShopWordingText?.replaceAll( + '{custom:address}', + this.providerFullAddress + ); + default: + return null; + } } } }; @@ -91,4 +226,34 @@ $page-side-padding: 1.5rem; padding: 0 1.5rem !important; } } + +.text-color--black { + color: $black; +} + +.appointment-details { + margin-bottom: 1.5rem; + padding: 1rem 1.5rem 1.5rem; + box-shadow: 0 3px 10px rgb(0 0 0 / 0.2); + border-radius: 5px; +} + +.appointment-date-time P { + color: $black; + font-size: $h5-font-size; + line-height: map-get($spacers, 6); + margin-bottom: 0.5rem; + + + p { + font-size: map-get($spacers, 4); + line-height: 1.625rem; + font-weight: $font-weight-bold; + } +} + +.appointment-text { + :deep(strong) { + font-weight: $font-weight-bold; + } +}