diff --git a/public/css/hop-styling.css b/public/css/hop-styling.css index 46607424..66d1b1aa 100644 --- a/public/css/hop-styling.css +++ b/public/css/hop-styling.css @@ -3,6 +3,11 @@ /* IMPORTANT: READ README.md in this folder! Do not edit .css file directly! */ /* IMPORTANT: READ README.md in this folder! Do not edit .css file directly! */ @import url("https://fonts.googleapis.com/css2?family=Urbanist:ital,wght@0,100..900;1,100..900&display=swap"); +@font-face { + font-family: "Glyphicons Halflings"; + src: url(/fonts/glyphicons-halflings-regular.eot); + src: url(/fonts/glyphicons-halflings-regular.eot?#iefix) format("embedded-opentype"), url(/fonts/glyphicons-halflings-regular.woff2) format("woff2"), url(/fonts/glyphicons-halflings-regular.woff) format("woff"), url(/fonts/glyphicons-halflings-regular.ttf) format("truetype"), url(/fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format("svg"); +} body { font-family: Urbanist, Roboto, Arial, sans-serif; font-weight: 400; diff --git a/public/fonts/glyphicons-halflings-regular.eot b/public/fonts/glyphicons-halflings-regular.eot new file mode 100644 index 00000000..4a4ca865 Binary files /dev/null and b/public/fonts/glyphicons-halflings-regular.eot differ diff --git a/public/fonts/glyphicons-halflings-regular.svg b/public/fonts/glyphicons-halflings-regular.svg new file mode 100644 index 00000000..e3e2dc73 --- /dev/null +++ b/public/fonts/glyphicons-halflings-regular.svg @@ -0,0 +1,229 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/fonts/glyphicons-halflings-regular.ttf b/public/fonts/glyphicons-halflings-regular.ttf new file mode 100644 index 00000000..67fa00bf Binary files /dev/null and b/public/fonts/glyphicons-halflings-regular.ttf differ diff --git a/public/fonts/glyphicons-halflings-regular.woff b/public/fonts/glyphicons-halflings-regular.woff new file mode 100644 index 00000000..8c54182a Binary files /dev/null and b/public/fonts/glyphicons-halflings-regular.woff differ diff --git a/public/fonts/glyphicons-halflings-regular.woff2 b/public/fonts/glyphicons-halflings-regular.woff2 new file mode 100644 index 00000000..64539b54 Binary files /dev/null and b/public/fonts/glyphicons-halflings-regular.woff2 differ diff --git a/public/scss/hop-styling.scss b/public/scss/hop-styling.scss index f32912e8..6e2c274a 100644 --- a/public/scss/hop-styling.scss +++ b/public/scss/hop-styling.scss @@ -3,6 +3,12 @@ /* IMPORTANT: READ README.md in this folder! Do not edit .css file directly! */ /* IMPORTANT: READ README.md in this folder! Do not edit .css file directly! */ @import url("https://fonts.googleapis.com/css2?family=Urbanist:ital,wght@0,100..900;1,100..900&display=swap"); + +@font-face { + font-family: 'Glyphicons Halflings'; + src: url(/fonts/glyphicons-halflings-regular.eot); + src: url(/fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'), url(/fonts/glyphicons-halflings-regular.woff2) format('woff2'), url(/fonts/glyphicons-halflings-regular.woff) format('woff'), url(/fonts/glyphicons-halflings-regular.ttf) format('truetype'), url(/fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg'); +} body { font-family: Urbanist, Roboto, Arial, sans-serif; font-weight: 400; diff --git a/src/iss-components/google-map/google-map.vue b/src/iss-components/google-map/google-map.vue index cf07efe7..273c382c 100644 --- a/src/iss-components/google-map/google-map.vue +++ b/src/iss-components/google-map/google-map.vue @@ -29,7 +29,10 @@ export default { } }, async beforeMount() { - await this.createMapWithMarkersForAddresses(this.markers); + await this.createMapWithMarkersForAddresses(this.markers) + .catch(() => { + console.error('Error creating map with markers: if handled jest fails to run tests'); + }); }, methods: { async getMap(center) { diff --git a/src/layouts/schedule-page/schedule-page.vue b/src/layouts/schedule-page/schedule-page.vue index b9cb3ba3..db90a0eb 100644 --- a/src/layouts/schedule-page/schedule-page.vue +++ b/src/layouts/schedule-page/schedule-page.vue @@ -20,6 +20,7 @@ :schedulingInshopAvailabilityRating="inShopAvailabilityRating" @appointmentTypeChanged="appointmentTypeChangedFromServiceLocation" @cityUpdated="cityUpdatedFromServiceLocation" + @clearMobileData="clearMobileDataFromServiceLocation" @inShopZipUpdated="inShopZipUpdatedFromServiceLocation" @mobileZipUpdated="mobileZipUpdatedFromServiceLocation" @providerChanged="providerChangedFromServiceLocation" /> @@ -89,9 +90,8 @@ import { import settleAllPromises from '@/helpers/layout-helper'; import showIssLoadingModal from '@/helpers/loading-modal-helper.js'; import { - getProviderData, - getZipCodeData, - onProviderChanged + getPricedMobileFeePart, + getZipCodeData } from '@/helpers/service-location-helper'; import { Form } from 'vee-validate'; import BaseFormMixin from '@/mixins/base-form-mixin.js'; @@ -225,9 +225,16 @@ export default { async beforeRouteEnter(to, from, next) { // Call APIs const cmsContentPromise = fetchCmsContentForPage(to.query.issPage); - const { isMobileAppointment, storeServiceLocation, storeSelectedProvider, serviceZipCode } = getProviderData(); + const storeServiceLocation = useMainStore().order.serviceLocation; + const storeSelectedAppointmentType = storeServiceLocation?.appointmentType; + const isMobileAppointment = storeSelectedAppointmentType === AppointmentTypeStrings.MOBILE + || storeSelectedAppointmentType === AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP; + const storeSelectedProvider = storeServiceLocation?.provider; + const serviceZipCode = storeServiceLocation?.zipCode || useMainStore().order.customer.address.zipCode; const zipCodeData = getZipCodeData(serviceZipCode); + const mobileFeePartPromise = getPricedMobileFeePart(serviceZipCode); const serviceabilityDetailsPromise = useMainStore().getServiceabilityDetails(serviceZipCode); + const getGlassFeesPromise = useMainStore().getGlassFees(); const providersPromise = useMainStore().getSafeliteProviders(serviceZipCode, 150); const premiumFeePromise = useMainStore().getMobilePremiumFee(); @@ -244,6 +251,14 @@ export default { resultKey: 'cmsContent', promise: cmsContentPromise }, + { + resultKey: 'mobileFeePart', + promise: mobileFeePartPromise + }, + { + resultKey: 'glassFees', + promise: getGlassFeesPromise + }, { resultKey: 'serviceabilityDetails', promise: serviceabilityDetailsPromise @@ -256,10 +271,6 @@ export default { resultKey: 'providers', promise: providersPromise }, - { - resultKey: 'providerChanged', - promise: onProviderChanged() - }, { resultKey: 'premiumFeeWithPrice', promise: premiumFeeWithPricePromise @@ -319,8 +330,10 @@ export default { return { inShopAvailabilityRating: 'high', inShopDatesData: [], + isDatePickerRefreshing: false, isMobileView: false, mobileDatesData: [], + mobileFeePart: null, mobilePremiumAppointmentFee: null, mobileProviderNumber: null, selectableDatesData: [], @@ -341,14 +354,18 @@ export default { return false; } + const validMobileAppointmentType = ((this.selectedAppointmentType === AppointmentTypeStrings.MOBILE + || this.selectedAppointmentType === AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP) + && serviceLocationToUse.mobileProviderNumber); + const validInShopAppointmentType = ((this.selectedAppointmentType === AppointmentTypeStrings.IN_SHOP + || this.selectedAppointmentType === AppointmentTypeStrings.DROP_OFF) + && (this.selectedProvider?.providerNumber || serviceLocationToUse.provider?.providerNumber)); + const serviceLocationPreReqs = serviceLocationToUse.zipCode !== null && serviceLocationToUse.zipCodeCtu !== null && serviceLocationToUse.appointmentType !== null - && (((this.selectedAppointmentType === AppointmentTypeStrings.MOBILE - || this.selectedAppointmentType === AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP) - && serviceLocationToUse.mobileProviderNumber) - || ((this.selectedAppointmentType === AppointmentTypeStrings.IN_SHOP || this.selectedAppointmentType === AppointmentTypeStrings.DROP_OFF) - && (this.selectedProvider?.providerNumber || serviceLocationToUse.provider?.providerNumber))); + && (validMobileAppointmentType || validInShopAppointmentType) + && !this.isDatePickerRefreshing; const damageInfo = useMainStore().order.damage.isRepair || (useMainStore().order.lineItems?.glassParts != null && useMainStore().order.lineItems.glassParts.length > 0); @@ -453,6 +470,14 @@ export default { cityUpdatedFromServiceLocation(newCity) { this.selectedServiceLocationCity = newCity; }, + clearMobileDataFromServiceLocation() { + if (this.selectedServiceLocation) { + this.selectedServiceLocation.mobileProviderNumber = null; + } + this.mobileDatesData = []; + this.mobileProviderNumber = null; + this.selectedMobileZipCode = null; + }, dateSelectedFromPicker(date) { this.selectedDate = date; this.showDatePickerError = false; @@ -647,9 +672,7 @@ export default { } }, mobileZipUpdatedFromServiceLocation(mobileZipServiceLocation) { - if (mobileZipServiceLocation - && (this.mobileProviderNumber !== mobileZipServiceLocation.mobileProviderNumber - || !this.hasNeededServiceLocationData)) { + if (mobileZipServiceLocation) { this.mobileProviderNumber = mobileZipServiceLocation.mobileProviderNumber; this.selectedMobileZipCode = mobileZipServiceLocation.zipCode; this.selectedServiceLocation = mobileZipServiceLocation; @@ -661,22 +684,27 @@ export default { }, async providerChangedFromServiceLocation(newProvider) { this.selectedProvider = newProvider?.provider; - if (newProvider?.provider) { - this.mainStore.updateServiceLocation(newProvider); + if (newProvider?.refreshDatePicker && newProvider.provider) { + this.mainStore.updateServiceLocationProvider(newProvider.provider); + showIssLoadingModal(true); - await onProviderChanged(); - if (newProvider.refreshDatePicker) { - await this.refreshDatePicker(); - } else { - showIssLoadingModal(false); - } + await this.mainStore.getBillToInfo(newProvider.provider?.providerNumber) + .then(() => { + this.refreshDatePicker(); + }) + .catch(() => { + console.warn('Error fetching bill to info...'); + showIssLoadingModal(false); + }); } }, async refreshDatePicker() { this.resetLocalFlags(); + this.isDatePickerRefreshing = true; await this.getDatePickerInitialData().then((data) => { this.selectableDatesData = data.initialShopTimeSlotsResponse; this.$refs.datePicker.initializeComponent(data); + this.isDatePickerRefreshing = false; showIssLoadingModal(false); }); }, @@ -700,7 +728,7 @@ export default { appointmentTypeToUse = AppointmentTypeStrings.DROP_OFF; } - this.mainStore.updateAppointmentType(appointmentTypeToUse); + await this.$refs.serviceLocation.forwardButtonAction(appointmentTypeToUse); this.mainStore.saveSchedule(this.selectedTimeSlotInfo.timeSlot); this.$router.navigate( this.navigationScenarios.CLICKED_FORWARD, diff --git a/src/layouts/schedule-page/service-location/service-location.spec.js b/src/layouts/schedule-page/service-location/service-location.spec.js index 7b1b4294..6a50fe05 100644 --- a/src/layouts/schedule-page/service-location/service-location.spec.js +++ b/src/layouts/schedule-page/service-location/service-location.spec.js @@ -203,7 +203,7 @@ describe('service-location.vue', () => { const newMobileServiceZipCode = '45433'; // Act - mobileServiceZipCodeQuestion.vm.$emit('update:modelValue', newMobileServiceZipCode); + mobileServiceZipCodeQuestion.vm.$emit('service-zip-code-updated', newMobileServiceZipCode); await wrapper.vm.$nextTick(); // Assert diff --git a/src/layouts/schedule-page/service-location/service-location.vue b/src/layouts/schedule-page/service-location/service-location.vue index 5b43a165..0af2fc9e 100644 --- a/src/layouts/schedule-page/service-location/service-location.vue +++ b/src/layouts/schedule-page/service-location/service-location.vue @@ -96,13 +96,14 @@ + cmsWidgetName="MobileZipWidget" + @serviceZipCodeUpdated="handleServiceZipCodeChanged" />
{ + this.setGlassFeeItems(combinedQuote); + }); + } }, setContainsMilitaryBase(val) { if (this.zipContainsMilitaryBase !== val) { this.zipContainsMilitaryBase = val; } }, + setGlassFeeItems(newGlassFeeItems) { + this.mainStore.updateGlassFees(newGlassFeeItems); + }, setMobileProviderNumber(providerNumber) { this.mobileProviderNumber = providerNumber; }, @@ -571,6 +622,22 @@ $page-side-padding: 1.5rem; div.alert { margin-top: 0.625rem; + &.widget-name-AlertLowAvailabilityInshopWidget { + @include media-breakpoint-up(xs) { + :deep(.alert-headline-text-container) { + padding-left: 0.5rem; + padding-right: 0.375rem; + letter-spacing: 0.45px; + } + } + @include media-breakpoint-up(xl) { + :deep(.alert-headline-text-container) { + padding: 0rem 0.625rem; + } + } + + } + button.looka-likea-link { background: none; border: none; diff --git a/src/layouts/schedule-page/service-location/service-zip-question/service-zip-question.spec.js b/src/layouts/schedule-page/service-location/service-zip-question/service-zip-question.spec.js index 82c44d56..182cbf42 100644 --- a/src/layouts/schedule-page/service-location/service-zip-question/service-zip-question.spec.js +++ b/src/layouts/schedule-page/service-location/service-zip-question/service-zip-question.spec.js @@ -7,7 +7,7 @@ describe('service-zip-question.vue', () => { const text = '12345'; const wrapper = shallowMount(serviceZipQuestion, { props: { - modelValue: text + parentZipCode: text }, attachTo: document.body }); @@ -32,9 +32,9 @@ describe('service-zip-question.vue', () => { // Act wrapper.vm.internalZipcode = '55555'; - wrapper.vm.updateModelValue(); + wrapper.vm.emitZipCodeUpdate(); // Assert - expect(wrapper.emitted('update:modelValue')).toEqual([['55555']]); + expect(wrapper.emitted('service-zip-code-updated')).toEqual([['55555']]); }); }); diff --git a/src/layouts/schedule-page/service-location/service-zip-question/service-zip-question.vue b/src/layouts/schedule-page/service-location/service-zip-question/service-zip-question.vue index 4f401049..5bea6fe0 100644 --- a/src/layouts/schedule-page/service-location/service-zip-question/service-zip-question.vue +++ b/src/layouts/schedule-page/service-location/service-zip-question/service-zip-question.vue @@ -10,7 +10,7 @@ isRequired :hasError="hasError" :validationRules="`zip-required|${cmsWidgetName}-zip-format`" - @clickEvent="updateModelValue" + @clickEvent="emitZipCodeUpdate" @fieldHasError="handleFieldError" /> @@ -32,7 +32,7 @@ export default { }, // TODO: Correct prop def. props: { - modelValue: String, + parentZipCode: String, cmsWidgetName: String, serviceZipFormatErrorMessage: { type: String, @@ -43,14 +43,14 @@ export default { default: false } }, - emits: ['update:modelValue', 'field-has-error'], + emits: ['service-zip-code-updated', 'field-has-error'], data() { return { - internalZipcode: this.modelValue + internalZipcode: this.parentZipCode }; }, watch: { - modelValue(newValue) { + parentZipCode(newValue) { if (newValue !== this.internalZipcode) { this.internalZipcode = newValue; } @@ -63,8 +63,8 @@ export default { handleFieldError(hasError) { this.$emit('field-has-error', hasError); }, - updateModelValue() { - this.$emit('update:modelValue', this.internalZipcode); + emitZipCodeUpdate() { + this.$emit('service-zip-code-updated', this.internalZipcode); } } }; diff --git a/src/layouts/schedule-page/service-location/shop-address/shop-address.vue b/src/layouts/schedule-page/service-location/shop-address/shop-address.vue index 98563ad1..f2fca788 100644 --- a/src/layouts/schedule-page/service-location/shop-address/shop-address.vue +++ b/src/layouts/schedule-page/service-location/shop-address/shop-address.vue @@ -75,10 +75,11 @@ + @fieldHasError="handleFieldError" + @serviceZipCodeUpdated="handleServiceZipCodeChanged" />
{ diff --git a/src/layouts/schedule-page/time-slot-modal-question/time-slot-modal-question.vue b/src/layouts/schedule-page/time-slot-modal-question/time-slot-modal-question.vue index e292f37a..fd0b880e 100644 --- a/src/layouts/schedule-page/time-slot-modal-question/time-slot-modal-question.vue +++ b/src/layouts/schedule-page/time-slot-modal-question/time-slot-modal-question.vue @@ -426,7 +426,7 @@ export default { `+${formatAmountInDollars(getPriceOfLineItem(this.premiumAppointmentFee))}`; return { - // Unique value is required for each and the premium appoinment shares an id + // Unique value is required for each and the premium appointment shares an id value: this.addPremiumFlagToInput(timeSlotData.id), buttonLabel: this.premiumAppointmentButtonText, buttonLabelSubCopy: formattedPrice, diff --git a/src/store/index.js b/src/store/index.js index b52d619e..32ee3b69 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -1865,7 +1865,6 @@ export const useMainStore = defineStore({ resetState() { Object.assign(this, getDefaultState()); }, - resetRegistrationState() { this.order.vehicle.registration.licensePlate = null; this.order.vehicle.registration.address = null; @@ -1904,14 +1903,12 @@ export const useMainStore = defineStore({ this.order.serviceLocation.state = null; this.order.serviceLocation.isVehicleProtected = null; }, - resetBailout() { this.updatePageData({ page: issPageValues.BAILOUT_PAGE, data: null }); }, - resetInsurance() { this.order.insuranceCoverage.coverageStatus = coverageStatuses.PENDING; this.order.insuranceCoverage.coverageType = coverageType.NONE;