CASH-816 clean up, defect fixes, looks to be running smoothly now
This commit is contained in:
Johnny shultz 2025-07-08 11:25:11 -04:00
parent 0032a3a1dd
commit ba1217d36c
3 changed files with 183 additions and 136 deletions

View file

@ -36,6 +36,7 @@
:id="modalId + '-modalbtn'"
ref="modalButtonMain"
loaderColor="white"
:isDisabled="footerButtonDisabled"
:buttonText="footerButtonText"
@click-event="validateAndEmit"
:class="isFooterButtonDisabled && 'form-test-invalid'" />
@ -59,6 +60,7 @@ export default {
footerButtonText: String,
suppressPageScroll: Boolean,
staticBackdrop: Boolean,
footerButtonDisabled: Boolean,
onModalOpenedCallback: {
type: Function,
},
@ -137,6 +139,9 @@ export default {
},
computed: {
isFooterButtonDisabled() {
if (this.footerButtonDisabled !== undefined) {
return this.footerButtonDisabled;
}
if (!this.meta.touched) {
return !this.meta.valid;
}

View file

@ -115,9 +115,9 @@
:selectedAppointmentType="selectedAppointmentType"
:modelValue="{
zipCode: zipCode,
providerNumber: selectedProvider?.providerNumber,
state: state,
}"
:preselectedProviderNumber="selectedProvider?.providerNumber"
:preSelectedProviderNumber="selectedProvider?.providerNumber"
:shopProviderData="shopProviderData"
:isServiceableInshop="isServiceableInshop"
@updated-serviceability="setServiceabilityDetails"
@ -844,25 +844,10 @@ export default {
let provider = this.shopProviderData?.shopProviders?.find(
(p) => p.providerNumber === providerObject?.providerNumber
);
if (!provider && this.shopProviderData?.shopProviders?.length > 0) {
provider = this.shopProviderData.shopProviders[0];
}
this.selectedProvider = provider;
if (provider && provider.address) {
this.zipCode = provider.address.zipCode;
this.state = provider.address.state;
}
if (
!this.selectedProvider ||
!this.shopProviderData?.shopProviders?.some(
(p) => p.providerNumber === this.selectedProvider?.providerNumber
)
) {
this.selectedProvider = this.shopProviderData?.shopProviders?.[0];
}
if (providerObject && providerObject.address) {
this.zipCode = providerObject.address.zipCode;
this.state = providerObject.address.state;
this.zipCode = providerObject.searchedZip;
this.state = providerObject.searchedState;
}
},
setZipcodeCtu(zipcodeCtu) {

View file

@ -24,22 +24,29 @@
:onModalOpenedCallback="onModalOpened"
:onModalClosedCallback="onModalClosed"
:footerButtonText="modalFooterText"
:footerButtonDisabled="
displayInvalidZipAlert || displayNoShopsAlert || !selectedProviderNumber
"
@footer-button-event="SetZipCodeAndShop">
<div class="zip-search-group">
<div class="zip-search-input">
<serviceZipQuestion
ref="serviceZipQuestion"
class="shop-question-zipcode"
customInputId="serviceZipCode"
v-model="internalModel.zipCode"
v-on="{ 'textboxQuestionEvent.inputIdAssigned': onInputIdAssigned }"
cmsWidgetName="ServiceZipQuestionWidget"
@search-icon-click="onSearchZip"
@keydown.enter="onSearchZip"
includeSearchIcon
:isRequired="false" />
</div>
</div>
<serviceZipQuestion
ref="serviceZipQuestion"
class="shop-question-zipcode"
customInputId="serviceZipCode"
v-model="internalModel.zipCode"
v-on="{ 'textboxQuestionEvent.inputIdAssigned': onInputIdAssigned }"
cmsWidgetName="ServiceZipQuestionWidget"
@search-icon-click="onSearchZip"
@keydown.enter="onSearchZip"
includeSearchIcon
:isRequired="false"
:validationRules="''" />
<alert
ref="alertInvalidZip"
v-if="displayInvalidZipAlert"
class="mb-4"
cmsWidgetName="AlertInvalidZipWidget"
alertClass="alert-danger"
v-bind:isDismissible="false" />
<alert
ref="alertMilitaryBaseZip"
class="my-5"
@ -81,29 +88,31 @@
cmsWidgetName="AlertMobileFeeFreeWidget"
v-if="showMobileFreeAlert"
alertClass="alert-success" />
<buttonQuestion
ref="buttonQuestion"
buttonTypeString="shopListButton"
:buttonTypeObject="shopListButton"
class="radioQuestion"
:questionText="questionText"
:answers="answers"
groupName="chooseShop"
textPosition="text-start"
v-model="selectedProviderNumber"
isRequired
validationRules="option-required" />
<div class="show-more-shops-link">
<textLink
v-if="displaySeeMoreLocationsLink"
ref="showMoreShopsLink"
id="showMoreShopsId2"
cmsWidgetName="ShowMoreShopsLinkWidget"
linkType="text"
:text="showMoreShopsLinkText"
href="#!"
@click-event="getNextShopsFromList(3)"
:aria-label="showMoreShopsLinkText" />
<div v-if="!displayInvalidZipAlert">
<buttonQuestion
ref="buttonQuestion"
buttonTypeString="shopListButton"
:buttonTypeObject="shopListButton"
class="radioQuestion"
:questionText="questionText"
:answers="answers"
groupName="chooseShop"
textPosition="text-start"
v-model="selectedProviderNumber"
isRequired
:validationRules="this.selectedProviderNumber ? '' : 'option-required'" />
<div class="show-more-shops-link">
<textLink
v-if="displaySeeMoreLocationsLink"
ref="showMoreShopsLink"
id="showMoreShopsId2"
cmsWidgetName="ShowMoreShopsLinkWidget"
linkType="text"
:text="showMoreShopsLinkText"
href="#!"
@click-event="getNextShopsFromList(3)"
:aria-label="showMoreShopsLinkText" />
</div>
</div>
</modal>
</template>
@ -124,6 +133,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,6 +151,7 @@ import {
} from "@/layouts/service-location/helpers/service-location-helper/service-location-helper";
defineRule("option-required", required(errorMessages.OPTION_REQUIRED));
defineRule("zip-format", regex(/(^\d{5}$)|(^\d{5}-\d{4}$)/, errorMessages.SERVICE_ZIP_FORMAT));
function getShopsByZip(zipCode, shops, numberToGet = 3) {
return shops
@ -167,6 +178,9 @@ export default {
lastSearchedZip: "",
lastSuccessfulZip: "",
hasLoadedShopData: false,
displayInvalidZipAlert: false,
areShopsAvialable: true,
displayNoShopsAlert: false,
};
},
props: {
@ -193,7 +207,7 @@ export default {
default: "",
},
preSelectedProviderNumber: {
type: [String, Number],
type: String,
default: null,
},
},
@ -243,9 +257,6 @@ export default {
showMoreShopsLinkText() {
return this.getCmsContent("ShowMoreShopsLinkWidget", "Text");
},
displayNoShopsAlert() {
return this.hasLoadedShopData && this.answers.length === 0;
},
},
methods: {
openModal(event) {
@ -369,20 +380,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 +411,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 +421,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 +480,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 = getShopsByZip(zipCode, this.shopProviders, this.shopIndex);
const toTitleCase = (str) => {
return str.replace(/\w\S*/g, function (txt) {
@ -498,12 +532,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 +559,24 @@ export default {
resetAlerts() {
this.displayInvalidZipAlert = false;
this.displayNoServiceAlert = false;
this.displayNoShopsAlert = false;
},
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 +599,6 @@ export default {
}
}
selectedProvider.address.state = zipCodeData.state;
selectedProvider.address.zipCodeCtu = zipCodeData.zipCodeCtu;
// retrieve mobile fee part
@ -585,7 +635,13 @@ 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();
}
@ -597,14 +653,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 +663,6 @@ export default {
buttonQuestion,
alert,
},
mounted() {
this.handleUpdate();
},
};
</script>
@ -658,7 +705,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 +720,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 +775,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%;
}
</style>