diff --git a/public/css/hop-styling.css b/public/css/hop-styling.css new file mode 100644 index 000000000..c307731f6 --- /dev/null +++ b/public/css/hop-styling.css @@ -0,0 +1,336 @@ +.btn-group-lg > .btn { + padding: 10px 20px; + font-size: 20px; + line-height: 1.3; + border-radius: 5px; +} +.btn-group-sm > .btn { + padding: 3px 6px; + font-size: 13px; + line-height: 1.5; + border-radius: 3px; +} +.btn-group-xs > .btn { + padding: 1px 5px; + font-size: 13px; + line-height: 1.5; + border-radius: 3px; +} +.dl-horizontal dd:before, +.dl-horizontal dd:after, +.container:before, +.container:after, +.container-fluid:before, +.container-fluid:after, +.row:before, +.row:after, +.form-horizontal .form-group:before, +.form-horizontal .form-group:after, +.btn-toolbar:before, +.btn-toolbar:after, +.btn-group-vertical > .btn-group:before, +.btn-group-vertical > .btn-group:after, +.panel-body:before, +.panel-body:after, +.modal-footer:before, +.modal-footer:after { + content: " "; + display: table; +} +.dl-horizontal dd:after, +.container:after, +.container-fluid:after, +.row:after, +.form-horizontal .form-group:after, +.btn-toolbar:after, +.btn-group-vertical > .btn-group:after, +.panel-body:after, +.modal-footer:after { + clear: both; +} +[data-icon]:before, +[data-icon-after]:after, +.ico:before { + speak: none; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + text-transform: none !important; + font-weight: normal !important; + font-style: normal !important; + font-variant: normal !important; + font-size: 20px !important; + font-family: "safelite-ico" !important; + line-height: 20px !important; +} +html { + position: static !important; + height: auto !important; +} +html, +body { + background-color: #ffffff !important; +} +body { + margin-bottom: 16px !important; +} +.headerlinecontainer { + color: #000000; + font-family: Roboto, Arial, "Helvetica Neue", Helvetica, Tahoma, Verdana, sans-serif; +} +.headerline1 { + display: block; + margin-bottom: 11.5px; + font-weight: 300; + font-size: 20px; + line-height: 30px; +} +.headerline2, +.headerline3, +.headerline4, +.headerline5 { + margin-bottom: 11.5px; + font-size: 16px; +} +.headerline3 { + color: #0c7e47; +} +.headerline5 { + display: block; + padding: 11.5px 0; + color: #167cac; + font-style: italic; +} +.headerline5v2 { + display: block; + padding: 11.5px 0; + color: #da291c; + font-style: italic; + font-size: 12px; +} +.form-group { + margin-bottom: 10px; +} +.form-group .control-label { + display: block; + margin-bottom: 2px; +} +.form-group .form-control { + margin-bottom: 2px; +} +@media (min-width: 500px) { + .form-group { + width: 80%; + } +} +@media (min-width: 800px) { + .form-group { + width: 60%; + } +} +@media (min-width: 576px) { + #cardExpirationContainer .card_expirationMonth, + #cardExpirationContainer .card_expirationYear { + position: relative; + width: 49%; + } + #cardExpirationContainer .card_expirationMonth { + float: left; + } + #cardExpirationContainer .card_expirationYear { + float: right; + } +} +.totalContainer { + margin: 16px 0; + padding: 8px; + border-radius: 5px; + background-color: #0c7e47; + color: #ffffff; + font-size: 16px; +} +.totalContainer em { + font-weight: 500; +} +.totalContainer .strikeThrough { + padding-left: 8px; + text-decoration: line-through; + font-size: 14px; +} +.callerDisplayTextContainer { + margin: 16px 0; + font-style: italic; +} +.disclaimerContainer { + margin: 16px 0; + padding: 8px; + border-radius: 5px; + background-color: #f0f1f2; +} +.disclaimerContainerv2 { + margin: 16px 0; + border-radius: 5px; +} +.buttonContainer #sghopcancelbutton { + float: left; +} +.buttonContainer #sghopsubmitbutton { + float: right; +} +.buttonContainer #sghopsubmitbutton.creditCardSpecific { + float: right; +} +@media (max-width: 500px) { + .buttonContainer #sghopsubmitbutton.creditCardSpecific { + float: left; + width: 100%; + } +} +.buttonContainer #selectPaymentTypeButton { + float: right; +} +@media (max-width: 500px) { + .buttonContainer #selectPaymentTypeButton { + float: left; + width: 100%; + } +} +.globalMessage .message { + padding-left: 0; + line-height: 1.25; +} +.globalMessage .message strong { + color: #da291c; +} +.italicSmall { + font-weight: 300; + font-style: normal; + font-size: 15px; +} +#pageLoadingModal { + background-image: url("data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=="); +} +#pageLoadingModal .modal-content { + margin-top: 100px; +} +#paypalParentDiv { + margin-bottom: 20px; +} +#creditCardParentDiv { + margin-bottom: 0; +} +.radio .list-group.paymentSelection, +.radio.list-group.paymentSelection { + margin-top: 0; + margin-bottom: 0; +} +@media (max-width: 33.9em) { + .radio .list-group.paymentSelection label, + .radio.list-group.paymentSelection label, + .radio .list-group.paymentSelection a, + .radio.list-group.paymentSelection a { + max-height: 52px; + } +} +#creditCardParentLink { + margin-bottom: 0; +} +@media (max-width: 33.9em) { + #creditCardParentLink { + min-height: 57px; + } +} +#creditCardImages, +#paypalImageSpan { + padding-right: 0; +} +@media (max-width: 33.9em) { + .paymentOptionText { + padding-left: 5px; + } +} +.extraPaddingTop { + padding-top: 20px; +} +.extraPaddingBottom { + padding-bottom: 20px; +} +@media (min-width: 33.9em) { + #paypalImage { + max-width: 22%; + } +} +@media (max-width: 33.9em) { + #paypalImage { + max-width: 55px; + } +} +label.centerme { + position: inherit; + display: table-cell; + width: 9999px !important; + /*This is kind of a hack*/ + vertical-align: middle; +} +input[name="rdioCreditCard"] { + display: block; +} +@media (min-width: 33.9em) { + input[name="rdioCreditCard"] { + margin-top: 10px; + } +} +@media (min-width: 33.9em) { + span.paymentOptionText { + margin-top: 6px; + } +} +.headerline1.safelitehop { + margin-top: 20px; +} +@media (max-width: 499px) { + #buttonContainer { + width: 100%; + } +} +.has-success .control-label { + color: #4d4e53; +} +#thm_fp { + display: block; +} +#paypalParentLink label, +#creditCardParentLink label { + padding-top: 9px; + padding-bottom: 9px; +} +#paypalParentLink label .paymentOptionText, +#creditCardParentLink label .paymentOptionText { + padding-left: 20px; + text-align: left; +} +#paypalParentLink { + margin-top: 12px; +} +html #creditCardParentLink.list-group-item { + border-radius: 48px; + text-align: center; +} +.optional-label span { + display: none; +} +.optional-label::after { + content: " (optional)"; + font-weight: 300; + font-style: normal; + font-size: 15px; +} +.creditCardSpecific { + display: none; +} +@media (max-width: 500px) { + .buy-backdrop.afterpay-checkout-wrapper { + -webkit-box-align: start; + -ms-flex-align: start; + align-items: flex-start; + } +} \ No newline at end of file diff --git a/src/digital-components/checkbox-question/checkbox-question.vue b/src/digital-components/checkbox-question/checkbox-question.vue index 50245d025..6d3777210 100644 --- a/src/digital-components/checkbox-question/checkbox-question.vue +++ b/src/digital-components/checkbox-question/checkbox-question.vue @@ -1,16 +1,18 @@ diff --git a/src/layouts/add-to-calendar/add-to-calendar.spec.js b/src/layouts/add-to-calendar/add-to-calendar.spec.js index 645e3b12f..14b6d978b 100644 --- a/src/layouts/add-to-calendar/add-to-calendar.spec.js +++ b/src/layouts/add-to-calendar/add-to-calendar.spec.js @@ -69,7 +69,23 @@ afterEach(() => { describe("Add-to-calendar methods...", () => { test("Add-to-calendar should trigger openModal method", () => { //Arrange - const { wrapper } = setupMocks({}); + + const { wrapper } = setupMocks({ + customMountOptions: { + propsData: { + appointment: { + RefrrlSeqNum: "123456", + StartDate: new Date(2023, 9, 11, 12, 0, 0), + EndDate: new Date(2023, 9, 11, 13, 0, 0), + Subject: "Meeting with John", + Location: "Conference Room", + Body: "Discuss the project progress", + IsHTML: true, + Duration: "1220", + }, + }, + }, + }); //Act wrapper.vm.openCalendarModal(); @@ -77,45 +93,26 @@ describe("Add-to-calendar methods...", () => { //Assert expect(wrapper.vm.$refs.calendarModalQuestion.openModal).toBeCalled(); }); - test("serviceType should return 'replacement and recalibration' when isRepair is false and funnelHasRecalibrationPart true.", () => { - //Arrange - store.getters.order.damage.isRepair = false; - store.getters.funnelHasRecalibrationPart = true; - const { wrapper } = setupMocks({}); - //Act - const testValue = wrapper.vm.serviceType(); - - //Assert - expect(testValue).toEqual("replacement and recalibration"); - }); - test("serviceType should return 'replacement' when isRepair is false and funnelHasRecalibrationPart is false.", () => { - //Arrange - store.getters.order.damage.isRepair = false; - store.getters.funnelHasRecalibrationPart = false; - const { wrapper } = setupMocks({}); - - //Act - const testValue = wrapper.vm.serviceType(); - - //Assert - expect(testValue).toEqual("replacement"); - }); - test("serviceType should return 'repair' when isRepair is true.", () => { - //Arrange - store.getters.order.damage.isRepair = true; - const { wrapper } = setupMocks({}); - - //Act - const testValue = wrapper.vm.serviceType(); - - //Assert - expect(testValue).toEqual("repair"); - }); test("getCalendarData should return expected model value.", () => { //Arrange const type = calendarOptions.OUTLOOKCOM; - const { wrapper } = setupMocks({}); + const { wrapper } = setupMocks({ + customMountOptions: { + propsData: { + appointment: { + RefrrlSeqNum: "123456", + StartDate: new Date(2023, 9, 11, 12, 0, 0), + EndDate: new Date(2023, 9, 11, 13, 0, 0), + Subject: "Meeting with John", + Location: "Conference Room", + Body: "Discuss the project progress", + IsHTML: true, + Duration: "1220", + }, + }, + }, + }); //Act const testValue = wrapper.vm.getCalendarData(type); @@ -123,95 +120,26 @@ describe("Add-to-calendar methods...", () => { //Assert expect(testValue.name).toEqual(calendarOptions.OUTLOOKCOM); }); - test("getappointmentData should return expected model value for appointmentType mobile.", () => { - //Arrange - store.getters.order.serviceLocation.appointmentType = AppointmentTypeStrings.MOBILE; - store.getters.order.damage.isRepair = false; - store.getters.funnelHasRecalibrationPart = true; - const { wrapper } = setupMocks({}); - //Act - const testValue = wrapper.vm.getappointmentData(); - - //Assert - expect(testValue.Subject).toEqual("Safelite replacement and recalibration appointment"); - }); - test("getappointmentData should return expected model value for appointmentType DROP_OFF and OVERNIGHT_DROP_OFF.", () => { - //Arrange - store.getters.order.serviceLocation.appointmentType = AppointmentTypeStrings.DROP_OFF; - store.getters.order.schedule.routeCode = RouteCodeFlags.OVERNIGHT_DROP_OFF; - const { wrapper } = setupMocks({}); - - //Act - const testValue = wrapper.vm.getappointmentData(); - - //Assert - expect(testValue.Subject).toEqual("Drop off your vehicle by 5:30 PM"); - }); - test("getappointmentData should return expected model value for appointmentType DROP_OFF and ALL_DAY_DROP_OFF and isSameDayDropOff.", () => { - //Arrange - store.getters.order.serviceLocation.appointmentType = AppointmentTypeStrings.DROP_OFF; - store.getters.order.schedule.routeCode = RouteCodeFlags.ALL_DAY_DROP_OFF; - store.getters.order.schedule.date = new Date().toISOString().split("T")[0]; - const { wrapper } = setupMocks({}); - - //Act - const testValue = wrapper.vm.getappointmentData(); - - //Assert - expect(testValue.Subject).toEqual("Drop off your vehicle in the next 30 - 60 minutes"); - }); - test("getappointmentData should return expected model value for appointmentType DROP_OFF and ALL_DAY_DROP_OFF.", () => { - //Arrange - store.getters.order.serviceLocation.appointmentType = AppointmentTypeStrings.DROP_OFF; - store.getters.order.schedule.routeCode = RouteCodeFlags.ALL_DAY_DROP_OFF; - const { wrapper } = setupMocks({}); - - //Act - const testValue = wrapper.vm.getappointmentData(); - - //Assert - expect(testValue.Subject).toEqual("Drop off your vehicle between 9:00 AM - 11:00 AM"); - }); - test("getappointmentData should return expected model value for appointmentType DROP_OFF.", () => { - //Arrange - store.getters.order.serviceLocation.appointmentType = AppointmentTypeStrings.DROP_OFF; - store.getters.order.damage.isRepair = true; - const { wrapper } = setupMocks({}); - - //Act - const testValue = wrapper.vm.getappointmentData(); - - //Assert - expect(testValue.Subject).toEqual("Safelite repair appointment"); - }); - test("getappointmentData should return expected model value for appointmentType IN_SHOP.", () => { - //Arrange - store.getters.order.serviceLocation.appointmentType = AppointmentTypeStrings.IN_SHOP; - store.getters.order.damage.isRepair = true; - const { wrapper } = setupMocks({}); - - //Act - const testValue = wrapper.vm.getappointmentData(); - - //Assert - expect(testValue.Subject).toEqual("Safelite repair appointment"); - }); - test("isSameDayDropOff should return true for current schedule date.", () => { - //Arrange - store.getters.order.schedule.date = new Date().toISOString().split("T")[0]; - const { wrapper } = setupMocks({}); - - //Act - const testValue = wrapper.vm.isSameDayDropOff(); - - //Assert - expect(testValue).toEqual(true); - }); test("setCalendarAppointment should call getAppointment method for iCAL and Outlook.", () => { //Arrange const newValue = { name: calendarOptions.OUTLOOK }; - const { wrapper } = setupMocks({}); + const { wrapper } = setupMocks({ + customMountOptions: { + propsData: { + appointment: { + RefrrlSeqNum: "123456", + StartDate: new Date(2023, 9, 11, 12, 0, 0), + EndDate: new Date(2023, 9, 11, 13, 0, 0), + Subject: "Meeting with John", + Location: "Conference Room", + Body: "Discuss the project progress", + IsHTML: true, + Duration: "1220", + }, + }, + }, + }); wrapper.vm.getAppointment = jest.fn(); //Act wrapper.vm.setCalendarAppointment(newValue); @@ -222,7 +150,22 @@ describe("Add-to-calendar methods...", () => { test("setCalendarAppointment should call window.open for Google, yahoo and Outlook.com.", () => { //Arrange const newValue = { name: calendarOptions.GOOGLE }; - const { wrapper } = setupMocks({}); + const { wrapper } = setupMocks({ + customMountOptions: { + propsData: { + appointment: { + RefrrlSeqNum: "123456", + StartDate: new Date(2023, 9, 11, 12, 0, 0), + EndDate: new Date(2023, 9, 11, 13, 0, 0), + Subject: "Meeting with John", + Location: "Conference Room", + Body: "Discuss the project progress", + IsHTML: true, + Duration: "1220", + }, + }, + }, + }); window.open = jest.fn(); //Act diff --git a/src/layouts/add-to-calendar/add-to-calendar.vue b/src/layouts/add-to-calendar/add-to-calendar.vue index 3f32a525b..afd9942ea 100644 --- a/src/layouts/add-to-calendar/add-to-calendar.vue +++ b/src/layouts/add-to-calendar/add-to-calendar.vue @@ -22,26 +22,19 @@ import { calendarOptions } from "@/constants/calendar-options"; import { calendarStatus } from "@/constants/calendar-status"; import calendarModalQuestion from "@/layouts/add-to-calendar/calendar-modal-question/calendar-modal-question"; -import { - getDateFormat, - calculateDuration, - combineDateAndTime, - shortTimeString, - addMinutes, -} from "@/helpers/date-helper"; +import { getDateFormat } from "@/helpers/date-helper"; import { getCalendarFile, download } from "@/helpers/add-to-calendar-helper"; -import { AppointmentTypeStrings, RouteCodeFlags } from "@/constants/schedule-constants"; -import store from "@/store"; -import { serviceType } from "@/constants/service-type"; export default { name: "add-to-calendar", components: { calendarModalQuestion, }, + props: { + appointment: Object, + }, data() { return { - appointment: this.getappointmentData(), selectedCalendarOption: null, }; }, @@ -83,67 +76,6 @@ export default { openCalendarModal() { this.$refs["calendarModalQuestion"].openModal(); }, - scheduleDate() { - return store.getters.order.schedule.date; - }, - scheduleStartTime() { - return store.getters.order.schedule.startTime; - }, - scheduleEndTime() { - return store.getters.order.schedule.endTime; - }, - serviceLocationAddress() { - return store.getters.order.serviceLocation.address; - }, - ServiceLocationAddress2() { - return store.getters.order.serviceLocation.address2; - }, - serviceLocationCity() { - return store.getters.order.serviceLocation.city; - }, - serviceLocationState() { - return store.getters.order.serviceLocation.state; - }, - serviceLocationZipCode() { - return store.getters.order.serviceLocation.zipCode; - }, - providerAddress() { - return store.getters.order.serviceLocation.provider.address.streetAddress; - }, - providerCity() { - return store.getters.order.serviceLocation.provider.address.city; - }, - providerState() { - return store.getters.order.serviceLocation.provider.address.state; - }, - providerZipCode() { - return store.getters.order.serviceLocation.provider.address.zipCode; - }, - appointmentType() { - return store.getters.order.serviceLocation.appointmentType; - }, - uniqueId() { - return store.getters.order.referralNumber?.toString(); - }, - serviceType() { - const isRepair = store.getters.order.damage.isRepair; - const funnelHasRecalibrationPart = store.getters.funnelHasRecalibrationPart; - if (!isRepair) { - if (funnelHasRecalibrationPart) { - return serviceType.REPLACEMENT_AND_RECALIBRATION; - } else { - return serviceType.REPLACEMENT; - } - } - return serviceType.REPAIR; - }, - routeCode() { - return store.getters.order.schedule.routeCode; - }, - isSameDayDropOff() { - const todaysDate = new Date().toISOString().split("T")[0]; - return this.scheduleDate() === todaysDate; - }, getCalendarData(type) { var URL = ""; const dateFormat = "yyyyMMddTHHmmss"; @@ -185,72 +117,6 @@ export default { icon: require(`@/assets/img/icons/${type}.svg`), }; }, - getappointmentData() { - var subject = ""; - var location = ""; - var startDateTime = combineDateAndTime(this.scheduleDate(), this.scheduleStartTime()); - var endDateTime = combineDateAndTime(this.scheduleDate(), this.scheduleEndTime()); - var body = ""; - var isHTML = false; - var duration = ""; - - if (this.appointmentType() === AppointmentTypeStrings.MOBILE) { - location = `${ - this.ServiceLocationAddress2() ? this.ServiceLocationAddress2() + ", " : "" - }${this.serviceLocationAddress()}, ${this.serviceLocationCity()}, ${this.serviceLocationState()} ${this.serviceLocationZipCode()}`; - subject = `Safelite ${this.serviceType()} appointment`; - - body = - "Your safety is our top priority, so we'll let you know who's coming to you and your tech will reach out to let you know when they're on the way."; - } else { - location = `${this.providerAddress()}, ${this.providerCity()}, ${this.providerState()} ${this.providerZipCode()}`; - if (this.appointmentType() === AppointmentTypeStrings.DROP_OFF) { - if (this.routeCode().includes(RouteCodeFlags.OVERNIGHT_DROP_OFF)) { - subject = "Drop off your vehicle by 5:30 PM"; - - body = - "Drop off your vehicle by 5:30 PM the night of your scheduled appointment. The shop will work with you on a convenient pick-up time."; - } else if (this.routeCode().includes(RouteCodeFlags.ALL_DAY_DROP_OFF)) { - if (this.isSameDayDropOff()) { - subject = "Drop off your vehicle in the next 30 - 60 minutes"; - endDateTime = addMinutes(startDateTime, 120); - body = - "Drop your vehicle off in the next 30 - 60 minutes. We'll work your vehicle into our day's schedule. The shop will work with you on a convenient pick-up time."; - } else { - endDateTime = addMinutes(startDateTime, 120); - subject = - "Drop off your vehicle between " + - shortTimeString(startDateTime) + - " - " + - shortTimeString(endDateTime); - - body = `Drop off your vehicle by ${shortTimeString( - endDateTime - )} and pick it up later in the day. When you arrive, park in an open space and follow the key drop instructions. We'll text you when your vehicle is ready.`; - } - } //normal time slot - else { - subject = `Safelite ${this.serviceType()} appointment`; - body = "Just a reminder that you're coming to us at " + location + "."; - } - } else { - subject = `Safelite ${this.serviceType()} appointment`; - body = "Just a reminder that you're coming to us at " + location + "."; - } - duration = calculateDuration(startDateTime, endDateTime); - } - return { - Subject: subject, - Location: location, - StartDate: startDateTime, - EndDate: endDateTime, - RefrrlSeqNum: this.uniqueId(), - Body: body, - IsHTML: isHTML, - Duration: duration, - }; - }, - getAppointment() { let body = this.appointment.Body; body = body.replace("|", "<").replace("~", ">"); diff --git a/src/layouts/confirmation/confirmation.spec.js b/src/layouts/confirmation/confirmation.spec.js index eb77afcb1..4bef5f8a3 100644 --- a/src/layouts/confirmation/confirmation.spec.js +++ b/src/layouts/confirmation/confirmation.spec.js @@ -115,16 +115,16 @@ describe("confirmation.vue", () => { }); }); describe("computed properties...", () => { - // test("ScheduleDateFormatted should return date in expected format.", () => { - // //Arrange - // const { wrapper } = setupMocks({}); + test("ScheduleDateFormatted should return date in expected format.", () => { + //Arrange + const { wrapper } = setupMocks({}); - // // Act - // const testValue = wrapper.vm.ScheduleDateFormatted; + // Act + const testValue = wrapper.vm.ScheduleDateFormatted; - // // Assert - // expect(testValue).toEqual("Sunday, January 1"); - // }); + // Assert + expect(testValue).toEqual("Sunday, January 1"); + }); test("ScheduleTimeFormatted should return mobile time in expected format.", () => { //Arrange store.getters.order.serviceLocation.appointmentType = AppointmentTypeStrings.MOBILE; diff --git a/src/layouts/confirmation/confirmation.vue b/src/layouts/confirmation/confirmation.vue index f0af10529..1ced1c495 100644 --- a/src/layouts/confirmation/confirmation.vue +++ b/src/layouts/confirmation/confirmation.vue @@ -10,10 +10,6 @@ -
-
-

{{ this.ScheduleDateFormatted }}

-

{{ this.ScheduleTimeFormatted }}

+

{{ ScheduleDateFormatted }}

+

{{ ScheduleTimeFormatted }}

- + -
+
-
+
"); + ?.replaceAll("{custom:MY_ACCOUNT_URL}", applicationConfig.MY_ACCOUNT) + ?.replaceAll("<", "<") + ?.replaceAll(">", ">"); }, ScheduleDate() { return store.getters.order.schedule.date; @@ -143,17 +143,17 @@ export default { }, AppointmentWordingText() { if (this.AppointmentType == AppointmentTypeStrings.MOBILE) { - return this.MobileWordingText.replaceAll( + return this.MobileWordingText?.replaceAll( "{custom:ADDRESS}", this.ServiceLocationFullAddress ); } else if (this.AppointmentType == AppointmentTypeStrings.DROP_OFF) { - return this.DropOffWordingText.replaceAll( + return this.DropOffWordingText?.replaceAll( "{custom:ADDRESS}", this.ProviderFullAddress ); } else { - return this.InShopWordingText.replaceAll( + return this.InShopWordingText?.replaceAll( "{custom:ADDRESS}", this.ProviderFullAddress ); @@ -190,6 +190,133 @@ export default { return `at ${get12HourTimeFormat(this.ScheduleStartTime)}`; } }, + IsSameDayDropOff() { + const todaysDate = new Date().toISOString().split("T")[0]; + return this.ScheduleDate === todaysDate; + }, + Mobile_Appointment_Subject() { + return this.getCmsContent("Mobile_Appointment_Widget", "HeaderText"); + }, + Mobile_Appointment_Body() { + return this.getCmsContent("Mobile_Appointment_Widget", "BodyText"); + }, + InShop_Appointment_Subject() { + return this.getCmsContent("InShop_Appointment_Widget", "HeaderText"); + }, + InShop_Appointment_Body() { + return this.getCmsContent("InShop_Appointment_Widget", "BodyText"); + }, + Drop_Off_Appointment_Subject() { + return this.getCmsContent("Drop_Off_Appointment_Widget", "HeaderText"); + }, + Drop_Off_Appointment_Body() { + return this.getCmsContent("Drop_Off_Appointment_Widget", "BodyText"); + }, + Overnight_Drop_Off_Appointment_Subject() { + return this.getCmsContent("Drop_Off_Appointment_Widget", "HeaderText"); + }, + Overnight_Drop_Off_Appointment_Body() { + return this.getCmsContent("Drop_Off_Appointment_Widget", "BodyText"); + }, + All_Day_Drop_Off_Appointment_Subject() { + return this.getCmsContent("All_Day_Drop_Off_Appointment_Widget", "HeaderText"); + }, + All_Day_Drop_Off_Appointment_Body() { + return this.getCmsContent("All_Day_Drop_Off_Appointment_Widget", "BodyText"); + }, + Same_Day_Drop_Off_Appointment_Subject() { + return this.getCmsContent("Same_Day_Drop_Off_Appointment_Widget", "HeaderText"); + }, + Same_Day_Drop_Off_Appointment_Body() { + return this.getCmsContent("Same_Day_Drop_Off_Appointment_Widget", "BodyText"); + }, + UniqueId() { + return store.getters.order.referralNumber?.toString(); + }, + ServiceType() { + const isRepair = store.getters.order.damage.isRepair; + const funnelHasRecalibrationPart = store.getters.funnelHasRecalibrationPart; + if (!isRepair) { + if (funnelHasRecalibrationPart) { + return serviceType.REPLACEMENT_AND_RECALIBRATION; + } else { + return serviceType.REPLACEMENT; + } + } + return serviceType.REPAIR; + }, + RouteCode() { + return store.getters.order.schedule.routeCode; + }, + AppointmentData() { + var subject = ""; + var location = ""; + var startDateTime = combineDateAndTime(this.ScheduleDate, this.ScheduleStartTime); + var endDateTime = combineDateAndTime(this.ScheduleDate, this.ScheduleEndTime); + var body = ""; + var isHTML = false; + var duration = ""; + + if (this.AppointmentType === AppointmentTypeStrings.MOBILE) { + location = this.ServiceLocationFullAddress?.replace("
", ""); + subject = this.Mobile_Appointment_Subject?.replace( + "{custom:SERVICETYPE}", + this.ServiceType + ); + body = this.Mobile_Appointment_Body; + } else { + location = this.ProviderFullAddress?.replace("
", ""); + if (this.AppointmentType === AppointmentTypeStrings.DROP_OFF) { + if (this.RouteCode.includes(RouteCodeFlags.OVERNIGHT_DROP_OFF)) { + subject = this.Overnight_Drop_Off_Appointment_Subject; + body = this.Overnight_Drop_Off_Appointment_Body; + } else if (this.RouteCode.includes(RouteCodeFlags.ALL_DAY_DROP_OFF)) { + if (this.IsSameDayDropOff) { + subject = this.Same_Day_Drop_Off_Appointment_Subject; + endDateTime = addMinutes(startDateTime, 120); + body = this.Same_Day_Drop_Off_Appointment_Body; + } else { + endDateTime = addMinutes(startDateTime, 120); + subject = this.All_Day_Drop_Off_Appointment_Subject?.replace( + "{custom:STARTDATETIME}", + shortTimeString(startDateTime) + ).replace("{custom:ENDDATETIME}", shortTimeString(endDateTime)); + body = this.All_Day_Drop_Off_Appointment_Body?.replace( + "{custom:ENDDATETIME}", + shortTimeString(endDateTime) + ); + } + } //normal time slot + else { + subject = this.Drop_Off_Appointment_Subject?.replace( + "{custom:SERVICETYPE}", + this.ServiceType + ); + body = this.Drop_Off_Appointment_Body?.replace( + "{custom:ADDRESS}", + location + ); + } + } else { + subject = this.InShop_Appointment_Subject?.replace( + "{custom:SERVICETYPE}", + this.ServiceType + ); + body = this.InShop_Appointment_Body?.replace("{custom:ADDRESS}", location); + } + duration = calculateDuration(startDateTime, endDateTime); + } + return { + Subject: subject, + Location: location, + StartDate: startDateTime, + EndDate: endDateTime, + RefrrlSeqNum: this.UniqueId, + Body: body, + IsHTML: isHTML, + Duration: duration, + }; + }, }, data() { return { diff --git a/src/layouts/customer-details/customer-details.vue b/src/layouts/customer-details/customer-details.vue index 6e5fc0f09..cd6cc17b0 100644 --- a/src/layouts/customer-details/customer-details.vue +++ b/src/layouts/customer-details/customer-details.vue @@ -8,7 +8,7 @@
- + - + @@ -229,7 +229,11 @@ export default { computed: { dynamicCSSUrl() { const { protocol, hostname, port } = window.location; - return `${protocol}//${hostname}:${port}/css/site-2020Styling.css`; + return `${protocol}//${hostname}:${port}/fmg/css/site-2020Styling.css`; + }, + dynamicHopCSSUrl() { + const { protocol, hostname, port } = window.location; + return `${protocol}//${hostname}:${port}/fmg/css/hop-styling.css`; }, }, methods: { diff --git a/src/layouts/review/review.vue b/src/layouts/review/review.vue index a5f6eca7c..f77e2b193 100644 --- a/src/layouts/review/review.vue +++ b/src/layouts/review/review.vue @@ -30,7 +30,7 @@ isPrimary :buttonText="forwardButtonText" loaderColor="white" - class="mb-2" + class="mb-2 w-100" @click-event="forwardButtonAction" />
diff --git a/src/layouts/schedule/location-alerts/location-alerts.vue b/src/layouts/schedule/location-alerts/location-alerts.vue index 66f26e263..80d7a5b00 100644 --- a/src/layouts/schedule/location-alerts/location-alerts.vue +++ b/src/layouts/schedule/location-alerts/location-alerts.vue @@ -3,7 +3,7 @@ v-for="alert in prefixedAlertReasons" :key="alert.cmsWidgetName" :ref="alert.cmsWidgetName" - class="mt-2 mb-3" + class="mt-5 mb-4" :cmsWidgetName="alert.cmsWidgetName" alertClass="alert-warning" /> diff --git a/src/layouts/service-location/service-location.spec.js b/src/layouts/service-location/service-location.spec.js index f8e9062b3..55ca775c4 100644 --- a/src/layouts/service-location/service-location.spec.js +++ b/src/layouts/service-location/service-location.spec.js @@ -7,7 +7,6 @@ import { getMountOptions } from "@/helpers/unit-test-helper"; import store from "@/store"; import baseMixin from "@/mixins/base-mixin"; -import { getServiceabilityDetails } from "@/layouts/service-location/helpers/service-location-helper/service-location-helper"; // Define Mocks jest.mock("@/helpers/cms-content-helper", () => ({ @@ -412,6 +411,9 @@ describe("service-location.vue", () => { const { wrapper } = setupMocks({}); await wrapper.setData({ + shopProviderData: { + mobileProviderNumber: "001820", + }, selectedAppointmentType: "Mobile", providerData: { mobileProviderNumber: "01820" }, }); @@ -464,6 +466,9 @@ describe("service-location.vue", () => { const { wrapper } = setupMocks({}); await wrapper.setData({ + shopProviderData: { + mobileProviderNumber: "001820", + }, selectedAppointmentType: "Mobile", providerData: { mobileProviderNumber: "01820" }, }); @@ -528,6 +533,9 @@ describe("service-location.vue", () => { const { wrapper } = setupMocks({}); await wrapper.setData({ + shopProviderData: { + mobileProviderNumber: "001820", + }, selectedAppointmentType: "Mobile", providerData: { mobileProviderNumber: "01820" }, }); @@ -984,14 +992,13 @@ describe("service-location.vue", () => { describe("should be based only on glass serviceability if recalibration is not defined.", () => { test("Should return true", async () => { // Arrange - getServiceabilityDetails.mockImplementation(() => + mockGetServiceabilityDetails = () => Promise.resolve({ isGlassServiceableInshop: true, isRecalibrationServiceableInshop: null, isGlassServiceableMobile: true, isRecalibrationServiceableMobile: null, - }) - ); + }); const { wrapper } = setupMocks({}); @@ -1012,14 +1019,13 @@ describe("service-location.vue", () => { test("Should return false", async () => { // Arrange - getServiceabilityDetails.mockImplementation(() => + mockGetServiceabilityDetails = () => Promise.resolve({ isGlassServiceableInshop: false, isRecalibrationServiceableInshop: null, isGlassServiceableMobile: false, isRecalibrationServiceableMobile: null, - }) - ); + }); const { wrapper } = setupMocks({}); @@ -1040,14 +1046,13 @@ describe("service-location.vue", () => { test("displayServiceableMobileOnly should be true if mobile is true and inshop is false.", async () => { // Arrange - getServiceabilityDetails.mockImplementation(() => + mockGetServiceabilityDetails = () => Promise.resolve({ isGlassServiceableInshop: false, isRecalibrationServiceableInshop: null, isGlassServiceableMobile: true, isRecalibrationServiceableMobile: null, - }) - ); + }); const { wrapper } = setupMocks({}); @@ -1069,14 +1074,13 @@ describe("service-location.vue", () => { test("displayServiceableMobileOnly should be false if mobile is false", async () => { // Arrange - getServiceabilityDetails.mockImplementation(() => + mockGetServiceabilityDetails = () => Promise.resolve({ isGlassServiceableInshop: false, isRecalibrationServiceableInshop: null, isGlassServiceableMobile: false, isRecalibrationServiceableMobile: null, - }) - ); + }); const { wrapper } = setupMocks({}); @@ -1098,14 +1102,13 @@ describe("service-location.vue", () => { test("displayServiceableMobileOnly should be false if inShop is true", async () => { // Arrange - getServiceabilityDetails.mockImplementation(() => + mockGetServiceabilityDetails = () => Promise.resolve({ isGlassServiceableInshop: true, isRecalibrationServiceableInshop: null, isGlassServiceableMobile: true, isRecalibrationServiceableMobile: null, - }) - ); + }); const { wrapper } = setupMocks({}); @@ -1127,14 +1130,13 @@ describe("service-location.vue", () => { test("requiresInshopRecalibration should not be true if recalibration info is null.", async () => { // Arrange - getServiceabilityDetails.mockImplementation(() => + mockGetServiceabilityDetails = () => Promise.resolve({ isGlassServiceableInshop: true, isRecalibrationServiceableInshop: null, isGlassServiceableMobile: true, isRecalibrationServiceableMobile: null, - }) - ); + }); const { wrapper } = setupMocks({}); @@ -1158,14 +1160,13 @@ describe("service-location.vue", () => { describe("should properly display alerts.", () => { test("Should show mobile-only error if only mobile is available.", async () => { // Arrange - getServiceabilityDetails.mockImplementation(() => + mockGetServiceabilityDetails = () => Promise.resolve({ isGlassServiceableInshop: false, isRecalibrationServiceableInshop: false, isGlassServiceableMobile: true, isRecalibrationServiceableMobile: true, - }) - ); + }); const { wrapper } = setupMocks({}); @@ -1187,14 +1188,13 @@ describe("service-location.vue", () => { test("Should not show mobile-only error if not mobile-only.", async () => { // Arrange - getServiceabilityDetails.mockImplementation(() => + mockGetServiceabilityDetails = () => Promise.resolve({ isGlassServiceableInshop: true, isRecalibrationServiceableInshop: true, isGlassServiceableMobile: true, isRecalibrationServiceableMobile: true, - }) - ); + }); const { wrapper } = setupMocks({}); @@ -1216,14 +1216,13 @@ describe("service-location.vue", () => { test("Should show no-shop error if no shops are available.", async () => { // Arrange - getServiceabilityDetails.mockImplementation(() => + mockGetServiceabilityDetails = () => Promise.resolve({ isGlassServiceableInshop: false, isRecalibrationServiceableInshop: false, isGlassServiceableMobile: false, isRecalibrationServiceableMobile: false, - }) - ); + }); const { wrapper } = setupMocks({}); @@ -1245,14 +1244,13 @@ describe("service-location.vue", () => { test("Should not show no-shop error if shops are available.", async () => { // Arrange - getServiceabilityDetails.mockImplementation(() => + mockGetServiceabilityDetails = () => Promise.resolve({ isGlassServiceableInshop: true, isRecalibrationServiceableInshop: true, isGlassServiceableMobile: true, isRecalibrationServiceableMobile: true, - }) - ); + }); const { wrapper } = setupMocks({}); @@ -1274,14 +1272,13 @@ describe("service-location.vue", () => { test("Should show inshop-only error if only inshop is available", async () => { // Arrange - getServiceabilityDetails.mockImplementation(() => + mockGetServiceabilityDetails = () => Promise.resolve({ isGlassServiceableInshop: true, isRecalibrationServiceableInshop: true, isGlassServiceableMobile: false, isRecalibrationServiceableMobile: false, - }) - ); + }); const { wrapper } = setupMocks({}); @@ -1303,14 +1300,13 @@ describe("service-location.vue", () => { test("Should not show inshop-only error if not inshop-only", async () => { // Arrange - getServiceabilityDetails.mockImplementation(() => + mockGetServiceabilityDetails = () => Promise.resolve({ isGlassServiceableInshop: false, isRecalibrationServiceableInshop: false, isGlassServiceableMobile: false, isRecalibrationServiceableMobile: false, - }) - ); + }); const { wrapper } = setupMocks({}); @@ -1332,14 +1328,13 @@ describe("service-location.vue", () => { test("Should not show inshop-only error if dual or static recalibration, but should show that error instead.", async () => { // Arrange - getServiceabilityDetails.mockImplementation(() => + mockGetServiceabilityDetails = () => Promise.resolve({ isGlassServiceableInshop: true, isRecalibrationServiceableInshop: true, isGlassServiceableMobile: true, isRecalibrationServiceableMobile: false, - }) - ); + }); const { wrapper } = setupMocks({}); @@ -1363,14 +1358,13 @@ describe("service-location.vue", () => { test("Should not show dual/static recalibration error if not those recalibration types", async () => { // Arrange - getServiceabilityDetails.mockImplementation(() => + mockGetServiceabilityDetails = () => Promise.resolve({ isGlassServiceableInshop: true, isRecalibrationServiceableInshop: true, isGlassServiceableMobile: true, isRecalibrationServiceableMobile: true, - }) - ); + }); const { wrapper } = setupMocks({}); diff --git a/src/layouts/service-location/service-location.vue b/src/layouts/service-location/service-location.vue index d1e818130..2a17766f9 100644 --- a/src/layouts/service-location/service-location.vue +++ b/src/layouts/service-location/service-location.vue @@ -502,15 +502,26 @@ export default { }, }, watch: { - selectedAppointmentType: { - async handler(newValue) { - if (newValue === "Mobile") { - getShopProviderData(this.zipCode).then(async (result) => { - this.shopProviderData = result.data; + zipCode: { + handler(newValue) { + getShopProviderData(this.zipCode).then(async (result) => { + this.shopProviderData = result.data; + if (this.selectedAppointmentType === "Mobile") { this.selectedProvider = new Provider( this.shopProviderData.mobileProviderNumber ); - }); + } else { + this.selectedProvider = new Provider(); + } + }); + }, + }, + selectedAppointmentType: { + handler(newValue) { + if (newValue === "Mobile") { + this.selectedProvider = new Provider( + this.shopProviderData.mobileProviderNumber + ); } else { this.selectedProvider = new Provider(); } diff --git a/src/layouts/service-location/shop-question/shop-question.vue b/src/layouts/service-location/shop-question/shop-question.vue index fa00ab1dc..57702f841 100644 --- a/src/layouts/service-location/shop-question/shop-question.vue +++ b/src/layouts/service-location/shop-question/shop-question.vue @@ -214,6 +214,8 @@ export default { }, shopProviders: { async handler(newValue) { + this.resetAnswers(); + await nextTick(); const selectedShopIndex = this.getSelectedProviderIndex( newValue,