@@ -24,86 +16,60 @@
:onModalOpenedCallback="onModalOpened"
:onModalClosedCallback="onModalClosed"
:footerButtonText="modalFooterText"
- @footer-button-event="SetZipCodeAndShop">
-
+ :footerButtonDisabled="
+ displayInvalidZipAlert || displayNoShopsAlert || !selectedProviderNumber
+ "
+ @footer-button-event="setZipCodeAndShop">
+
-
+ ref="alertInvalidZip"
+ v-if="displayInvalidZipAlert"
+ class="mb-4"
+ cmsWidgetName="AlertInvalidZipWidget"
+ alertClass="alert-danger"
+ v-bind:isDismissible="false" />
-
+
@@ -124,6 +90,7 @@ import { required } from "@/helpers/validation-rules";
import { errorMessages } from "@/constants/error-messages";
import baseMixin from "@/mixins/base-mixin.js";
import { nextTick } from "vue";
+import { regex } from "@/helpers/validation-rules";
import { getAvailabilityRating } from "@/layouts/service-location/helpers/service-location-helper/service-location-helper";
@@ -141,13 +108,7 @@ import {
} from "@/layouts/service-location/helpers/service-location-helper/service-location-helper";
defineRule("option-required", required(errorMessages.OPTION_REQUIRED));
-
-function getShopsByZip(zipCode, shops, numberToGet = 3) {
- return shops
- .filter((shop) => shop.address && shop.address.zipCode && shop.address.zipCode !== "")
- .sort((a, b) => a.distanceInMiles - b.distanceInMiles)
- .slice(0, numberToGet);
-}
+defineRule("zip-format", regex(/(^\d{5}$)|(^\d{5}-\d{4}$)/, errorMessages.SERVICE_ZIP_FORMAT));
export default {
name: "shop-question-popup",
@@ -167,6 +128,9 @@ export default {
lastSearchedZip: "",
lastSuccessfulZip: "",
hasLoadedShopData: false,
+ displayInvalidZipAlert: false,
+ areShopsAvialable: true,
+ displayNoShopsAlert: false,
};
},
props: {
@@ -193,7 +157,7 @@ export default {
default: "",
},
preSelectedProviderNumber: {
- type: [String, Number],
+ type: String,
default: null,
},
},
@@ -243,9 +207,6 @@ export default {
showMoreShopsLinkText() {
return this.getCmsContent("ShowMoreShopsLinkWidget", "Text");
},
- displayNoShopsAlert() {
- return this.hasLoadedShopData && this.answers.length === 0;
- },
},
methods: {
openModal(event) {
@@ -369,20 +330,22 @@ export default {
this.shopIndex = 0;
},
onModalOpened() {
- this.shopIndex = 3;
+ const preSelectedIndex = this.shopProviders.findIndex(
+ (provider) => provider.providerNumber == this.preSelectedProviderNumber
+ );
+
+ if (preSelectedIndex > -1) {
+ this.shopIndex = Math.ceil((preSelectedIndex + 1) / 3) * 3;
+ } else {
+ this.shopIndex = 3;
+ }
this.internalModel = this.copyModel(this.modelValue);
this.internalModel.zipCode = this.zipCode;
this.localShopProviderData = this.shopProviderData;
this.answers = [];
- this.updateShopsForZip(this.internalModel.zipCode);
+ this.onSearchZip();
this.focusOnZipInput();
this.selectedProviderNumber = this.preSelectedProviderNumber;
- if (this.internalModel.zipCode && this.shopProviders.length > 0) {
- this.updateShopsForZip(this.internalModel.zipCode);
- } else {
- this.internalModel.zipCode = this.zipCode;
- this.updateShopsForZip(this.internalModel.zipCode);
- }
if (
(!this.selectedProviderNumber ||
!this.shopProviders.some(
@@ -398,14 +361,7 @@ export default {
},
onModalClosed() {
this.internalModel = this.copyModel(this.modelValue);
- this.selectedProviderNumber = this.preSelectedProviderNumber;
this.resetsOnZipInput();
- let selectedProvider = this.shopProviders.find(
- (provider) => provider.providerNumber == this.selectedProviderNumber
- );
- if (!selectedProvider && this.shopProviders.length > 0) {
- selectedProvider = this.shopProviders[0];
- }
},
resetsOnZipInput() {
this.resetAlerts();
@@ -415,33 +371,49 @@ export default {
event.preventDefault();
event.stopPropagation();
}
+ this.resetsOnZipInput();
+ this.displayNoShopsAlert = false;
const zip = this.internalModel.zipCode;
- if (this.isVehicleHeavyTruck) {
- // check the location endpoint to verify this zip can service a heavy truck
- const closestShops = await getClosestApplicableShops(
- this.internalModel.zipCode,
- this.carId,
- "service-location"
- );
-
- if (!closestShops || closestShops.providers?.length === 0) {
- this.displayNoServiceAlert = true;
- this.focusOnZipInput();
- this.resetModalButtonStyle();
- return null;
- }
+ const zipCodeData = await this.getZipCodeData(
+ this.internalModel.zipCode,
+ "service-location"
+ );
+ if (!zipCodeData.isValid) {
+ this.displayInvalidZipAlert = true;
+ //this.selectedProviderNumber = null;
+ this.focusOnZipInput();
+ this.resetModalButtonStyle();
} else {
- getShopProviderData(zip).then(async (result) => {
- this.localShopProviderData = result.data;
- this.updateShopsForZip(this.internalModel.zipCode);
- if (this.selectedAppointmentType === "Mobile") {
- this.selectedProvider = new Provider(
- this.shopProviderData.mobileProviderNumber
- );
- } else {
- this.selectedProvider = new Provider();
+ if (this.isVehicleHeavyTruck) {
+ // check the location endpoint to verify this zip can service a heavy truck
+ const closestShops = await getClosestApplicableShops(
+ this.internalModel.zipCode,
+ this.carId,
+ "service-location"
+ );
+
+ if (!closestShops || closestShops.providers?.length === 0) {
+ this.displayNoServiceAlert = true;
+ this.focusOnZipInput();
+ this.resetModalButtonStyle();
+ return null;
}
- });
+ } else {
+ getShopProviderData(zip).then(async (result) => {
+ if (this.displayNoShopsAlert === true) {
+ this.displayNoShopsAlert = false;
+ }
+ this.localShopProviderData = result.data;
+ this.updateShopsForZip(this.internalModel.zipCode);
+ if (this.selectedAppointmentType === "Mobile") {
+ this.selectedProvider = new Provider(
+ this.shopProviderData.mobileProviderNumber
+ );
+ } else {
+ this.selectedProvider = new Provider();
+ }
+ });
+ }
}
},
async updateShopsForZip(zipCode) {
@@ -458,11 +430,23 @@ export default {
}
this.isLoadingShops = true;
if (!zipCode || !this.shopProviders.length) {
- this.handleUpdate();
+ this.answers = [];
+ this.displayNoShopsAlert = true;
+ this.displaySeeMoreLocationsLink = false;
+ this.isLoadingShops = false;
return;
}
- const sortedShops = getShopsByZip(zipCode, this.shopProviders, 3);
+ const selectedIndex = this.shopProviders.findIndex(
+ (provider) => provider.providerNumber == this.selectedProviderNumber
+ );
+
+ // If not found, only show the first 3 shops
+ if (selectedIndex === -1) {
+ this.shopIndex = 3;
+ }
+
+ const sortedShops = this.getShopsByZip(zipCode, this.shopProviders, this.shopIndex);
const toTitleCase = (str) => {
return str.replace(/\w\S*/g, function (txt) {
@@ -498,12 +482,22 @@ export default {
};
});
this.answers = mappedData;
+ if (
+ this.selectedProviderNumber &&
+ !this.answers.some((a) => String(a.value) === String(this.selectedProviderNumber))
+ ) {
+ this.selectedProviderNumber = null;
+ }
this.shopIndex = mappedData.length;
this.displaySeeMoreLocationsLink = this.shopIndex < this.shopProviders.length;
if (mappedData.length > 0) {
this.lastSuccessfulZip = zipCode;
}
this.isLoadingShops = false;
+ this.displayNoShopsAlert = this.answers.length === 0;
+ },
+ resetModalButtonStyle() {
+ this.modal.resetButtonStyle();
},
onInputIdAssigned(inputId) {
this.serviceZipCodeTextInputId = inputId;
@@ -515,17 +509,24 @@ export default {
resetAlerts() {
this.displayInvalidZipAlert = false;
this.displayNoServiceAlert = false;
+ this.displayNoShopsAlert = false;
},
- async SetZipCodeAndShop() {
+ async setZipCodeAndShop() {
+ if (
+ this.displayInvalidZipAlert ||
+ this.displayNoShopsAlert ||
+ !this.selectedProviderNumber
+ ) {
+ return;
+ }
+ const zipToEmit = this.lastSearchedZip;
+
const selectedProvider = this.shopProviders.find(
(provider) => provider.providerNumber == this.selectedProviderNumber
);
this.resetAlerts();
- const zipCodeData = await this.getZipCodeData(
- selectedProvider.address.zipCode,
- "service-location"
- );
+ const zipCodeData = await this.getZipCodeData(zipToEmit, "service-location");
if (!zipCodeData.isValid) {
this.displayInvalidZipAlert = true;
@@ -548,7 +549,6 @@ export default {
}
}
- selectedProvider.address.state = zipCodeData.state;
selectedProvider.address.zipCodeCtu = zipCodeData.zipCodeCtu;
// retrieve mobile fee part
@@ -585,11 +585,25 @@ export default {
this.$emit("update:modelValue", this.selectedValue);
this.$emit("update-shop-provider-data", this.localShopProviderData);
- this.$emit("shop-selected", selectedProvider);
+ this.internalModel.state = zipCodeData.state;
+ this.$emit("shop-selected", {
+ providerNumber: selectedProvider?.providerNumber,
+ address: selectedProvider?.address,
+ searchedZip: zipToEmit,
+ searchedState: this.internalModel.state,
+ });
this.closeModal();
}
},
+ getShopsByZip(zipCode, shops, numberToGet = 3) {
+ return shops
+ .filter(
+ (shop) => shop.address && shop.address.zipCode && shop.address.zipCode !== ""
+ )
+ .sort((a, b) => a.distanceInMiles - b.distanceInMiles)
+ .slice(0, numberToGet);
+ },
},
watch: {
shopProviderData: {
@@ -597,14 +611,8 @@ export default {
handler(newVal) {
this.localShopProviderData = newVal;
this.hasLoadedShopData = true;
- if (this.internalModel && this.internalModel.zipCode) {
- this.updateShopsForZip(this.internalModel.zipCode);
- }
},
},
- preSelectedProviderNumber(newVal) {
- this.selectedProviderNumber = newVal;
- },
},
components: {
modal,
@@ -613,9 +621,6 @@ export default {
buttonQuestion,
alert,
},
- mounted() {
- this.handleUpdate();
- },
};
@@ -658,7 +663,7 @@ export default {
.modal.modal-component .modal-dialog .modal-content .modal-body .textbox-question {
padding: 0;
}
-.zip-search-group {
+.shop-question-zipcode {
display: flex;
flex-direction: row;
align-items: stretch;
@@ -673,44 +678,40 @@ export default {
height: auto;
padding-left: 0;
}
- .zip-search-input {
+ .input-wrapper.has-search-icon {
+ display: flex;
align-items: stretch;
flex: 1 1 0;
height: 100%;
margin-bottom: 0;
- input {
+ input,
+ .form-control {
width: 100%;
- height: 100%;
+ height: 48px;
+ min-height: 48px;
+ max-height: 48px;
border: none;
- padding-top: 0;
- padding-bottom: 0;
+ padding: 0 1.5rem 0 1.5rem;
outline: none;
font-size: 1rem;
background: transparent;
box-sizing: border-box;
- border-radius: 48px 0 0 48px;
- padding-left: 0;
+ border-radius: 2.5rem 0 0 2.5rem;
}
}
- .btn {
+ .search-icon-button {
display: flex;
align-items: center;
- background-color: #e5f1fa;
justify-content: center;
- width: 48px;
+ background-color: #e5f1fa !important;
+ width: 2.5rem;
height: 48px;
-
border: none;
-
- border-radius: 0 48px 48px 0;
+ max-height: 48px;
+ border-radius: 0 2.5rem 2.5rem 0;
cursor: pointer;
}
}
-@media (max-width: 767.98px) {
- .zip-search-group {
- padding-left: 0;
- }
-}
.show-more-shops-link a {
text-align: center;
display: block;
@@ -732,4 +733,18 @@ export default {
border-radius: 2.5rem;
padding-left: 1.5rem;
}
+
+.shop-question-zipcode .form-test-error {
+ display: block;
+ width: 100%;
+ margin-top: 0.25rem;
+ margin-left: 0;
+ color: #d32f2f; // or your error color
+ position: static !important;
+ font-size: 0.875rem;
+ clear: both;
+ float: none;
+ display: block;
+ width: 100%;
+}
diff --git a/src/router/constants/routes.js b/src/router/constants/routes.js
index 7e9a3a793..5632fe357 100644
--- a/src/router/constants/routes.js
+++ b/src/router/constants/routes.js
@@ -56,6 +56,7 @@ export const routeData = {
path: "/insurance-company",
},
SERVICE_LOCATION: {
+ // keep even though this page has been removed from the regular flow bc Heritage still points to it
name: "service-location",
path: "/service-location",
},
diff --git a/src/router/constants/routing-table.js b/src/router/constants/routing-table.js
index 4a07bcfaf..02a0d9384 100644
--- a/src/router/constants/routing-table.js
+++ b/src/router/constants/routing-table.js
@@ -421,7 +421,7 @@ const routingTable = function () {
},
{
scenario: navigationScenarios.CLICKED_FORWARD_WITH_CASH,
- destinationPageData: routeData.SERVICE_LOCATION,
+ destinationPageData: routeData.SCHEDULE,
},
{
scenario: navigationScenarios.CLICKED_FORWARD_WITH_INSURANCE,
@@ -449,8 +449,26 @@ const routingTable = function () {
},
],
},
+ // TODO: REMOVE THIS COMMENT AND BELOW, ONCE CASH-803 (SERVICE-LOCATION AND SCHEDULE PAGE COMBINATION) HAS BEEN VETTED
+ // {
+ // pageName: routeData.SERVICE_LOCATION.name,
+ // maps: [
+ // {
+ // scenario: navigationScenarios.CLICKED_BACK,
+ // destinationPageData: routeData.QUOTE,
+ // },
+ // {
+ // scenario: navigationScenarios.CLICKED_FORWARD,
+ // destinationPageData: routeData.SCHEDULE,
+ // },
+ // {
+ // scenario: navigationScenarios.CLICKED_FORWARD_WITH_MOBILE_SERVICE,
+ // destinationPageData: routeData.MOBILE_DETAILS,
+ // },
+ // ],
+ // },
{
- pageName: routeData.SERVICE_LOCATION.name,
+ pageName: routeData.SCHEDULE.name,
maps: [
{
scenario: navigationScenarios.CLICKED_BACK,
@@ -458,37 +476,24 @@ const routingTable = function () {
},
{
scenario: navigationScenarios.CLICKED_FORWARD,
- destinationPageData: routeData.SCHEDULE,
+ destinationPageData: routeData.CUSTOMER_DETAILS,
},
+ // {
+ // scenario: navigationScenarios.CLICKED_CHANGE_LOCATION,
+ // destinationPageData: routeData.SERVICE_LOCATION,
+ // },
{
scenario: navigationScenarios.CLICKED_FORWARD_WITH_MOBILE_SERVICE,
destinationPageData: routeData.MOBILE_DETAILS,
},
],
},
- {
- pageName: routeData.SCHEDULE.name,
- maps: [
- {
- scenario: navigationScenarios.CLICKED_BACK,
- destinationPageData: routeData.SERVICE_LOCATION,
- },
- {
- scenario: navigationScenarios.CLICKED_FORWARD,
- destinationPageData: routeData.CUSTOMER_DETAILS,
- },
- {
- scenario: navigationScenarios.CLICKED_CHANGE_LOCATION,
- destinationPageData: routeData.SERVICE_LOCATION,
- },
- ],
- },
{
pageName: routeData.MOBILE_DETAILS.name,
maps: [
{
scenario: navigationScenarios.CLICKED_BACK,
- destinationPageData: routeData.SERVICE_LOCATION,
+ destinationPageData: routeData.SCHEDULE,
},
{
scenario: navigationScenarios.CLICKED_FORWARD,
diff --git a/src/router/methods/route-logic/service-location.js b/src/router/methods/route-logic/service-location.js
new file mode 100644
index 000000000..ecaebe149
--- /dev/null
+++ b/src/router/methods/route-logic/service-location.js
@@ -0,0 +1,8 @@
+import { routeData } from "@/router/constants/routes";
+
+export async function serviceLocationBeforeEnter(to, from) {
+ return {
+ name: routeData.SCHEDULE.name,
+ replace: true,
+ };
+}
diff --git a/src/router/methods/routes.js b/src/router/methods/routes.js
index 01f4f8a58..97af04068 100644
--- a/src/router/methods/routes.js
+++ b/src/router/methods/routes.js
@@ -12,6 +12,7 @@ import { errorBeforeEnter } from "@/router/methods/route-logic/error";
import { restartBeforeEnter } from "@/router/methods/route-logic/restart";
import { paymentMethodBeforeEnter } from "@/router/methods/route-logic/payment-method";
import { paymentBeforeEnter } from "@/router/methods/route-logic/payment";
+import { serviceLocationBeforeEnter } from "@/router/methods/route-logic/service-location";
export const routes = [
// Non-virtual pages.
@@ -29,7 +30,7 @@ export const routes = [
createRoute(routeData.CAPABILITY_QUESTIONS),
createRoute(routeData.QUOTE, quoteBeforeEnter),
createRoute(routeData.INSURANCE_COMPANY, insuranceCompanyBeforeEnter),
- createRoute(routeData.SERVICE_LOCATION),
+ createRoute(routeData.SERVICE_LOCATION, serviceLocationBeforeEnter),
createRoute(routeData.SCHEDULE),
createRoute(routeData.CUSTOMER_DETAILS),
createRoute(routeData.PAYMENT_METHOD, paymentMethodBeforeEnter),
diff --git a/src/styles/ux-variables.scss b/src/styles/ux-variables.scss
index 22e17e3b7..3171ea8d9 100644
--- a/src/styles/ux-variables.scss
+++ b/src/styles/ux-variables.scss
@@ -123,8 +123,8 @@ $body-color: $gray-600;
//Fonts
$font-family-sans-serif: UrbanistRegular, Arial, Helvetica, sans-serif;
-$font-family-monospace:
- UrbanistRegular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
+$font-family-monospace: UrbanistRegular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New",
+ monospace;
// stylelint-enable value-keyword-case
$font-family-base: $font-family-sans-serif;
$font-family-code: $font-family-monospace;