Merge branch 'develop' into feature/CSR-1382-loose-ends
This commit is contained in:
commit
aa492f31e6
20 changed files with 1119 additions and 625 deletions
336
public/css/hop-styling.css
Normal file
336
public/css/hop-styling.css
Normal file
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -21,6 +21,10 @@ const applicationConfig = {
|
||||||
"//" +
|
"//" +
|
||||||
location.host +
|
location.host +
|
||||||
"/fmg/?fmgPage=payment-method&src=concept-funnel",
|
"/fmg/?fmgPage=payment-method&src=concept-funnel",
|
||||||
|
GOOGLE_CALENDAR: "https://www.google.com/calendar/render?action=TEMPLATE",
|
||||||
|
YAHOO_CALENDAR: "https://calendar.yahoo.com/?v=60",
|
||||||
|
OUTLOOK_CALENDAR:
|
||||||
|
"https://outlook.office.com/calendar/deeplink/compose?path=/calendar/action/compose&rru=addevent",
|
||||||
};
|
};
|
||||||
|
|
||||||
export { applicationConfig };
|
export { applicationConfig };
|
||||||
|
|
|
||||||
|
|
@ -41,18 +41,18 @@ export default {
|
||||||
isDisplayed() {
|
isDisplayed() {
|
||||||
return this.showAddRainDefense || this.showAddWipers;
|
return this.showAddRainDefense || this.showAddWipers;
|
||||||
},
|
},
|
||||||
// showAddRainDefense() {
|
showAddRainDefense() {
|
||||||
// // if not found in the cart
|
// if not found in the cart
|
||||||
// return !this.cartItems.some((vap) => {
|
return !this.cartItems.some((vap) => {
|
||||||
// return vap.partType === "RAIN DEFENSE";
|
return vap?.partType === "RAIN DEFENSE";
|
||||||
// });
|
});
|
||||||
// },
|
},
|
||||||
// showAddWipers() {
|
showAddWipers() {
|
||||||
// // if not found in the cart
|
// if not found in the cart
|
||||||
// return !this.cartItems.some((vap) => {
|
return !this.cartItems.some((vap) => {
|
||||||
// return vap.partType.includes(" WIPER");
|
return vap?.partType?.includes(" WIPER");
|
||||||
// });
|
});
|
||||||
// },
|
},
|
||||||
rainDefensePrice() {
|
rainDefensePrice() {
|
||||||
// const lineItem = this.rainDefenseItem;
|
// const lineItem = this.rainDefenseItem;
|
||||||
const lineItem = this.availableLineItems.find((item) => {
|
const lineItem = this.availableLineItems.find((item) => {
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@
|
||||||
class="my-4 caption modal-sub-body"
|
class="my-4 caption modal-sub-body"
|
||||||
v-if="ModalSubBodyText"
|
v-if="ModalSubBodyText"
|
||||||
v-html="ModalSubBodyText"></p>
|
v-html="ModalSubBodyText"></p>
|
||||||
|
<slot></slot>
|
||||||
</modal>
|
</modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -22,6 +23,10 @@ export default {
|
||||||
name: "content-group-modal",
|
name: "content-group-modal",
|
||||||
props: {
|
props: {
|
||||||
cmsWidgetName: String,
|
cmsWidgetName: String,
|
||||||
|
footerButtonActionName: {
|
||||||
|
type: String,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
ModalName() {
|
ModalName() {
|
||||||
|
|
@ -50,10 +55,16 @@ export default {
|
||||||
openModal() {
|
openModal() {
|
||||||
this.$refs[this.ModalName].openModal();
|
this.$refs[this.ModalName].openModal();
|
||||||
},
|
},
|
||||||
|
closeModal() {
|
||||||
footerButtonClick() {
|
|
||||||
this.$refs[this.ModalName].closeModal();
|
this.$refs[this.ModalName].closeModal();
|
||||||
},
|
},
|
||||||
|
footerButtonClick() {
|
||||||
|
if (this.footerButtonActionName) {
|
||||||
|
this.$emit(this.footerButtonActionName, this.ModalName);
|
||||||
|
} else {
|
||||||
|
this.closeModal();
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
modal,
|
modal,
|
||||||
|
|
|
||||||
|
|
@ -1,244 +0,0 @@
|
||||||
// Components
|
|
||||||
import addToCalendar from "@/layouts/add-to-calendar/add-to-calendar.vue";
|
|
||||||
import { calendarOptions } from "@/constants/calendar-options";
|
|
||||||
import { AppointmentTypeStrings, RouteCodeFlags } from "@/constants/schedule-constants";
|
|
||||||
|
|
||||||
// Supporting Files
|
|
||||||
import { shallowMount } from "@vue/test-utils";
|
|
||||||
import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
|
||||||
import store from "@/store";
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
jest.restoreAllMocks();
|
|
||||||
jest.clearAllMocks();
|
|
||||||
store.getters = {
|
|
||||||
order: {
|
|
||||||
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,
|
|
||||||
},
|
|
||||||
lineItems: {
|
|
||||||
glassParts: [],
|
|
||||||
supportingItems: [],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
afterEach(() => {
|
|
||||||
store.getters = {};
|
|
||||||
jest.restoreAllMocks();
|
|
||||||
jest.clearAllMocks();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("Add-to-calendar methods...", () => {
|
|
||||||
test("Add-to-calendar should trigger openModal method", () => {
|
|
||||||
//Arrange
|
|
||||||
const { wrapper } = setupMocks({});
|
|
||||||
|
|
||||||
//Act
|
|
||||||
wrapper.vm.openCalendarModal();
|
|
||||||
|
|
||||||
//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({});
|
|
||||||
|
|
||||||
//Act
|
|
||||||
const testValue = wrapper.vm.getCalendarData(type);
|
|
||||||
|
|
||||||
//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({});
|
|
||||||
wrapper.vm.getAppointment = jest.fn();
|
|
||||||
//Act
|
|
||||||
wrapper.vm.setCalendarAppointment(newValue);
|
|
||||||
|
|
||||||
//Assert
|
|
||||||
expect(wrapper.vm.getAppointment).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
test("setCalendarAppointment should call window.open for Google, yahoo and Outlook.com.", () => {
|
|
||||||
//Arrange
|
|
||||||
const newValue = { name: calendarOptions.GOOGLE };
|
|
||||||
const { wrapper } = setupMocks({});
|
|
||||||
window.open = jest.fn();
|
|
||||||
|
|
||||||
//Act
|
|
||||||
wrapper.vm.setCalendarAppointment(newValue);
|
|
||||||
|
|
||||||
//Assert
|
|
||||||
expect(window.open).toHaveBeenCalledTimes(1);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
function setupMocks({ customMountOptions }) {
|
|
||||||
const mountOptions = getMountOptions({
|
|
||||||
...customMountOptions,
|
|
||||||
});
|
|
||||||
|
|
||||||
const wrapper = shallowMount(addToCalendar, mountOptions);
|
|
||||||
wrapper.vm.$refs.calendarModalQuestion.openModal = jest.fn();
|
|
||||||
return { wrapper };
|
|
||||||
}
|
|
||||||
|
|
@ -1,301 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="calendar-section">
|
|
||||||
<div class="add-to-calendar">
|
|
||||||
<button v-on:click="openCalendarModal">
|
|
||||||
<span class="add-to-calendar-span">
|
|
||||||
<img src="@/assets/img/icons/add-to-calendar.svg" class="add-to-calendar-img" />
|
|
||||||
<span class="add-to-calendar-text">Add to calendar</span></span
|
|
||||||
>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<calendarModalQuestion
|
|
||||||
ref="calendarModalQuestion"
|
|
||||||
customComponentId="calendarModalQuestion"
|
|
||||||
cmsWidgetName="CalendarModalQuestion"
|
|
||||||
@calendar-modal-closed="calendarModalClosed"
|
|
||||||
:calendarOptionsData="calendarValues"
|
|
||||||
v-model="selectedCalendarOption">
|
|
||||||
</calendarModalQuestion>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
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 { 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,
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
appointment: this.getappointmentData(),
|
|
||||||
selectedCalendarOption: null,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
calendarValues: {
|
|
||||||
get: function () {
|
|
||||||
var obj = [];
|
|
||||||
const options = Object.values(calendarOptions);
|
|
||||||
for (var i = 0; i < options.length; i++) {
|
|
||||||
obj[i] = this.getCalendarData(options[i]);
|
|
||||||
}
|
|
||||||
return obj;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
selectedCalendarOption(newValue) {
|
|
||||||
if (newValue) {
|
|
||||||
this.setCalendarAppointment(newValue);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
setCalendarAppointment(calendarOption) {
|
|
||||||
if (
|
|
||||||
calendarOption.name.includes(calendarOptions.ICAL) ||
|
|
||||||
calendarOption.name.includes(calendarOptions.OUTLOOK)
|
|
||||||
) {
|
|
||||||
this.getAppointment();
|
|
||||||
} else {
|
|
||||||
window.open(calendarOption.url);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
calendarModalClosed() {
|
|
||||||
// Clear the selectedOption if close the modal
|
|
||||||
this.selectedCalendarOption = null;
|
|
||||||
},
|
|
||||||
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";
|
|
||||||
const outlookComTimeSpanFormat = "yyyy-MM-ddTHH:mm:ss";
|
|
||||||
if (type == calendarOptions.OUTLOOKCOM) {
|
|
||||||
URL = `https://outlook.office.com/calendar/deeplink/compose?path=/calendar/action/compose&rru=addevent&startdt=${encodeURIComponent(
|
|
||||||
getDateFormat(this.appointment.StartDate, outlookComTimeSpanFormat)
|
|
||||||
)}&enddt=${encodeURIComponent(
|
|
||||||
getDateFormat(this.appointment.EndDate, outlookComTimeSpanFormat)
|
|
||||||
)}&subject=${encodeURIComponent(
|
|
||||||
this.appointment.Subject
|
|
||||||
)}&body=${encodeURIComponent(this.appointment.Body)}&location=${encodeURIComponent(
|
|
||||||
this.appointment.Location
|
|
||||||
)}`;
|
|
||||||
}
|
|
||||||
if (type == calendarOptions.GOOGLE) {
|
|
||||||
URL = `https://www.google.com/calendar/render?action=TEMPLATE&text=${encodeURIComponent(
|
|
||||||
this.appointment.Subject
|
|
||||||
)}&dates=${encodeURIComponent(
|
|
||||||
getDateFormat(this.appointment.StartDate, dateFormat)
|
|
||||||
)}/${encodeURIComponent(
|
|
||||||
getDateFormat(this.appointment.EndDate, dateFormat)
|
|
||||||
)}&details=${encodeURIComponent(
|
|
||||||
this.appointment.Body
|
|
||||||
)}&location=${encodeURIComponent(this.appointment.Location)}&sf=true&output=xml`;
|
|
||||||
}
|
|
||||||
if (type == calendarOptions.YAHOO) {
|
|
||||||
URL = `https://calendar.yahoo.com/?v=60&TITLE=${encodeURIComponent(
|
|
||||||
this.appointment.Subject
|
|
||||||
)}&DESC=${encodeURIComponent(this.appointment.Body)}&ST=${encodeURIComponent(
|
|
||||||
getDateFormat(this.appointment.StartDate, dateFormat)
|
|
||||||
)}&DUR=${this.appointment.Duration}&in_loc=${encodeURIComponent(
|
|
||||||
this.appointment.Location
|
|
||||||
)}`;
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
url: URL,
|
|
||||||
name: type,
|
|
||||||
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("~", ">");
|
|
||||||
|
|
||||||
const calFile = {
|
|
||||||
Location: this.appointment.Location,
|
|
||||||
Body: body,
|
|
||||||
IsHTML: this.appointment.IsHTML,
|
|
||||||
UniqueId: this.appointment.RefrrlSeqNum,
|
|
||||||
Subject: this.appointment.Subject,
|
|
||||||
StartDate: this.appointment.StartDate,
|
|
||||||
EndDate: this.appointment.EndDate,
|
|
||||||
Status: calendarStatus.BUSY,
|
|
||||||
};
|
|
||||||
var calBody = getCalendarFile(calFile);
|
|
||||||
download("SafeliteAppointment.ics", calBody);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
<style scoped>
|
|
||||||
.add-to-calendar-text {
|
|
||||||
margin: 0 0 0 7px;
|
|
||||||
color: #1574a1;
|
|
||||||
vertical-align: middle;
|
|
||||||
font-weight: 500;
|
|
||||||
font-family: "Roboto";
|
|
||||||
}
|
|
||||||
.add-to-calendar button {
|
|
||||||
border: 0;
|
|
||||||
background-color: #fff;
|
|
||||||
}
|
|
||||||
.calendar-section {
|
|
||||||
display: table;
|
|
||||||
position: relative;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
.calendar-section .add-to-calendar {
|
|
||||||
padding-top: 15px;
|
|
||||||
}
|
|
||||||
.calendar-section .add-to-calendar .add-to-calendar-span {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.calendar-section .add-to-calendar .add-to-calendar-img {
|
|
||||||
display: inline;
|
|
||||||
width: 20px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
196
src/layouts/confirmation/add-to-calendar/add-to-calendar.spec.js
Normal file
196
src/layouts/confirmation/add-to-calendar/add-to-calendar.spec.js
Normal file
|
|
@ -0,0 +1,196 @@
|
||||||
|
// Components
|
||||||
|
import addToCalendar from "@/layouts/confirmation/add-to-calendar/add-to-calendar.vue";
|
||||||
|
import { calendarOptions } from "@/constants/calendar-options";
|
||||||
|
import { AppointmentTypeStrings, RouteCodeFlags } from "@/constants/schedule-constants";
|
||||||
|
|
||||||
|
// Supporting Files
|
||||||
|
import { shallowMount } from "@vue/test-utils";
|
||||||
|
import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
||||||
|
import store from "@/store";
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.restoreAllMocks();
|
||||||
|
jest.clearAllMocks();
|
||||||
|
store.getters = {
|
||||||
|
order: {
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
lineItems: {
|
||||||
|
glassParts: [],
|
||||||
|
supportingItems: [],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
afterEach(() => {
|
||||||
|
store.getters = {};
|
||||||
|
jest.restoreAllMocks();
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Add-to-calendar methods...", () => {
|
||||||
|
test("Add-to-calendar should trigger openModal method", () => {
|
||||||
|
//Arrange
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
expect(wrapper.vm.$refs.calendarModalQuestion.openModal).toBeCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("getCalendarData should return expected model value.", () => {
|
||||||
|
//Arrange
|
||||||
|
const type = calendarOptions.OUTLOOKCOM;
|
||||||
|
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);
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
expect(testValue.name).toEqual(calendarOptions.OUTLOOKCOM);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("setCalendarAppointment should call getAppointment method for iCAL and Outlook.", () => {
|
||||||
|
//Arrange
|
||||||
|
const newValue = { name: calendarOptions.OUTLOOK };
|
||||||
|
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);
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
expect(wrapper.vm.getAppointment).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
test("setCalendarAppointment should call window.open for Google, yahoo and Outlook.com.", () => {
|
||||||
|
//Arrange
|
||||||
|
const newValue = { name: calendarOptions.GOOGLE };
|
||||||
|
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
|
||||||
|
wrapper.vm.setCalendarAppointment(newValue);
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
expect(window.open).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
var appointmentText = "Appointment content";
|
||||||
|
function setupMocks({ customMountOptions }) {
|
||||||
|
const mountOptions = getMountOptions({
|
||||||
|
...customMountOptions,
|
||||||
|
});
|
||||||
|
mountOptions.mixins = [
|
||||||
|
{
|
||||||
|
methods: {
|
||||||
|
getCmsContent: jest.fn().mockImplementation(() => {
|
||||||
|
return appointmentText;
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const wrapper = shallowMount(addToCalendar, mountOptions);
|
||||||
|
wrapper.vm.$refs.calendarModalQuestion.openModal = jest.fn();
|
||||||
|
return { wrapper };
|
||||||
|
}
|
||||||
314
src/layouts/confirmation/add-to-calendar/add-to-calendar.vue
Normal file
314
src/layouts/confirmation/add-to-calendar/add-to-calendar.vue
Normal file
|
|
@ -0,0 +1,314 @@
|
||||||
|
<template>
|
||||||
|
<div class="calendar-section">
|
||||||
|
<div class="add-to-calendar">
|
||||||
|
<button v-on:click="openCalendarModal">
|
||||||
|
<span class="add-to-calendar-span">
|
||||||
|
<img src="@/assets/img/icons/add-to-calendar.svg" class="add-to-calendar-img" />
|
||||||
|
<span class="add-to-calendar-text">Add to calendar</span></span
|
||||||
|
>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<calendarModalQuestion
|
||||||
|
ref="calendarModalQuestion"
|
||||||
|
customComponentId="calendarModalQuestion"
|
||||||
|
cmsWidgetName="CalendarModalQuestion"
|
||||||
|
@calendar-modal-closed="calendarModalClosed"
|
||||||
|
:calendarOptionsData="calendarValues"
|
||||||
|
v-model="selectedCalendarOption">
|
||||||
|
</calendarModalQuestion>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { calendarOptions } from "@/constants/calendar-options";
|
||||||
|
import { calendarStatus } from "@/constants/calendar-status";
|
||||||
|
import calendarModalQuestion from "@/layouts/confirmation/add-to-calendar/calendar-modal-question/calendar-modal-question";
|
||||||
|
import { getDateFormat } from "@/helpers/date-helper";
|
||||||
|
import { AppointmentTypeStrings, RouteCodeFlags } from "@/constants/schedule-constants";
|
||||||
|
import { getCalendarFile, download } from "@/helpers/add-to-calendar-helper";
|
||||||
|
import {
|
||||||
|
calculateDuration,
|
||||||
|
combineDateAndTime,
|
||||||
|
shortTimeString,
|
||||||
|
addMinutes,
|
||||||
|
} from "@/helpers/date-helper";
|
||||||
|
import store from "@/store";
|
||||||
|
import { serviceType } from "@/constants/service-type";
|
||||||
|
import { applicationConfig } from "@/constants/application-config.js";
|
||||||
|
export default {
|
||||||
|
name: "add-to-calendar",
|
||||||
|
components: {
|
||||||
|
calendarModalQuestion,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
mobileWidgetName: String,
|
||||||
|
inShopWidgetName: String,
|
||||||
|
dropOffWidgetName: String,
|
||||||
|
overnightDropOffWidgetName: String,
|
||||||
|
allDayDropOffWidgetName: String,
|
||||||
|
sameDayDropOffWidgetName: String,
|
||||||
|
serviceLocationFullAddress: String,
|
||||||
|
providerFullAddress: String,
|
||||||
|
appointmentType: String,
|
||||||
|
scheduleDate: String,
|
||||||
|
scheduleStartTime: String,
|
||||||
|
scheduleEndTime: String,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
selectedCalendarOption: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
calendarValues: {
|
||||||
|
get: function () {
|
||||||
|
var obj = [];
|
||||||
|
const options = Object.values(calendarOptions);
|
||||||
|
for (var i = 0; i < options.length; i++) {
|
||||||
|
obj[i] = this.getCalendarData(options[i]);
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
IsSameDayDropOff() {
|
||||||
|
const todaysDate = new Date().toISOString().split("T")[0];
|
||||||
|
return this.scheduleDate === todaysDate;
|
||||||
|
},
|
||||||
|
AddToCalendar_Mobile_Subject() {
|
||||||
|
return this.getCmsContent(this.mobileWidgetName, "HeaderText");
|
||||||
|
},
|
||||||
|
AddToCalendar_Mobile_Body() {
|
||||||
|
return this.getCmsContent(this.mobileWidgetName, "BodyText");
|
||||||
|
},
|
||||||
|
AddToCalendar_InShop_Subject() {
|
||||||
|
return this.getCmsContent(this.inShopWidgetName, "HeaderText");
|
||||||
|
},
|
||||||
|
AddToCalendar_InShop_Body() {
|
||||||
|
return this.getCmsContent(this.inShopWidgetName, "BodyText");
|
||||||
|
},
|
||||||
|
AddToCalendar_DropOff_Subject() {
|
||||||
|
return this.getCmsContent(this.dropOffWidgetName, "HeaderText");
|
||||||
|
},
|
||||||
|
AddToCalendar_DropOff_Body() {
|
||||||
|
return this.getCmsContent(this.dropOffWidgetName, "BodyText");
|
||||||
|
},
|
||||||
|
AddToCalendar_OvernightDropOff_Subject() {
|
||||||
|
return this.getCmsContent(this.overnightDropOffWidgetName, "HeaderText");
|
||||||
|
},
|
||||||
|
AddToCalendar_OvernightDropOff_Body() {
|
||||||
|
return this.getCmsContent(this.overnightDropOffWidgetName, "BodyText");
|
||||||
|
},
|
||||||
|
AddToCalendar_AllDayDropOff_Subject() {
|
||||||
|
return this.getCmsContent(this.allDayDropOffWidgetName, "HeaderText");
|
||||||
|
},
|
||||||
|
AddToCalendar_AllDayDropOff_Body() {
|
||||||
|
return this.getCmsContent(this.allDayDropOffWidgetName, "BodyText");
|
||||||
|
},
|
||||||
|
AddToCalendar_SameDayDropOff_Subject() {
|
||||||
|
return this.getCmsContent(this.sameDayDropOffWidgetName, "HeaderText");
|
||||||
|
},
|
||||||
|
AddToCalendar_SameDayDropOff_Body() {
|
||||||
|
return this.getCmsContent(this.sameDayDropOffWidgetName, "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;
|
||||||
|
},
|
||||||
|
Appointment() {
|
||||||
|
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("<br/>", "");
|
||||||
|
subject = this.AddToCalendar_Mobile_Subject?.replace(
|
||||||
|
"{custom:SERVICETYPE}",
|
||||||
|
this.ServiceType
|
||||||
|
);
|
||||||
|
body = this.AddToCalendar_Mobile_Body;
|
||||||
|
} else {
|
||||||
|
location = this.providerFullAddress?.replace("<br/>", "");
|
||||||
|
if (this.appointmentType === AppointmentTypeStrings.DROP_OFF) {
|
||||||
|
if (this.RouteCode.includes(RouteCodeFlags.OVERNIGHT_DROP_OFF)) {
|
||||||
|
subject = this.AddToCalendar_OvernightDropOff_Subject;
|
||||||
|
body = this.AddToCalendar_OvernightDropOff_Body;
|
||||||
|
} else if (this.RouteCode.includes(RouteCodeFlags.ALL_DAY_DROP_OFF)) {
|
||||||
|
if (this.IsSameDayDropOff) {
|
||||||
|
subject = this.AddToCalendar_SameDayDropOff_Subject;
|
||||||
|
endDateTime = addMinutes(startDateTime, 120);
|
||||||
|
body = this.AddToCalendar_SameDayDropOff_Body;
|
||||||
|
} else {
|
||||||
|
endDateTime = addMinutes(startDateTime, 120);
|
||||||
|
subject = this.AddToCalendar_AllDayDropOff_Subject?.replace(
|
||||||
|
"{custom:STARTDATETIME}",
|
||||||
|
shortTimeString(startDateTime)
|
||||||
|
).replace("{custom:ENDDATETIME}", shortTimeString(endDateTime));
|
||||||
|
body = this.AddToCalendar_AllDayDropOff_Body?.replace(
|
||||||
|
"{custom:ENDDATETIME}",
|
||||||
|
shortTimeString(endDateTime)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} //normal time slot
|
||||||
|
else {
|
||||||
|
subject = this.AddToCalendar_DropOff_Subject?.replace(
|
||||||
|
"{custom:SERVICETYPE}",
|
||||||
|
this.ServiceType
|
||||||
|
);
|
||||||
|
body = this.AddToCalendar_DropOff_Body?.replace(
|
||||||
|
"{custom:ADDRESS}",
|
||||||
|
location
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
subject = this.AddToCalendar_InShop_Subject?.replace(
|
||||||
|
"{custom:SERVICETYPE}",
|
||||||
|
this.ServiceType
|
||||||
|
);
|
||||||
|
body = this.AddToCalendar_InShop_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,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
selectedCalendarOption(newValue) {
|
||||||
|
if (newValue) {
|
||||||
|
this.setCalendarAppointment(newValue);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
setCalendarAppointment(calendarOption) {
|
||||||
|
if (
|
||||||
|
calendarOption.name == calendarOptions.ICAL ||
|
||||||
|
calendarOption.name == calendarOptions.OUTLOOK
|
||||||
|
) {
|
||||||
|
this.getAppointment();
|
||||||
|
} else {
|
||||||
|
window.open(calendarOption.url);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
calendarModalClosed() {
|
||||||
|
// Clear the selectedOption if close the modal
|
||||||
|
this.selectedCalendarOption = null;
|
||||||
|
},
|
||||||
|
openCalendarModal() {
|
||||||
|
this.$refs["calendarModalQuestion"].openModal();
|
||||||
|
},
|
||||||
|
getCalendarData(type) {
|
||||||
|
var URL = "";
|
||||||
|
const dateFormat = "yyyyMMddTHHmmss";
|
||||||
|
const outlookComTimeSpanFormat = "yyyy-MM-ddTHH:mm:ss";
|
||||||
|
if (type == calendarOptions.OUTLOOKCOM) {
|
||||||
|
URL = `${applicationConfig.OUTLOOK_CALENDAR}&startdt=${encodeURIComponent(
|
||||||
|
getDateFormat(this.Appointment.StartDate, outlookComTimeSpanFormat)
|
||||||
|
)}&enddt=${encodeURIComponent(
|
||||||
|
getDateFormat(this.Appointment.EndDate, outlookComTimeSpanFormat)
|
||||||
|
)}&subject=${encodeURIComponent(
|
||||||
|
this.Appointment.Subject
|
||||||
|
)}&body=${encodeURIComponent(this.Appointment.Body)}&location=${encodeURIComponent(
|
||||||
|
this.Appointment.Location
|
||||||
|
)}`;
|
||||||
|
}
|
||||||
|
if (type == calendarOptions.GOOGLE) {
|
||||||
|
URL = `${applicationConfig.GOOGLE_CALENDAR}&text=${encodeURIComponent(
|
||||||
|
this.Appointment.Subject
|
||||||
|
)}&dates=${encodeURIComponent(
|
||||||
|
getDateFormat(this.Appointment.StartDate, dateFormat)
|
||||||
|
)}/${encodeURIComponent(
|
||||||
|
getDateFormat(this.Appointment.EndDate, dateFormat)
|
||||||
|
)}&details=${encodeURIComponent(
|
||||||
|
this.Appointment.Body
|
||||||
|
)}&location=${encodeURIComponent(this.Appointment.Location)}&sf=true&output=xml`;
|
||||||
|
}
|
||||||
|
if (type == calendarOptions.YAHOO) {
|
||||||
|
URL = `${applicationConfig.YAHOO_CALENDAR}&TITLE=${encodeURIComponent(
|
||||||
|
this.Appointment.Subject
|
||||||
|
)}&DESC=${encodeURIComponent(this.Appointment.Body)}&ST=${encodeURIComponent(
|
||||||
|
getDateFormat(this.Appointment.StartDate, dateFormat)
|
||||||
|
)}&DUR=${this.Appointment.Duration}&in_loc=${encodeURIComponent(
|
||||||
|
this.Appointment.Location
|
||||||
|
)}`;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
url: URL,
|
||||||
|
name: type,
|
||||||
|
icon: require(`@/assets/img/icons/${type}.svg`),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
getAppointment() {
|
||||||
|
let body = this.Appointment.Body;
|
||||||
|
body = body?.replace("|", "<").replace("~", ">");
|
||||||
|
|
||||||
|
const calFile = {
|
||||||
|
Location: this.Appointment.Location,
|
||||||
|
Body: body,
|
||||||
|
IsHTML: this.Appointment.IsHTML,
|
||||||
|
UniqueId: this.Appointment.RefrrlSeqNum,
|
||||||
|
Subject: this.Appointment.Subject,
|
||||||
|
StartDate: this.Appointment.StartDate,
|
||||||
|
EndDate: this.Appointment.EndDate,
|
||||||
|
Status: calendarStatus.BUSY,
|
||||||
|
};
|
||||||
|
var calBody = getCalendarFile(calFile);
|
||||||
|
download("SafeliteAppointment.ics", calBody);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
.add-to-calendar-text {
|
||||||
|
margin: 0 0 0 7px;
|
||||||
|
color: #1574a1;
|
||||||
|
vertical-align: middle;
|
||||||
|
font-weight: 500;
|
||||||
|
font-family: "Roboto";
|
||||||
|
}
|
||||||
|
.add-to-calendar button {
|
||||||
|
border: 0;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
.calendar-section {
|
||||||
|
display: table;
|
||||||
|
position: relative;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
.calendar-section .add-to-calendar {
|
||||||
|
padding-top: 15px;
|
||||||
|
}
|
||||||
|
.calendar-section .add-to-calendar .add-to-calendar-span {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.calendar-section .add-to-calendar .add-to-calendar-img {
|
||||||
|
display: inline;
|
||||||
|
width: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { shallowMount } from "@vue/test-utils";
|
import { shallowMount } from "@vue/test-utils";
|
||||||
import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
||||||
import calendarModalQuestion from "@/layouts/add-to-calendar/calendar-modal-question/calendar-modal-question.vue";
|
import calendarModalQuestion from "@/layouts/confirmation/add-to-calendar/calendar-modal-question/calendar-modal-question.vue";
|
||||||
|
|
||||||
describe("calendar-modal-question.vue", () => {
|
describe("calendar-modal-question.vue", () => {
|
||||||
test("When selected calendar option is changed, a correctly selectedCalendarOption should be emitted", async () => {
|
test("When selected calendar option is changed, a correctly selectedCalendarOption should be emitted", async () => {
|
||||||
|
|
@ -115,16 +115,16 @@ describe("confirmation.vue", () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe("computed properties...", () => {
|
describe("computed properties...", () => {
|
||||||
// test("ScheduleDateFormatted should return date in expected format.", () => {
|
test("ScheduleDateFormatted should return date in expected format.", () => {
|
||||||
// //Arrange
|
//Arrange
|
||||||
// const { wrapper } = setupMocks({});
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
// // Act
|
// Act
|
||||||
// const testValue = wrapper.vm.ScheduleDateFormatted;
|
const testValue = wrapper.vm.ScheduleDateFormatted;
|
||||||
|
|
||||||
// // Assert
|
// Assert
|
||||||
// expect(testValue).toEqual("Sunday, January 1");
|
expect(testValue).toEqual("Sunday, January 1");
|
||||||
// });
|
});
|
||||||
test("ScheduleTimeFormatted should return mobile time in expected format.", () => {
|
test("ScheduleTimeFormatted should return mobile time in expected format.", () => {
|
||||||
//Arrange
|
//Arrange
|
||||||
store.getters.order.serviceLocation.appointmentType = AppointmentTypeStrings.MOBILE;
|
store.getters.order.serviceLocation.appointmentType = AppointmentTypeStrings.MOBILE;
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,6 @@
|
||||||
<img :src="ScheduleConfirmationImage" />
|
<img :src="ScheduleConfirmationImage" />
|
||||||
<span v-html="ScheduleConfirmationText"></span>
|
<span v-html="ScheduleConfirmationText"></span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
|
||||||
class="appoinmenttext"
|
|
||||||
v-html="this.getAppointmentWordingText"></div>
|
|
||||||
|
|
||||||
<div class="main">
|
<div class="main">
|
||||||
<div>
|
<div>
|
||||||
<vehicleBanner
|
<vehicleBanner
|
||||||
|
|
@ -21,16 +17,26 @@
|
||||||
:displayGenericVehicleImage="false" />
|
:displayGenericVehicleImage="false" />
|
||||||
</div>
|
</div>
|
||||||
<div class="scheduleText">
|
<div class="scheduleText">
|
||||||
<p>{{ this.ScheduleDateFormatted }}</p>
|
<p>{{ ScheduleDateFormatted }}</p>
|
||||||
<p>{{ this.ScheduleTimeFormatted }}</p>
|
<p>{{ ScheduleTimeFormatted }}</p>
|
||||||
</div>
|
</div>
|
||||||
<addToCalendar></addToCalendar>
|
<addToCalendar
|
||||||
|
mobileWidgetName="AddToCalendar_Mobile"
|
||||||
|
inShopWidgetName="AddToCalendar_InShop"
|
||||||
|
dropOffWidgetName="AddToCalendar_DropOff"
|
||||||
|
overnightDropOffWidgetName="AddToCalendar_OvernightDropOff"
|
||||||
|
allDayDropOffWidgetName="AddToCalendar_AllDayDropOff"
|
||||||
|
sameDayDropOffWidgetName="AddToCalendar_SameDayDropOff"
|
||||||
|
:serviceLocationFullAddress="ServiceLocationFullAddress"
|
||||||
|
:providerFullAddress="ProviderFullAddress"
|
||||||
|
:appointmentType="AppointmentType"
|
||||||
|
:scheduleDate="ScheduleDate"
|
||||||
|
:scheduleStartTime="ScheduleStartTime"
|
||||||
|
:scheduleEndTime="ScheduleEndTime" />
|
||||||
|
|
||||||
<div
|
<div class="appoinmenttext" v-html="AppointmentWordingText"></div>
|
||||||
class="appoinmenttext"
|
|
||||||
v-html="this.AppointmentWordingText"></div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="emailtext" v-html="this.ConfirmationEmailText"></div>
|
<div class="emailtext" v-html="ConfirmationEmailText"></div>
|
||||||
<navbar
|
<navbar
|
||||||
cmsWidgetName="FunnelFooterWidget"
|
cmsWidgetName="FunnelFooterWidget"
|
||||||
ref="navbar"
|
ref="navbar"
|
||||||
|
|
@ -49,13 +55,14 @@
|
||||||
import funnelHeader from "@/fmg-components/funnel-header/funnel-header";
|
import funnelHeader from "@/fmg-components/funnel-header/funnel-header";
|
||||||
import vehicleBanner from "@/fmg-components/vehicle-banner/vehicle-banner";
|
import vehicleBanner from "@/fmg-components/vehicle-banner/vehicle-banner";
|
||||||
import navbar from "@/fmg-components/nav-bar/nav-bar";
|
import navbar from "@/fmg-components/nav-bar/nav-bar";
|
||||||
import addToCalendar from "@/layouts/add-to-calendar/add-to-calendar";
|
import addToCalendar from "@/layouts/confirmation/add-to-calendar/add-to-calendar";
|
||||||
//Supporting files
|
//Supporting files
|
||||||
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||||
import { AppointmentTypeStrings } from "@/constants/schedule-constants";
|
import { AppointmentTypeStrings } from "@/constants/schedule-constants";
|
||||||
import { settleAllPromises } from "@/helpers/layout-helper";
|
import { settleAllPromises } from "@/helpers/layout-helper";
|
||||||
import { applicationConfig } from "@/constants/application-config.js";
|
import { applicationConfig } from "@/constants/application-config.js";
|
||||||
import { convertDateStringToDate } from "@/layouts/schedule/helpers/schedule-helper";
|
import { convertDateStringToDate } from "@/layouts/schedule/helpers/schedule-helper";
|
||||||
|
|
||||||
import { Form } from "vee-validate";
|
import { Form } from "vee-validate";
|
||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
|
|
||||||
|
|
@ -89,9 +96,9 @@ export default {
|
||||||
},
|
},
|
||||||
ConfirmationEmailText() {
|
ConfirmationEmailText() {
|
||||||
return this.getCmsContent("ConfirmationEmailWidget", "BodyText")
|
return this.getCmsContent("ConfirmationEmailWidget", "BodyText")
|
||||||
.replaceAll("{custom:MY_ACCOUNT_URL}", applicationConfig.MY_ACCOUNT)
|
?.replaceAll("{custom:MY_ACCOUNT_URL}", applicationConfig.MY_ACCOUNT)
|
||||||
.replaceAll("<", "<")
|
?.replaceAll("<", "<")
|
||||||
.replaceAll(">", ">");
|
?.replaceAll(">", ">");
|
||||||
},
|
},
|
||||||
ScheduleDate() {
|
ScheduleDate() {
|
||||||
return store.getters.order.schedule.date;
|
return store.getters.order.schedule.date;
|
||||||
|
|
@ -143,17 +150,17 @@ export default {
|
||||||
},
|
},
|
||||||
AppointmentWordingText() {
|
AppointmentWordingText() {
|
||||||
if (this.AppointmentType == AppointmentTypeStrings.MOBILE) {
|
if (this.AppointmentType == AppointmentTypeStrings.MOBILE) {
|
||||||
return this.MobileWordingText.replaceAll(
|
return this.MobileWordingText?.replaceAll(
|
||||||
"{custom:ADDRESS}",
|
"{custom:ADDRESS}",
|
||||||
this.ServiceLocationFullAddress
|
this.ServiceLocationFullAddress
|
||||||
);
|
);
|
||||||
} else if (this.AppointmentType == AppointmentTypeStrings.DROP_OFF) {
|
} else if (this.AppointmentType == AppointmentTypeStrings.DROP_OFF) {
|
||||||
return this.DropOffWordingText.replaceAll(
|
return this.DropOffWordingText?.replaceAll(
|
||||||
"{custom:ADDRESS}",
|
"{custom:ADDRESS}",
|
||||||
this.ProviderFullAddress
|
this.ProviderFullAddress
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return this.InShopWordingText.replaceAll(
|
return this.InShopWordingText?.replaceAll(
|
||||||
"{custom:ADDRESS}",
|
"{custom:ADDRESS}",
|
||||||
this.ProviderFullAddress
|
this.ProviderFullAddress
|
||||||
);
|
);
|
||||||
|
|
@ -191,11 +198,6 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
workOrderNumber: store.getters.order.workOrderNumber,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
arePagePrerequisitesValid() {
|
arePagePrerequisitesValid() {
|
||||||
// Service Location
|
// Service Location
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,164 @@
|
||||||
|
<template>
|
||||||
|
<transition name="fade" mode="out-in">
|
||||||
|
<div class="add-vaps-buttons" aria-live="polite">
|
||||||
|
<buttonQuestion
|
||||||
|
ref="buttonQuestion"
|
||||||
|
:answers="answersToDisplay"
|
||||||
|
groupName="addVaps"
|
||||||
|
v-model="answersToDisplay"
|
||||||
|
buttonTypeString="addVapsButton"
|
||||||
|
:buttonTypeObject="addVapsButton"
|
||||||
|
:isWide="answersToDisplay.length > 1 ? false : true"
|
||||||
|
@update:modelValue="openModal" />
|
||||||
|
|
||||||
|
<contentGroupModal
|
||||||
|
ref="RainDefenseModal"
|
||||||
|
cmsWidgetName="RainDefenseModal"
|
||||||
|
v-if="rainDefenseItem"
|
||||||
|
@footer-button-action="addToCart('RainDefenseModal', rainDefenseItem)"
|
||||||
|
footerButtonActionName="footer-button-action">
|
||||||
|
<p class="price">{{ rainDefenseItem.listPrice }}</p>
|
||||||
|
</contentGroupModal>
|
||||||
|
|
||||||
|
<contentGroupModal
|
||||||
|
ref="WipersModal"
|
||||||
|
cmsWidgetName="WipersModal"
|
||||||
|
v-if="wipersItem"
|
||||||
|
@footer-button-action="addToCart('WipersModal', wipersItem)"
|
||||||
|
footerButtonActionName="footer-button-action">
|
||||||
|
<p class="price">{{ wipersItem.listPrice }}</p>
|
||||||
|
</contentGroupModal>
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import buttonQuestion from "@/digital-components/button-question/button-question";
|
||||||
|
import addVapsButton from "./add-vaps-button/add-vaps-button";
|
||||||
|
import contentGroupModal from "@/fmg-components/content-group-modal/content-group-modal";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "add-vaps-modal-buttons",
|
||||||
|
props: {
|
||||||
|
vapsTilesCmsName: String,
|
||||||
|
availableLineItems: Object,
|
||||||
|
modelValue: Object,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
addVapsButton: addVapsButton,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
openModal(modalName) {
|
||||||
|
this.$refs[modalName].openModal();
|
||||||
|
},
|
||||||
|
addToCart(modalName, part) {
|
||||||
|
const vapsItems = this.currentCartItems.vaps;
|
||||||
|
const alreadyInVaps = vapsItems.some((item) => {
|
||||||
|
return item.partType === part.partType;
|
||||||
|
});
|
||||||
|
if (!alreadyInVaps) {
|
||||||
|
if (part.subItems) {
|
||||||
|
// for wipers, a "combined" part (w/ subItems)
|
||||||
|
part.subItems.forEach((part) => {
|
||||||
|
this.currentCartItems.vaps.push(part);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// for rain defense, a "single" part (w/o subItems)
|
||||||
|
this.currentCartItems.vaps.push(part);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.$refs[modalName].closeModal();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
currentCartItems() {
|
||||||
|
return this.modelValue;
|
||||||
|
},
|
||||||
|
showAddRainDefense() {
|
||||||
|
// show if not in cart
|
||||||
|
return !this.currentCartItems.vaps?.some((vap) => {
|
||||||
|
return vap?.partType === "RAIN DEFENSE";
|
||||||
|
});
|
||||||
|
},
|
||||||
|
showAddWipers() {
|
||||||
|
// show if not in cart
|
||||||
|
if (this.currentCartItems && this.currentCartItems.vaps) {
|
||||||
|
return !this.currentCartItems.vaps.some((vap) => {
|
||||||
|
return vap?.partType?.includes(" WIPER");
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
rainDefenseItem() {
|
||||||
|
const lineItem = this.availableLineItems.find((item) => {
|
||||||
|
return item.partType === "RAIN DEFENSE";
|
||||||
|
});
|
||||||
|
if (lineItem)
|
||||||
|
lineItem.listPrice = parseFloat(
|
||||||
|
lineItem?.laborAmount + lineItem?.sellingPrice + lineItem?.kitPrice
|
||||||
|
).toFixed(2);
|
||||||
|
return lineItem;
|
||||||
|
},
|
||||||
|
wipersItem() {
|
||||||
|
const wiperLineItems = this.availableLineItems.filter((item) => {
|
||||||
|
return item.partType.includes("WIPER");
|
||||||
|
});
|
||||||
|
let wiperLineItem;
|
||||||
|
if (wiperLineItems.length === 1) {
|
||||||
|
wiperLineItem = wiperLineItems[0];
|
||||||
|
wiperLineItem.listPrice = parseFloat(
|
||||||
|
wiperLineItem.laborAmount + wiperLineItem.sellingPrice + wiperLineItem.kitPrice
|
||||||
|
).toFixed(2);
|
||||||
|
return wiperLineItem;
|
||||||
|
} else if (wiperLineItems.length > 1) {
|
||||||
|
let combinedPrice = 0;
|
||||||
|
wiperLineItems?.forEach((item) => {
|
||||||
|
combinedPrice += item?.laborAmount + item?.sellingPrice + item?.kitPrice;
|
||||||
|
});
|
||||||
|
wiperLineItem = {
|
||||||
|
description: "COMBINED ITEM",
|
||||||
|
partType: "WIPERS",
|
||||||
|
subItems: [],
|
||||||
|
listPrice: parseFloat(combinedPrice).toFixed(2),
|
||||||
|
};
|
||||||
|
wiperLineItems?.forEach((item) => {
|
||||||
|
wiperLineItem.subItems.push(item);
|
||||||
|
});
|
||||||
|
return wiperLineItem;
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
},
|
||||||
|
answersCmsData() {
|
||||||
|
return this.getCmsContent(this.vapsTilesCmsName, "Answers");
|
||||||
|
},
|
||||||
|
answersToDisplay() {
|
||||||
|
const answers = [];
|
||||||
|
if (!this.answersCmsData) return answers;
|
||||||
|
const getAnswer = (name, answers) => {
|
||||||
|
const answerIndex = answers.findIndex((answer) => {
|
||||||
|
return answer.Name === name;
|
||||||
|
});
|
||||||
|
return answers[answerIndex];
|
||||||
|
};
|
||||||
|
if (this.showAddWipers) {
|
||||||
|
const answer = getAnswer("WipersModal", this.answersCmsData);
|
||||||
|
answer.SubText = "+$" + this.wipersItem.listPrice;
|
||||||
|
answers.push(answer);
|
||||||
|
}
|
||||||
|
if (this.showAddRainDefense) {
|
||||||
|
const answer = getAnswer("RainDefenseModal", this.answersCmsData);
|
||||||
|
answer.SubText = "+$" + this.rainDefenseItem.listPrice;
|
||||||
|
answers.push(answer);
|
||||||
|
}
|
||||||
|
return answers;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
buttonQuestion,
|
||||||
|
contentGroupModal,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
@ -27,7 +27,7 @@
|
||||||
<cart
|
<cart
|
||||||
:damage="damageInfo"
|
:damage="damageInfo"
|
||||||
:availableLineItems="availableLineItems"
|
:availableLineItems="availableLineItems"
|
||||||
:lineItems="lineItems"
|
:lineItems="taxedLineItems"
|
||||||
servicePackageOptionsCmsName="ServicePackageTitle"
|
servicePackageOptionsCmsName="ServicePackageTitle"
|
||||||
@cart-remove="cartRemove" />
|
@cart-remove="cartRemove" />
|
||||||
|
|
||||||
|
|
@ -42,12 +42,10 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<add-vaps-modal-buttons
|
<add-vaps-modal-buttons
|
||||||
:cartItems="cartItems"
|
v-model="lineItems"
|
||||||
:availableLineItems="availableLineItems"
|
:availableLineItems="availableLineItems"
|
||||||
vapsTilesCmsName="VapsProductTiles"
|
vapsTilesCmsName="VapsProductTiles" />
|
||||||
v-on="{ 'buttonEvent.openModal': openModal }" />
|
|
||||||
|
|
||||||
<contentGroupModal ref="RainDefenseModal" cmsWidgetName="RainDefenseModal" />
|
|
||||||
<paymentMethodQuestion
|
<paymentMethodQuestion
|
||||||
v-if="!isPiaDisabled"
|
v-if="!isPiaDisabled"
|
||||||
v-model="paymentMethodInternalModel"
|
v-model="paymentMethodInternalModel"
|
||||||
|
|
@ -74,11 +72,11 @@ import funnelSubHeader from "@/fmg-components/funnel-sub-header/funnel-sub-heade
|
||||||
import paymentMethodQuestion from "@/layouts/payment-method/payment-method-question/payment-method-question";
|
import paymentMethodQuestion from "@/layouts/payment-method/payment-method-question/payment-method-question";
|
||||||
import loadingModal from "@/fmg-components/loading-modal/loading-modal.vue";
|
import loadingModal from "@/fmg-components/loading-modal/loading-modal.vue";
|
||||||
import cart from "@/fmg-components/cart/cart";
|
import cart from "@/fmg-components/cart/cart";
|
||||||
import addVapsModalButtons from "@/fmg-components/add-vaps-modal-buttons/add-vaps-modal-buttons";
|
import addVapsModalButtons from "./add-vaps-modal-buttons/add-vaps-modal-buttons";
|
||||||
import contentGroupModal from "@/fmg-components/content-group-modal/content-group-modal";
|
|
||||||
import { submitWorkOrder } from "@/helpers/heritage-integration/order-helper.js";
|
import { submitWorkOrder } from "@/helpers/heritage-integration/order-helper.js";
|
||||||
import alert from "@/ux-components/alert/alert";
|
import alert from "@/ux-components/alert/alert";
|
||||||
|
|
||||||
|
// Supporting Items
|
||||||
import baseMixin from "@/mixins/base-mixin.js";
|
import baseMixin from "@/mixins/base-mixin.js";
|
||||||
import { settleAllPromises } from "@/helpers/layout-helper";
|
import { settleAllPromises } from "@/helpers/layout-helper";
|
||||||
import { storeActions } from "@/constants/store-actions";
|
import { storeActions } from "@/constants/store-actions";
|
||||||
|
|
@ -182,6 +180,7 @@ export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
lineItems: [],
|
lineItems: [],
|
||||||
|
taxedLineItems: [], // temporary, for use by cart.vue
|
||||||
availableLineItems: [],
|
availableLineItems: [],
|
||||||
paymentMethodInternalModel: this.getPaymentMethodFromStore(),
|
paymentMethodInternalModel: this.getPaymentMethodFromStore(),
|
||||||
displayPiaAlert: this.getPiaAlert(),
|
displayPiaAlert: this.getPiaAlert(),
|
||||||
|
|
@ -243,9 +242,6 @@ export default {
|
||||||
packageReqs && serviceLocationReqs && isInsuranceSet && scheduleReqs && customerReqs
|
packageReqs && serviceLocationReqs && isInsuranceSet && scheduleReqs && customerReqs
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
openModal(modalName) {
|
|
||||||
this.$refs[modalName].openModal();
|
|
||||||
},
|
|
||||||
getPiaAlert() {
|
getPiaAlert() {
|
||||||
return store.getters.order.payment.piaErrorCode ? true : false;
|
return store.getters.order.payment.piaErrorCode ? true : false;
|
||||||
},
|
},
|
||||||
|
|
@ -262,13 +258,6 @@ export default {
|
||||||
return paymentMethods.LATER;
|
return paymentMethods.LATER;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
vapsItemsSelectedAction(vapsItemsSelected) {
|
|
||||||
this.cartItems = vapsItemsSelected;
|
|
||||||
},
|
|
||||||
// TODO (as part of CSR-1384) enable adding to the cart
|
|
||||||
// vapsItemsSelectedAction(vapsItemsSelected) {
|
|
||||||
// this.cartItems = vapsItemsSelected;
|
|
||||||
// },
|
|
||||||
cartRemove(item) {
|
cartRemove(item) {
|
||||||
const matchedIndices = [];
|
const matchedIndices = [];
|
||||||
this.cartItems.forEach((cartItem, i) => {
|
this.cartItems.forEach((cartItem, i) => {
|
||||||
|
|
@ -311,7 +300,6 @@ export default {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async piaSetup(payNowType) {
|
async piaSetup(payNowType) {
|
||||||
this.$refs.loadingModal.showModal();
|
this.$refs.loadingModal.showModal();
|
||||||
|
|
||||||
|
|
@ -389,7 +377,6 @@ export default {
|
||||||
cart,
|
cart,
|
||||||
alert,
|
alert,
|
||||||
addVapsModalButtons,
|
addVapsModalButtons,
|
||||||
contentGroupModal,
|
|
||||||
paymentMethodQuestion,
|
paymentMethodQuestion,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@
|
||||||
|
|
||||||
<input type="hidden" name="maskCharacter" value="*" />
|
<input type="hidden" name="maskCharacter" value="*" />
|
||||||
<input type="hidden" name="styleSheetCode" :value="dynamicCSSUrl" />
|
<input type="hidden" name="styleSheetCode" :value="dynamicCSSUrl" />
|
||||||
<input type="hidden" name="styleSheetCode2" :value="dynamicCSSUrl" />
|
<input type="hidden" name="styleSheetCode2" :value="dynamicHopCSSUrl" />
|
||||||
|
|
||||||
<input type="hidden" name="sgReceiptResponseURL" :value="piaResponseUrl" />
|
<input type="hidden" name="sgReceiptResponseURL" :value="piaResponseUrl" />
|
||||||
<input type="hidden" name="sgDeclineResponseURL" :value="piaResponseUrl" />
|
<input type="hidden" name="sgDeclineResponseURL" :value="piaResponseUrl" />
|
||||||
|
|
@ -229,7 +229,11 @@ export default {
|
||||||
computed: {
|
computed: {
|
||||||
dynamicCSSUrl() {
|
dynamicCSSUrl() {
|
||||||
const { protocol, hostname, port } = window.location;
|
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: {
|
methods: {
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,19 @@ import vehicle from "@/layouts/vehicle/vehicle.vue";
|
||||||
import { shallowMount } from "@vue/test-utils";
|
import { shallowMount } from "@vue/test-utils";
|
||||||
import { nextTick } from "vue";
|
import { nextTick } from "vue";
|
||||||
import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
||||||
|
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||||
|
import baseMixin from "../../mixins/base-mixin";
|
||||||
|
|
||||||
// Mock our module for promises.
|
// Mock our module for promises.
|
||||||
jest.mock("@/helpers/layout-helper.js", () => ({
|
jest.mock("@/helpers/layout-helper.js", () => ({
|
||||||
settleAllPromises: jest.fn(),
|
settleAllPromises: jest.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
// Mock fetchCmsContentForPage
|
||||||
|
jest.mock("@/helpers/cms-content-helper", () => ({
|
||||||
|
fetchCmsContentForPage: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
describe("vehicle.vue", () => {
|
describe("vehicle.vue", () => {
|
||||||
test("arePagePrerequisitesValid should be true ", async () => {
|
test("arePagePrerequisitesValid should be true ", async () => {
|
||||||
//Arrange
|
//Arrange
|
||||||
|
|
@ -40,17 +47,24 @@ describe("vehicle.vue", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
function setupMocks() {
|
function setupMocks() {
|
||||||
const wrapper = shallowMount(
|
const mountOptions = getMountOptions({
|
||||||
vehicle,
|
router: {
|
||||||
getMountOptions({
|
navigate: jest.fn(),
|
||||||
router: {
|
navigate: jest.fn(),
|
||||||
navigate: jest.fn(),
|
navigateWithSaving: jest.fn(),
|
||||||
navigate: jest.fn(),
|
navigateWithoutSaving: jest.fn(),
|
||||||
navigateWithSaving: jest.fn(),
|
},
|
||||||
navigateWithoutSaving: jest.fn(),
|
});
|
||||||
},
|
|
||||||
})
|
//Mock props
|
||||||
);
|
const mockMixin = {
|
||||||
|
methods: {
|
||||||
|
getCmsContent: jest.fn(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
mountOptions.mixins = [mockMixin];
|
||||||
|
const wrapper = shallowMount(vehicle, mountOptions);
|
||||||
|
|
||||||
return { wrapper };
|
return { wrapper };
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,10 @@
|
||||||
<div class="row justify-content-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-md-6 col-xl-4">
|
<div class="col-md-6 col-xl-4">
|
||||||
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" class="siteSubHeader" />
|
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" class="siteSubHeader" />
|
||||||
|
<div
|
||||||
|
cmsWidgetName="FindYourVehicleCopyWidget"
|
||||||
|
v-html="this.findYourVehicleCopy"
|
||||||
|
class="mt-2 mb-4 small"></div>
|
||||||
<vehicleQuestion
|
<vehicleQuestion
|
||||||
ref="vehicleYearQuestion"
|
ref="vehicleYearQuestion"
|
||||||
v-model="selectedYear"
|
v-model="selectedYear"
|
||||||
|
|
@ -294,6 +298,9 @@ export default {
|
||||||
else if (this.imageUrl == null && this.carId !== null) return false;
|
else if (this.imageUrl == null && this.carId !== null) return false;
|
||||||
else return true;
|
else return true;
|
||||||
},
|
},
|
||||||
|
findYourVehicleCopy() {
|
||||||
|
return this.getCmsContent("FindYourVehicleCopyWidget", "QuestionText");
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getVehicle(year, make, model, style) {
|
getVehicle(year, make, model, style) {
|
||||||
|
|
@ -430,6 +437,6 @@ export default {
|
||||||
}
|
}
|
||||||
.siteSubHeader {
|
.siteSubHeader {
|
||||||
margin-top: 1.5rem;
|
margin-top: 1.5rem;
|
||||||
margin-bottom: 1.5rem;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="alert fade show text-center mb-0 py-2 px-4"
|
class="alert fade show text-center mb-0 py-2 px-4 border-0"
|
||||||
role="alert"
|
role="alert"
|
||||||
:class="[
|
:class="[
|
||||||
isDismissible ? 'alert-dismissible' : '',
|
isDismissible ? 'alert-dismissible' : '',
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue