Merge pull request #2865 from Safelite/rlsmerge/2025.09.25-to-dev
Rlsmerge/2025.09.25 to dev
This commit is contained in:
commit
c469be54f1
17 changed files with 572 additions and 471 deletions
|
|
@ -110,18 +110,6 @@ stages:
|
||||||
clearFolder: true
|
clearFolder: true
|
||||||
deployFolder: ""
|
deployFolder: ""
|
||||||
region: us-east-2
|
region: us-east-2
|
||||||
appDeployVariables:
|
|
||||||
__VUE_APP_CONSUMER_CF_DISTRO__: $(__VUE_APP_CONSUMER_CF_DISTRO__)
|
|
||||||
__VUE_APP_GOOGLE_PLACES_API_KEY__: $(__VUE_APP_GOOGLE_PLACES_API_KEY__)
|
|
||||||
__VUE_APP_HERITAGE_FUNNEL__: $(__VUE_APP_HERITAGE_FUNNEL__)
|
|
||||||
__VUE_APP_SAFELITE_HOP__: $(__VUE_APP_SAFELITE_HOP__)
|
|
||||||
__VUE_APP_CURRENT_ENVIRONMENT__: $(__VUE_APP_CURRENT_ENVIRONMENT__)
|
|
||||||
__VUE_APP_MY_ACCOUNT__: $(__VUE_APP_MY_ACCOUNT__)
|
|
||||||
__VUE_APP_SESSION_TIMEOUT_MINUTES__: $(__VUE_APP_SESSION_TIMEOUT_MINUTES__)
|
|
||||||
indexDeployVariables:
|
|
||||||
__VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY__: $(__VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY__)
|
|
||||||
__VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC__: $(__VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC__)
|
|
||||||
__VUE_APP_SOLARWINDS_MONITORING_SCRIPT__: $(__VUE_APP_SOLARWINDS_MONITORING_SCRIPT__)
|
|
||||||
- template: templates/digital/invalidate-cloudfront-cache.yml@AzureDevOps
|
- template: templates/digital/invalidate-cloudfront-cache.yml@AzureDevOps
|
||||||
parameters:
|
parameters:
|
||||||
awsCliContainer: awscli
|
awsCliContainer: awscli
|
||||||
|
|
@ -171,18 +159,6 @@ stages:
|
||||||
clearFolder: true
|
clearFolder: true
|
||||||
deployFolder: ""
|
deployFolder: ""
|
||||||
region: us-east-2
|
region: us-east-2
|
||||||
appDeployVariables:
|
|
||||||
__VUE_APP_CONSUMER_CF_DISTRO__: $(__VUE_APP_CONSUMER_CF_DISTRO__)
|
|
||||||
__VUE_APP_GOOGLE_PLACES_API_KEY__: $(__VUE_APP_GOOGLE_PLACES_API_KEY__)
|
|
||||||
__VUE_APP_HERITAGE_FUNNEL__: $(__VUE_APP_HERITAGE_FUNNEL__)
|
|
||||||
__VUE_APP_SAFELITE_HOP__: $(__VUE_APP_SAFELITE_HOP__)
|
|
||||||
__VUE_APP_CURRENT_ENVIRONMENT__: $(__VUE_APP_CURRENT_ENVIRONMENT__)
|
|
||||||
__VUE_APP_MY_ACCOUNT__: $(__VUE_APP_MY_ACCOUNT__)
|
|
||||||
__VUE_APP_SESSION_TIMEOUT_MINUTES__: $(__VUE_APP_SESSION_TIMEOUT_MINUTES__)
|
|
||||||
indexDeployVariables:
|
|
||||||
__VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY__: $(__VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY__)
|
|
||||||
__VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC__: $(__VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC__)
|
|
||||||
__VUE_APP_SOLARWINDS_MONITORING_SCRIPT__: $(__VUE_APP_SOLARWINDS_MONITORING_SCRIPT__)
|
|
||||||
- template: templates/digital/invalidate-cloudfront-cache.yml@AzureDevOps
|
- template: templates/digital/invalidate-cloudfront-cache.yml@AzureDevOps
|
||||||
parameters:
|
parameters:
|
||||||
awsCliContainer: awscli
|
awsCliContainer: awscli
|
||||||
|
|
@ -232,18 +208,6 @@ stages:
|
||||||
clearFolder: true
|
clearFolder: true
|
||||||
deployFolder: ""
|
deployFolder: ""
|
||||||
region: us-east-2
|
region: us-east-2
|
||||||
appDeployVariables:
|
|
||||||
__VUE_APP_CONSUMER_CF_DISTRO__: $(__VUE_APP_CONSUMER_CF_DISTRO__)
|
|
||||||
__VUE_APP_GOOGLE_PLACES_API_KEY__: $(__VUE_APP_GOOGLE_PLACES_API_KEY__)
|
|
||||||
__VUE_APP_HERITAGE_FUNNEL__: $(__VUE_APP_HERITAGE_FUNNEL__)
|
|
||||||
__VUE_APP_SAFELITE_HOP__: $(__VUE_APP_SAFELITE_HOP__)
|
|
||||||
__VUE_APP_CURRENT_ENVIRONMENT__: $(__VUE_APP_CURRENT_ENVIRONMENT__)
|
|
||||||
__VUE_APP_MY_ACCOUNT__: $(__VUE_APP_MY_ACCOUNT__)
|
|
||||||
__VUE_APP_SESSION_TIMEOUT_MINUTES__: $(__VUE_APP_SESSION_TIMEOUT_MINUTES__)
|
|
||||||
indexDeployVariables:
|
|
||||||
__VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY__: $(__VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY__)
|
|
||||||
__VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC__: $(__VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC__)
|
|
||||||
__VUE_APP_SOLARWINDS_MONITORING_SCRIPT__: $(__VUE_APP_SOLARWINDS_MONITORING_SCRIPT__)
|
|
||||||
- template: templates/digital/invalidate-cloudfront-cache.yml@AzureDevOps
|
- template: templates/digital/invalidate-cloudfront-cache.yml@AzureDevOps
|
||||||
parameters:
|
parameters:
|
||||||
awsCliContainer: awscli
|
awsCliContainer: awscli
|
||||||
|
|
@ -293,18 +257,6 @@ stages:
|
||||||
clearFolder: true
|
clearFolder: true
|
||||||
deployFolder: ""
|
deployFolder: ""
|
||||||
region: us-east-2
|
region: us-east-2
|
||||||
appDeployVariables:
|
|
||||||
__VUE_APP_CONSUMER_CF_DISTRO__: $(__VUE_APP_CONSUMER_CF_DISTRO__)
|
|
||||||
__VUE_APP_GOOGLE_PLACES_API_KEY__: $(__VUE_APP_GOOGLE_PLACES_API_KEY__)
|
|
||||||
__VUE_APP_HERITAGE_FUNNEL__: $(__VUE_APP_HERITAGE_FUNNEL__)
|
|
||||||
__VUE_APP_SAFELITE_HOP__: $(__VUE_APP_SAFELITE_HOP__)
|
|
||||||
__VUE_APP_CURRENT_ENVIRONMENT__: $(__VUE_APP_CURRENT_ENVIRONMENT__)
|
|
||||||
__VUE_APP_MY_ACCOUNT__: $(__VUE_APP_MY_ACCOUNT__)
|
|
||||||
__VUE_APP_SESSION_TIMEOUT_MINUTES__: $(__VUE_APP_SESSION_TIMEOUT_MINUTES__)
|
|
||||||
indexDeployVariables:
|
|
||||||
__VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY__: $(__VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY__)
|
|
||||||
__VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC__: $(__VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC__)
|
|
||||||
__VUE_APP_SOLARWINDS_MONITORING_SCRIPT__: $(__VUE_APP_SOLARWINDS_MONITORING_SCRIPT__)
|
|
||||||
- template: templates/digital/invalidate-cloudfront-cache.yml@AzureDevOps
|
- template: templates/digital/invalidate-cloudfront-cache.yml@AzureDevOps
|
||||||
parameters:
|
parameters:
|
||||||
awsCliContainer: awscli
|
awsCliContainer: awscli
|
||||||
|
|
|
||||||
23
environment-variables.js
Normal file
23
environment-variables.js
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
// DEFINE every environment variable we will use here,
|
||||||
|
// with a default value to use in local development and unit tests.
|
||||||
|
//
|
||||||
|
// Default values will not be used when we deploy to prod or non-prod.
|
||||||
|
const environmentVariables = {
|
||||||
|
['__VUE_APP_CONSUMER_CF_DISTRO__']: "https://digitalapi.dev.safelite.io",
|
||||||
|
['__VUE_APP_CURRENT_ENVIRONMENT__']: "Localhost",
|
||||||
|
['__VUE_APP_GOOGLE_PLACES_API_KEY__']: "AIzaSyDptGCkOPgN2uWJOy4ou4M33phRD4MAoJo",
|
||||||
|
['__VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC__']: "https://www.googletagmanager.com/ns.html?id=GTM-M6XCRH>m_auth=amlAYNhxUxuskQo7jmjadg>m_preview=env-38>m_cookies_win=x",
|
||||||
|
['__VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY__']: "(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src='https://www.googletagmanager.com/gtm.js?id='+i+dl+ '>m_auth=amlAYNhxUxuskQo7jmjadg>m_preview=env-38>m_cookies_win=x';f.parentNode.insertBefore(j,f);})(window,document,'script','dataLayer','GTM-M6XCRH');",
|
||||||
|
['__VUE_APP_HERITAGE_FUNNEL__']: "https://fixmyglassdev.safelite.com/default.aspx",
|
||||||
|
['__VUE_APP_MY_ACCOUNT__']: "https://myaccountdev.safelite.com/",
|
||||||
|
['__VUE_APP_SAFELITE_HOP__']: "https://sv2-safelitehop-dev.safelite.com/fmgCheckoutShared.aspx",
|
||||||
|
['__VUE_APP_SESSION_TIMEOUT_MINUTES__']: 30,
|
||||||
|
['__VUE_APP_SOLARWINDS_MONITORING_SCRIPT__']: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
const keysOnly = Object.keys(environmentVariables);
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
environmentVariablesWithoutDefaults: keysOnly,
|
||||||
|
environmentVariablesWithDefaults: environmentVariables,
|
||||||
|
};
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
const { environmentVariablesWithDefaults } = require('./environment-variables.js');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
verbose: true,
|
verbose: true,
|
||||||
coverageReporters: ["html", "text", "jest-junit", "cobertura"], reporters: ['default', 'jest-junit'],
|
coverageReporters: ["html", "text", "jest-junit", "cobertura"], reporters: ['default', 'jest-junit'],
|
||||||
|
|
@ -49,3 +51,5 @@ module.exports = {
|
||||||
// Uncomment this to avoid the massive amount of warnings we are getting for onSubmit and onInvalidSubmit
|
// Uncomment this to avoid the massive amount of warnings we are getting for onSubmit and onInvalidSubmit
|
||||||
//silent: true,
|
//silent: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
process.env = Object.assign(process.env, environmentVariablesWithDefaults);
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
<script>
|
<script>
|
||||||
window.dataLayer = [{}];
|
window.dataLayer = [{}];
|
||||||
</script>
|
</script>
|
||||||
<script><%= process.env.VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY %></script>
|
<script><%= process.env.__VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY__ %></script>
|
||||||
<script src="https://applepay.cdn-apple.com/jsapi/v1.1.0/apple-pay-sdk.js"></script>
|
<script src="https://applepay.cdn-apple.com/jsapi/v1.1.0/apple-pay-sdk.js"></script>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
<!-- Google Tag Manager -->
|
<!-- Google Tag Manager -->
|
||||||
<noscript>
|
<noscript>
|
||||||
<iframe src="<%= process.env.VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC %>"
|
<iframe src="<%= process.env.__VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC__ %>"
|
||||||
height="0" width="0" style="display:none;visibility:hidden"></iframe>
|
height="0" width="0" style="display:none;visibility:hidden"></iframe>
|
||||||
</noscript>
|
</noscript>
|
||||||
|
|
||||||
|
|
@ -27,6 +27,6 @@
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
<!-- built files will be auto injected -->
|
<!-- built files will be auto injected -->
|
||||||
|
|
||||||
<%= process.env.VUE_APP_SOLARWINDS_MONITORING_SCRIPT %>
|
<%= process.env.__VUE_APP_SOLARWINDS_MONITORING_SCRIPT__ %>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
const applicationConfig = {
|
const applicationConfig = {
|
||||||
CONSUMER_CF_DISTRO: process.env.VUE_APP_CONSUMER_CF_DISTRO,
|
CONSUMER_CF_DISTRO: process.env.__VUE_APP_CONSUMER_CF_DISTRO__,
|
||||||
GOOGLE_PLACES_API_KEY: process.env.VUE_APP_GOOGLE_PLACES_API_KEY,
|
GOOGLE_PLACES_API_KEY: process.env.__VUE_APP_GOOGLE_PLACES_API_KEY__,
|
||||||
ANALYTICS_SESSION_TIMEOUT_MINUTES: 30,
|
ANALYTICS_SESSION_TIMEOUT_MINUTES: 30,
|
||||||
SAVED_SESSION_TIMEOUT_DAYS: 45,
|
SAVED_SESSION_TIMEOUT_DAYS: 45,
|
||||||
COOKIE_PATH: "/",
|
COOKIE_PATH: "/",
|
||||||
CURRENT_ENVIRONMENT: process.env.VUE_APP_CURRENT_ENVIRONMENT, // "Localhost", "Dev", "QA", and "Prod"
|
CURRENT_ENVIRONMENT: process.env.__VUE_APP_CURRENT_ENVIRONMENT__, // "Localhost", "Dev", "QA", and "Prod"
|
||||||
HERITAGE_FUNNEL: process.env.VUE_APP_HERITAGE_FUNNEL,
|
HERITAGE_FUNNEL: process.env.__VUE_APP_HERITAGE_FUNNEL__,
|
||||||
SAFELITE_HOP: process.env.VUE_APP_SAFELITE_HOP,
|
SAFELITE_HOP: process.env.__VUE_APP_SAFELITE_HOP__,
|
||||||
APPLICATION_NAME: "FixMyGlass",
|
APPLICATION_NAME: "FixMyGlass",
|
||||||
ANALYTICS_APPLICATION_NAME: "FixMyGlassNextGen",
|
ANALYTICS_APPLICATION_NAME: "FixMyGlassNextGen",
|
||||||
APPLICATION_ABBREVIATION: "fmg",
|
APPLICATION_ABBREVIATION: "fmg",
|
||||||
|
|
@ -14,7 +14,7 @@ const applicationConfig = {
|
||||||
SITE_ENTRY_TRIGGER_VALUE: "FixMyGlass",
|
SITE_ENTRY_TRIGGER_VALUE: "FixMyGlass",
|
||||||
CASH_PARENT_ACCOUNT_NUMBER: 167132,
|
CASH_PARENT_ACCOUNT_NUMBER: 167132,
|
||||||
CASH_DEFAULT_BILL_TO_ACCOUNT_NUMBER: "87291",
|
CASH_DEFAULT_BILL_TO_ACCOUNT_NUMBER: "87291",
|
||||||
MY_ACCOUNT: process.env.VUE_APP_MY_ACCOUNT,
|
MY_ACCOUNT: process.env.__VUE_APP_MY_ACCOUNT__,
|
||||||
PIA_RESPONSE_URL:
|
PIA_RESPONSE_URL:
|
||||||
location.protocol + "//" + location.host + "/fmg/payment-pia-return?src=concept-funnel",
|
location.protocol + "//" + location.host + "/fmg/payment-pia-return?src=concept-funnel",
|
||||||
PIA_CANCEL_URL:
|
PIA_CANCEL_URL:
|
||||||
|
|
@ -29,7 +29,7 @@ const applicationConfig = {
|
||||||
FRONTEND_LOGGER_PATH: "/analytics/api/v1/logging",
|
FRONTEND_LOGGER_PATH: "/analytics/api/v1/logging",
|
||||||
AFFILIATE_COOKIE_CONTAINING_NAME: "_Track",
|
AFFILIATE_COOKIE_CONTAINING_NAME: "_Track",
|
||||||
COOKIE_NAME_LENGTH_MAX_LIMIT: 44,
|
COOKIE_NAME_LENGTH_MAX_LIMIT: 44,
|
||||||
SESSION_TIMEOUT_MINUTES: process.env.VUE_APP_SESSION_TIMEOUT_MINUTES,
|
SESSION_TIMEOUT_MINUTES: process.env.__VUE_APP_SESSION_TIMEOUT_MINUTES__,
|
||||||
RETURN_USER_PAGE: "/fmg/return-user",
|
RETURN_USER_PAGE: "/fmg/return-user",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,16 +2,6 @@
|
||||||
<div
|
<div
|
||||||
class="date-picker text-center"
|
class="date-picker text-center"
|
||||||
:class="`${calendarViewDirection} calendar-2025 ${showPricingByDayClass}`">
|
:class="`${calendarViewDirection} calendar-2025 ${showPricingByDayClass}`">
|
||||||
<div class="date-picker-header">
|
|
||||||
<funnelSubHeader cmsWidgetName="DatePickerSubHeaderWidget" class="mt-5" />
|
|
||||||
<textBlock
|
|
||||||
v-show="durationTextBlockCopy"
|
|
||||||
:customText="durationTextBlockCopy"
|
|
||||||
typeStyle="small"
|
|
||||||
marginTopSizeOverride="0"
|
|
||||||
class="duration-text-block" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<fieldset id="date-picker-fieldset" ref="datePickerFieldset">
|
<fieldset id="date-picker-fieldset" ref="datePickerFieldset">
|
||||||
<legend class="sr-only">Select a day and time</legend>
|
<legend class="sr-only">Select a day and time</legend>
|
||||||
<div
|
<div
|
||||||
|
|
@ -139,36 +129,11 @@
|
||||||
@click="showAnotherMonth">
|
@click="showAnotherMonth">
|
||||||
View more dates
|
View more dates
|
||||||
</button>
|
</button>
|
||||||
<timeSlotQuestion
|
|
||||||
ref="timeSlotModalQuestion"
|
|
||||||
v-model="selectedTimeSlotInfo"
|
|
||||||
cmsWidgetName="TimeSlotModalQuestion"
|
|
||||||
waitListLabelWidget="WaitListLabelWidget"
|
|
||||||
waitListQuestionWidget="WaitListQuestionWidget"
|
|
||||||
mobilePremiumCmsWidgetName="MobilePremiumTimeSlotModal"
|
|
||||||
mobileCmsWidgetName="MobileTimeSlotModal"
|
|
||||||
dropoffCmsWidgetName="DropOffTimeSlotModal"
|
|
||||||
sameDayDropOffCmsWidgetName="SameDayDropOffTimeSlotModal"
|
|
||||||
overnightDropOffCmsWidgetName="OvernightDropOffTimeSlotModal"
|
|
||||||
dropOffOrPickATimeQuestionCmsWidgetName="DropOffOrPickATimeQuestionWidget"
|
|
||||||
:selectedDate="selectedDate"
|
|
||||||
:appointmentType="appointmentType"
|
|
||||||
:premiumAppointmentFee="premiumAppointmentFee"
|
|
||||||
:displayWaitList="displayWaitList"
|
|
||||||
:timeSlotsForSelectedDate="timeSlotsForSelectedDate"
|
|
||||||
:estimatedServiceMinutesMinimum="estimatedServiceMinutesMinimum"
|
|
||||||
:estimatedServiceMinutesMaximum="estimatedServiceMinutesMaximum"
|
|
||||||
@waitListRequested="handleWaitListRequested"
|
|
||||||
@time-slot-modal-closed="timeSlotModalClosed" />
|
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import timeSlotQuestion from "@/layouts/schedule/time-slot-question/time-slot-question.vue";
|
|
||||||
import textBlock from "@/digital-components/text-block/text-block";
|
|
||||||
import funnelSubHeader from "@/fmg-components/funnel-sub-header/funnel-sub-header";
|
|
||||||
|
|
||||||
// Supporting files
|
// Supporting files
|
||||||
import loader from "@/ux-components/loader/loader";
|
import loader from "@/ux-components/loader/loader";
|
||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
|
|
@ -178,12 +143,6 @@ import {
|
||||||
MONTHS_OF_YEAR,
|
MONTHS_OF_YEAR,
|
||||||
DAYS_OF_WEEK,
|
DAYS_OF_WEEK,
|
||||||
} from "@/digital-components/date-picker/mixins/constants";
|
} from "@/digital-components/date-picker/mixins/constants";
|
||||||
import {
|
|
||||||
AppointmentTypeStrings,
|
|
||||||
RouteCodeFlags,
|
|
||||||
PREMIUM_FEE_PART_TYPE,
|
|
||||||
cmsWidgetFieldMappings,
|
|
||||||
} from "@/constants/schedule-constants";
|
|
||||||
import {
|
import {
|
||||||
selectableDaysOptions,
|
selectableDaysOptions,
|
||||||
requiredParameter,
|
requiredParameter,
|
||||||
|
|
@ -195,7 +154,6 @@ import {
|
||||||
import { useField, ErrorMessage } from "vee-validate";
|
import { useField, ErrorMessage } from "vee-validate";
|
||||||
import { deepClone } from "@/helpers/object-helper";
|
import { deepClone } from "@/helpers/object-helper";
|
||||||
import { v4 as uuidv4 } from "uuid";
|
import { v4 as uuidv4 } from "uuid";
|
||||||
import { getDisplayTextForDurationLength } from "@/helpers/duration-length-helper";
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "datePicker",
|
name: "datePicker",
|
||||||
|
|
@ -205,10 +163,8 @@ export default {
|
||||||
months: null,
|
months: null,
|
||||||
disableViewMoreDatesButton: false,
|
disableViewMoreDatesButton: false,
|
||||||
hideSomeDaysForInitialView: null,
|
hideSomeDaysForInitialView: null,
|
||||||
selectedTimeSlotInfo: this.getSelectedTimeSlotInfo(),
|
|
||||||
selectableDatesInshop: [], // NOTE: this and the mobile version below use monthNum (1-based), NOT monthIndex (0-based)
|
selectableDatesInshop: [], // NOTE: this and the mobile version below use monthNum (1-based), NOT monthIndex (0-based)
|
||||||
selectableDatesMobile: [],
|
selectableDatesMobile: [],
|
||||||
durationTextBlockCopyForInshopOrDropoff: null,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
|
|
@ -242,12 +198,6 @@ export default {
|
||||||
pricingByDayBasePrice: Number,
|
pricingByDayBasePrice: Number,
|
||||||
pricingByDayUpcharge: Number,
|
pricingByDayUpcharge: Number,
|
||||||
isPricingByDayExperiment: Boolean,
|
isPricingByDayExperiment: Boolean,
|
||||||
timeSlotsForSelectedDate: Object,
|
|
||||||
appointmentType: String,
|
|
||||||
premiumAppointmentFee: Object,
|
|
||||||
estimatedServiceMinutesMinimum: Number,
|
|
||||||
estimatedServiceMinutesMaximum: Number,
|
|
||||||
displayWaitList: Boolean,
|
|
||||||
isMobileSelected: Boolean,
|
isMobileSelected: Boolean,
|
||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
|
|
@ -309,80 +259,10 @@ export default {
|
||||||
this.dispatchStoreAction(this.storeActions.SAVE_WAITLIST_REQUESTED, false, false);
|
this.dispatchStoreAction(this.storeActions.SAVE_WAITLIST_REQUESTED, false, false);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
dropOffDurationText() {
|
|
||||||
return this.getCmsContent("DropOffTimeSlotModal", cmsWidgetFieldMappings.DURATION);
|
|
||||||
},
|
|
||||||
sameDayDropoffDurationText() {
|
|
||||||
return this.getCmsContent(
|
|
||||||
"SameDayDropOffTimeSlotModal",
|
|
||||||
cmsWidgetFieldMappings.DURATION
|
|
||||||
);
|
|
||||||
},
|
|
||||||
overnightDropoffDurationText() {
|
|
||||||
return this.getCmsContent(
|
|
||||||
"OvernightDropOffTimeSlotModal",
|
|
||||||
cmsWidgetFieldMappings.DURATION
|
|
||||||
);
|
|
||||||
},
|
|
||||||
inshopDurationText() {
|
|
||||||
const inshopDurationTextWithoutTime = this.getCmsContent(
|
|
||||||
"TimeSlotModalQuestion",
|
|
||||||
cmsWidgetFieldMappings.DURATION
|
|
||||||
);
|
|
||||||
|
|
||||||
const inshopDurationTime = getDisplayTextForDurationLength(
|
|
||||||
this.estimatedServiceMinutesMinimum,
|
|
||||||
this.estimatedServiceMinutesMaximum
|
|
||||||
);
|
|
||||||
|
|
||||||
if (this.estimatedServiceMinutesMinimum && this.estimatedServiceMinutesMaximum) {
|
|
||||||
return `${inshopDurationTextWithoutTime} <b>${inshopDurationTime}</b>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
mobileDurationText() {
|
|
||||||
const mobileDurationTextWithoutTime = this.getCmsContent(
|
|
||||||
"TimeSlotModalQuestion",
|
|
||||||
cmsWidgetFieldMappings.DURATION
|
|
||||||
);
|
|
||||||
|
|
||||||
const mobileDurationTime = getDisplayTextForDurationLength(
|
|
||||||
this.estimatedServiceMinutesMinimum,
|
|
||||||
this.estimatedServiceMinutesMaximum
|
|
||||||
);
|
|
||||||
|
|
||||||
if (this.estimatedServiceMinutesMinimum && this.estimatedServiceMinutesMaximum) {
|
|
||||||
return `${mobileDurationTextWithoutTime} <b>${mobileDurationTime}</b>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
durationTextBlockCopy() {
|
|
||||||
if (this.appointmentType === AppointmentTypeStrings.MOBILE) {
|
|
||||||
return this.mobileDurationText;
|
|
||||||
} else if (this.appointmentType === AppointmentTypeStrings.IN_SHOP) {
|
|
||||||
return this.inshopDurationText;
|
|
||||||
} else if (
|
|
||||||
this.appointmentType === AppointmentTypeStrings.IN_SHOP_OR_DROP_OFF ||
|
|
||||||
this.appointmentType === AppointmentTypeStrings.DROP_OFF
|
|
||||||
) {
|
|
||||||
return this.durationTextBlockCopyForInshopOrDropoff ?? this.inshopDurationText;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
isSameDay() {
|
|
||||||
if (!this.timeSlotsForSelectedDate) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const todaysDate = new Date().toISOString().split("T")[0];
|
|
||||||
return this.selectedDate === todaysDate;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async initializeComponent(initialData) {
|
async initializeComponent(initialData) {
|
||||||
await this.setCalendarData(initialData);
|
await this.setCalendarData(initialData);
|
||||||
this.$refs.timeSlotModalQuestion.initializeComponent();
|
|
||||||
},
|
},
|
||||||
fireDateSelectedEvent(event, date) {
|
fireDateSelectedEvent(event, date) {
|
||||||
// Ignore if arrow key selected radioButton
|
// Ignore if arrow key selected radioButton
|
||||||
|
|
@ -949,42 +829,9 @@ export default {
|
||||||
};
|
};
|
||||||
window.requestAnimationFrame(step);
|
window.requestAnimationFrame(step);
|
||||||
},
|
},
|
||||||
getSelectedTimeSlotInfo() {
|
|
||||||
const supportingItems = this.getSupportingItems();
|
|
||||||
|
|
||||||
var isPremiumAppointment = false;
|
|
||||||
if (supportingItems) {
|
|
||||||
isPremiumAppointment =
|
|
||||||
!!supportingItems.filter(
|
|
||||||
(lineItem) => lineItem.partType === PREMIUM_FEE_PART_TYPE
|
|
||||||
).length > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
const selectedTimeSlotInfo = {
|
|
||||||
timeSlot: store.getters.order.schedule,
|
|
||||||
isPremiumAppointment: isPremiumAppointment,
|
|
||||||
};
|
|
||||||
|
|
||||||
return selectedTimeSlotInfo;
|
|
||||||
},
|
|
||||||
getSupportingItems() {
|
getSupportingItems() {
|
||||||
return store.getters.lineItems.supportingItems;
|
return store.getters.lineItems.supportingItems;
|
||||||
},
|
},
|
||||||
handleWaitListRequested(value) {
|
|
||||||
this.$emit("waitListRequested", value);
|
|
||||||
},
|
|
||||||
getDurationTextBlockCopyForInshopOrDropoff(selectedRouteCode) {
|
|
||||||
if (selectedRouteCode?.includes(RouteCodeFlags.OVERNIGHT_DROP_OFF)) {
|
|
||||||
return this.overnightDropoffDurationText;
|
|
||||||
} else if (selectedRouteCode?.includes(RouteCodeFlags.ALL_DAY_DROP_OFF)) {
|
|
||||||
if (this.isSameDay) {
|
|
||||||
return this.sameDayDropoffDurationText;
|
|
||||||
} else {
|
|
||||||
return this.dropOffDurationText;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return this.inshopDurationText;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
modelValue(newValue) {
|
modelValue(newValue) {
|
||||||
|
|
@ -998,19 +845,10 @@ export default {
|
||||||
this.scrollToElement("date-of-month-error");
|
this.scrollToElement("date-of-month-error");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
selectedTimeSlotInfo(newValue) {
|
|
||||||
const routeCode = newValue?.timeSlot?.routeCode;
|
|
||||||
this.durationTextBlockCopyForInshopOrDropoff =
|
|
||||||
this.getDurationTextBlockCopyForInshopOrDropoff(routeCode);
|
|
||||||
this.$emit("TimeSlotSelected", newValue); // needed to update footer button text on Schedule page and to save to Store correctly
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
loader,
|
loader,
|
||||||
ErrorMessage,
|
ErrorMessage,
|
||||||
timeSlotQuestion,
|
|
||||||
textBlock,
|
|
||||||
funnelSubHeader,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -1028,14 +866,6 @@ export default {
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
|
||||||
.duration-text-block {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.date-picker-header {
|
|
||||||
margin-bottom: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
fieldset {
|
fieldset {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
|
||||||
|
|
@ -195,6 +195,7 @@ export default {
|
||||||
name: "cart",
|
name: "cart",
|
||||||
props: {
|
props: {
|
||||||
modelValue: Object,
|
modelValue: Object,
|
||||||
|
inactivePromos: Object,
|
||||||
damage: Object,
|
damage: Object,
|
||||||
servicePackageOptionsCmsName: String,
|
servicePackageOptionsCmsName: String,
|
||||||
availableVaps: Object,
|
availableVaps: Object,
|
||||||
|
|
@ -327,15 +328,26 @@ export default {
|
||||||
this.lineItems.vaps = [...lineItems.vaps];
|
this.lineItems.vaps = [...lineItems.vaps];
|
||||||
this.lineItems.promos = [...lineItems.promos];
|
this.lineItems.promos = [...lineItems.promos];
|
||||||
await this.dispatchStoreAction(storeActions.SAVE_VAPS, this.lineItems.vaps, false);
|
await this.dispatchStoreAction(storeActions.SAVE_VAPS, this.lineItems.vaps, false);
|
||||||
|
|
||||||
|
// any new promos that are also in inactive list?
|
||||||
|
const newInactivePromos = this.inactivePromos.filter((promo) => {
|
||||||
|
return this.lineItems.promos.includes(promo);
|
||||||
|
});
|
||||||
|
this.updateInactivePromos(newInactivePromos);
|
||||||
|
|
||||||
await this.dispatchStoreAction(
|
await this.dispatchStoreAction(
|
||||||
storeActions.SAVE_ACTIVE_AND_OR_INACTIVE_PROMOS,
|
storeActions.SAVE_ACTIVE_AND_OR_INACTIVE_PROMOS,
|
||||||
{
|
{
|
||||||
activePromos: this.lineItems.promos,
|
activePromos: this.lineItems.promos,
|
||||||
|
inactivePromos: this.inactivePromos,
|
||||||
},
|
},
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
updateInactivePromos(newInactivePromos) {
|
||||||
|
this.$emit("update:inactivePromos", newInactivePromos);
|
||||||
|
},
|
||||||
getPromoCodeList() {
|
getPromoCodeList() {
|
||||||
if (this.$refs["promoModalQuestion"]) {
|
if (this.$refs["promoModalQuestion"]) {
|
||||||
return this.$refs["promoModalQuestion"].getPromoCodeList();
|
return this.$refs["promoModalQuestion"].getPromoCodeList();
|
||||||
|
|
|
||||||
|
|
@ -42,12 +42,12 @@ export async function navigateToHeritageFunnel({ shouldSaveSession, pageNameToLo
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(process.env.VUE_APP_HERITAGE_FUNNEL.includes("fixmyglasstest") &&
|
(applicationConfig.HERITAGE_FUNNEL.includes("fixmyglasstest") &&
|
||||||
process.env.VUE_APP_CURRENT_ENVIRONMENT === "Localhost") ||
|
applicationConfig.CURRENT_ENVIRONMENT === "Localhost") ||
|
||||||
(process.env.VUE_APP_HERITAGE_FUNNEL.includes("fixmyglassdev") &&
|
(applicationConfig.HERITAGE_FUNNEL.includes("fixmyglassdev") &&
|
||||||
process.env.VUE_APP_CURRENT_ENVIRONMENT === "Localhost") ||
|
applicationConfig.CURRENT_ENVIRONMENT === "Localhost") ||
|
||||||
(process.env.VUE_APP_HERITAGE_FUNNEL.includes("localhost") &&
|
(applicationConfig.HERITAGE_FUNNEL.includes("localhost") &&
|
||||||
process.env.VUE_APP_CURRENT_ENVIRONMENT === "Localhost")
|
applicationConfig.CURRENT_ENVIRONMENT === "Localhost")
|
||||||
) {
|
) {
|
||||||
heritageParms["forceEndToEndInsurance"] = true;
|
heritageParms["forceEndToEndInsurance"] = true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,15 @@ import { shallowMount } from "@vue/test-utils";
|
||||||
import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
import { experimentSettings } from "@/constants/experiments";
|
import { experimentSettings } from "@/constants/experiments";
|
||||||
|
import { storeMutations } from "@/constants/store-mutations";
|
||||||
|
import globalMethods from "@/global-methods";
|
||||||
|
|
||||||
|
globalMethods.callHttpClient = jest.fn();
|
||||||
|
|
||||||
|
global.crypto = { randomUUID: jest.fn() };
|
||||||
|
const mockPriceOrderItemsAndSaveServerData = jest
|
||||||
|
.fn()
|
||||||
|
.mockResolvedValue(/* your mock return value */);
|
||||||
|
|
||||||
// Mock our module for promises.
|
// Mock our module for promises.
|
||||||
jest.mock("@/helpers/layout-helper.js", () => ({
|
jest.mock("@/helpers/layout-helper.js", () => ({
|
||||||
|
|
@ -34,7 +43,6 @@ describe("payment-method.vue", () => {
|
||||||
// Assert
|
// Assert
|
||||||
expect(wrapper.vm.$router.navigateWithoutSaving).toHaveBeenCalled();
|
expect(wrapper.vm.$router.navigateWithoutSaving).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("if the continue button is clicked, navigate forward", async () => {
|
test("if the continue button is clicked, navigate forward", async () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
const { wrapper } = setupMocks();
|
const { wrapper } = setupMocks();
|
||||||
|
|
@ -81,7 +89,10 @@ function setupMocks() {
|
||||||
city: "Anytown",
|
city: "Anytown",
|
||||||
state: "OH",
|
state: "OH",
|
||||||
zipCode: "00000",
|
zipCode: "00000",
|
||||||
|
provider: { providerNumber: "0000567" },
|
||||||
},
|
},
|
||||||
|
vehicle: { year: 2020, make: "Toyota", model: "Camry", carId: "12345" },
|
||||||
|
damage: { isRepair: false },
|
||||||
},
|
},
|
||||||
policy: {
|
policy: {
|
||||||
currentDeductible: 123,
|
currentDeductible: 123,
|
||||||
|
|
@ -101,6 +112,9 @@ function setupMocks() {
|
||||||
store: {
|
store: {
|
||||||
getters: store.getters,
|
getters: store.getters,
|
||||||
},
|
},
|
||||||
|
actions: {
|
||||||
|
priceOrderItemsAndSaveServerData: mockPriceOrderItemsAndSaveServerData,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const mockMixin = {
|
const mockMixin = {
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@
|
||||||
:availableVaps="availableVaps"
|
:availableVaps="availableVaps"
|
||||||
:allowItemRemoval="true"
|
:allowItemRemoval="true"
|
||||||
v-model="lineItems"
|
v-model="lineItems"
|
||||||
|
v-model:inactivePromos="inactivePromos"
|
||||||
pageName="payment-method"
|
pageName="payment-method"
|
||||||
servicePackageOptionsCmsName="ServicePackageTitle"
|
servicePackageOptionsCmsName="ServicePackageTitle"
|
||||||
recyclingModalCmsWidgetName="RecycleModal"
|
recyclingModalCmsWidgetName="RecycleModal"
|
||||||
|
|
@ -263,7 +264,7 @@ export default {
|
||||||
: (resultMap.wipers.filter((wiper) => wiper.partType == partTypeStrings.REAR_WIPER) ??
|
: (resultMap.wipers.filter((wiper) => wiper.partType == partTypeStrings.REAR_WIPER) ??
|
||||||
[]);
|
[]);
|
||||||
|
|
||||||
const lineItemsToTax = [
|
const allLineItems = [
|
||||||
resultMap.rainDefense,
|
resultMap.rainDefense,
|
||||||
...supportingItems,
|
...supportingItems,
|
||||||
...availableFrontWipers,
|
...availableFrontWipers,
|
||||||
|
|
@ -272,6 +273,10 @@ export default {
|
||||||
...vaps,
|
...vaps,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const lineItemsToTax = Array.from(
|
||||||
|
new Map(allLineItems.map((item) => [item.partNumber, item])).values()
|
||||||
|
);
|
||||||
|
|
||||||
const availableVaps = [
|
const availableVaps = [
|
||||||
resultMap.rainDefense,
|
resultMap.rainDefense,
|
||||||
...availableFrontWipers,
|
...availableFrontWipers,
|
||||||
|
|
@ -542,6 +547,16 @@ export default {
|
||||||
getPromoCodeWithoutBundleIdentifier(x.promoCode)
|
getPromoCodeWithoutBundleIdentifier(x.promoCode)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// SAVE PROMO CHANGES TO STORE
|
||||||
|
this.dispatchStoreAction(
|
||||||
|
storeActions.SAVE_ACTIVE_AND_OR_INACTIVE_PROMOS,
|
||||||
|
{
|
||||||
|
activePromos: this.lineItems.promos,
|
||||||
|
inactivePromos: this.inactivePromos,
|
||||||
|
},
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
this.$refs.loadingModal.hideModal();
|
this.$refs.loadingModal.hideModal();
|
||||||
},
|
},
|
||||||
getIsRecalAckOptInFromStore() {
|
getIsRecalAckOptInFromStore() {
|
||||||
|
|
@ -563,6 +578,7 @@ export default {
|
||||||
this.paymentMethod,
|
this.paymentMethod,
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
// save lineitems as they now have salestax added
|
// save lineitems as they now have salestax added
|
||||||
await this.dispatchStoreAction(
|
await this.dispatchStoreAction(
|
||||||
storeActions.SAVE_GLASS_PARTS_SUPPRESSING_STATE_RESETTING,
|
storeActions.SAVE_GLASS_PARTS_SUPPRESSING_STATE_RESETTING,
|
||||||
|
|
|
||||||
114
src/layouts/schedule/duration-text-block/duration-text-block.vue
Normal file
114
src/layouts/schedule/duration-text-block/duration-text-block.vue
Normal file
|
|
@ -0,0 +1,114 @@
|
||||||
|
<template>
|
||||||
|
<textBlock
|
||||||
|
:customText="durationTextBlockCopy"
|
||||||
|
typeStyle="small"
|
||||||
|
marginTopSizeOverride="0"
|
||||||
|
class="duration-text-block" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Components
|
||||||
|
import textBlock from "@/digital-components/text-block/text-block";
|
||||||
|
|
||||||
|
// Constants
|
||||||
|
import { AppointmentTypeStrings, cmsWidgetFieldMappings } from "@/constants/schedule-constants";
|
||||||
|
|
||||||
|
// Helpers
|
||||||
|
import { getDisplayTextForDurationLength } from "@/helpers/duration-length-helper";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "duration-text-block",
|
||||||
|
props: {
|
||||||
|
isSameDay: Boolean,
|
||||||
|
appointmentType: String,
|
||||||
|
estimatedServiceMinutesMinimum: String,
|
||||||
|
estimatedServiceMinutesMaximum: String,
|
||||||
|
isOvernightDropoff: Boolean,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
selectedRouteCode: null,
|
||||||
|
selectedAnswerForDropOffOrInshop: null,
|
||||||
|
selectedAnswerForTimeSlots: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
dropOffDurationText() {
|
||||||
|
return this.getCmsContent("DropOffTimeSlotModal", cmsWidgetFieldMappings.DURATION);
|
||||||
|
},
|
||||||
|
sameDayDropoffDurationText() {
|
||||||
|
return this.getCmsContent(
|
||||||
|
"SameDayDropOffTimeSlotModal",
|
||||||
|
cmsWidgetFieldMappings.DURATION
|
||||||
|
);
|
||||||
|
},
|
||||||
|
overnightDropoffDurationText() {
|
||||||
|
return this.getCmsContent(
|
||||||
|
"OvernightDropOffTimeSlotModal",
|
||||||
|
cmsWidgetFieldMappings.DURATION
|
||||||
|
);
|
||||||
|
},
|
||||||
|
inshopDurationText() {
|
||||||
|
const inshopDurationTextWithoutTime = this.getCmsContent(
|
||||||
|
"TimeSlotModalQuestion",
|
||||||
|
cmsWidgetFieldMappings.DURATION
|
||||||
|
);
|
||||||
|
|
||||||
|
const inshopDurationTime = getDisplayTextForDurationLength(
|
||||||
|
this.estimatedServiceMinutesMinimum,
|
||||||
|
this.estimatedServiceMinutesMaximum
|
||||||
|
);
|
||||||
|
|
||||||
|
if (this.estimatedServiceMinutesMinimum && this.estimatedServiceMinutesMaximum) {
|
||||||
|
return `${inshopDurationTextWithoutTime} <b>${inshopDurationTime}</b>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
mobileDurationText() {
|
||||||
|
const mobileDurationTextWithoutTime = this.getCmsContent(
|
||||||
|
"TimeSlotModalQuestion",
|
||||||
|
cmsWidgetFieldMappings.DURATION
|
||||||
|
);
|
||||||
|
|
||||||
|
const mobileDurationTime = getDisplayTextForDurationLength(
|
||||||
|
this.estimatedServiceMinutesMinimum,
|
||||||
|
this.estimatedServiceMinutesMaximum
|
||||||
|
);
|
||||||
|
|
||||||
|
if (this.estimatedServiceMinutesMinimum && this.estimatedServiceMinutesMaximum) {
|
||||||
|
return `${mobileDurationTextWithoutTime} ${mobileDurationTime}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
durationTextBlockCopy() {
|
||||||
|
if (this.appointmentType === AppointmentTypeStrings.MOBILE) {
|
||||||
|
return this.mobileDurationText;
|
||||||
|
} else if (this.appointmentType === AppointmentTypeStrings.DROP_OFF) {
|
||||||
|
return this.getDurationTextBlockCopyForDropoff();
|
||||||
|
} else {
|
||||||
|
return this.inshopDurationText;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getDurationTextBlockCopyForDropoff() {
|
||||||
|
if (this.isOvernightDropoff) {
|
||||||
|
return this.overnightDropoffDurationText;
|
||||||
|
} else {
|
||||||
|
if (this.isSameDay) {
|
||||||
|
return this.sameDayDropoffDurationText;
|
||||||
|
} else {
|
||||||
|
return this.dropOffDurationText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
textBlock,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style></style>
|
||||||
|
|
@ -0,0 +1,141 @@
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="mobile-fee-waiver-alert-container py-3 px-4 my-2"
|
||||||
|
:class="[isExpanded ? 'expanded' : '']">
|
||||||
|
<div
|
||||||
|
class="mobile-fee-waiver-header d-flex align-items-center justify-content-between"
|
||||||
|
@click="toggleIsExpanded">
|
||||||
|
<span>
|
||||||
|
{{ headerText }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="mobile-fee-waiver-body">
|
||||||
|
<div class="mobile-fee-waiver-copy">
|
||||||
|
{{ subheaderText }}
|
||||||
|
</div>
|
||||||
|
<div class="mobile-fee-waiver-price-comp">
|
||||||
|
<span class="mobile-fee-waiver-strikethrough-price">
|
||||||
|
{{ priceStrikethroughText }}
|
||||||
|
</span>
|
||||||
|
<span class="mobile-fee-waiver-free-price">{{ freePriceText }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "mobile-fee-waiver-alert",
|
||||||
|
props: {
|
||||||
|
cmsWidgetName: String,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isExpanded: true,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
headerText() {
|
||||||
|
return this.getCmsContent(this.cmsWidgetName, "HeaderText");
|
||||||
|
},
|
||||||
|
subheaderText() {
|
||||||
|
return this.getCmsContent(this.cmsWidgetName, "SubheaderText");
|
||||||
|
},
|
||||||
|
priceStrikethroughText() {
|
||||||
|
return this.getCmsContent(this.cmsWidgetName, "FooterText");
|
||||||
|
},
|
||||||
|
freePriceText() {
|
||||||
|
return this.getCmsContent(this.cmsWidgetName, "FooterText2");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
toggleIsExpanded() {
|
||||||
|
this.setIsExpanded(!this.isExpanded);
|
||||||
|
},
|
||||||
|
setIsExpanded(newVal) {
|
||||||
|
this.isExpanded = newVal;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
components: {},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.mobile-fee-waiver-alert-container {
|
||||||
|
background: $green-100;
|
||||||
|
border-radius: 0.25em;
|
||||||
|
border: 1px solid $green-400;
|
||||||
|
|
||||||
|
.mobile-fee-waiver-header {
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
span {
|
||||||
|
flex-grow: 1;
|
||||||
|
color: $black;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: "";
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3E%3Cpath fill='%230C7E47' d='M8 0a8 8 0 1 0 0 16A8 8 0 0 0 8 0Zm3.7 6.171-4.449 4.45a.559.559 0 0 1-.8 0l-2.16-2.16a.563.563 0 0 1 .792-.8l1.76 1.76 4.066-4.042a.563.563 0 1 1 .792.8v-.008Z'/%3E%3C/svg%3E");
|
||||||
|
width: 1rem;
|
||||||
|
height: 1rem;
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: "";
|
||||||
|
transition: all 0.5s ease;
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 9'%3E%3Cpath d='M7.99282 0.117702C7.75716 0.116049 7.53044 0.207797 7.3623 0.37286L0.255553 7.46878C0.0862976 7.63796 -0.00878906 7.86742 -0.00878906 8.10667C-0.00878906 8.34593 0.0862976 8.57539 0.255553 8.74457C0.424808 8.91374 0.654368 9.00879 0.893731 9.00879C1.13309 9.00879 1.36265 8.91374 1.53191 8.74457L7.99282 2.27378L14.4614 8.74457C14.6307 8.91205 14.8595 9.00547 15.0977 9.00428C15.3359 9.00308 15.5638 8.90737 15.7314 8.73819C15.8989 8.56901 15.9924 8.34022 15.9912 8.10216C15.99 7.8641 15.8942 7.63627 15.725 7.46878L8.6259 0.377963C8.45765 0.21088 8.22999 0.117289 7.99282 0.117702Z' fill='%230C7E47'/%3E%3C/svg%3E");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center center;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
display: inline-flex;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-fee-waiver-body {
|
||||||
|
transition: all 500ms ease-out;
|
||||||
|
visibility: hidden;
|
||||||
|
overflow: hidden;
|
||||||
|
max-height: 0;
|
||||||
|
|
||||||
|
.mobile-fee-waiver-price-comp {
|
||||||
|
margin-top: 0.5em;
|
||||||
|
|
||||||
|
.mobile-fee-waiver-strikethrough-price {
|
||||||
|
font-size: smaller;
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-fee-waiver-free-price {
|
||||||
|
display: inline-block;
|
||||||
|
font-weight: bolder;
|
||||||
|
border-radius: 1em;
|
||||||
|
background-color: $green-300;
|
||||||
|
padding: 0 0.5em;
|
||||||
|
margin-inline-start: 0.5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.expanded {
|
||||||
|
.mobile-fee-waiver-header {
|
||||||
|
&::after {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-fee-waiver-body {
|
||||||
|
visibility: visible;
|
||||||
|
max-height: none;
|
||||||
|
|
||||||
|
border-top: 1px solid $green-400;
|
||||||
|
padding-top: 0.5em;
|
||||||
|
margin-top: 0.5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -5,17 +5,16 @@
|
||||||
<div class="container page-container-grouped-styles">
|
<div class="container page-container-grouped-styles">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 col-md-10 col-lg-8 col-xl-7">
|
<div class="col-12 col-md-10 col-lg-8 col-xl-7">
|
||||||
<alert
|
<mobileFeeWaiverAlert
|
||||||
ref="alertMobileFeeFree"
|
cmsWidgetName="MobileFeeFreeDropdownWidget"
|
||||||
class="mb-5"
|
v-if="showMobileFreeAlert" />
|
||||||
cmsWidgetName="AlertMobileFeeFreeWidget"
|
|
||||||
v-if="showMobileFreeAlert"
|
|
||||||
alertClass="alert-success" />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row appointment-type">
|
<div class="row appointment-type">
|
||||||
<div class="col-12 col-md-10 col-lg-8 col-xl-7">
|
<div class="col-12 col-md-10 col-lg-8 col-xl-7">
|
||||||
<funnelSubHeader cmsWidgetName="ScheduleYourServiceWidget" class="mb-5" />
|
<funnelSubHeader
|
||||||
|
cmsWidgetName="ScheduleYourServiceWidget"
|
||||||
|
id="schedule-your-service" />
|
||||||
<appointmentTypeQuestion
|
<appointmentTypeQuestion
|
||||||
v-model="appointmentTypeFromAppointmentTypeQuestion"
|
v-model="appointmentTypeFromAppointmentTypeQuestion"
|
||||||
v-show="isAppointmentTypeDisplayed"
|
v-show="isAppointmentTypeDisplayed"
|
||||||
|
|
@ -118,6 +117,15 @@
|
||||||
<div class="row date-picker-wrapper">
|
<div class="row date-picker-wrapper">
|
||||||
<div class="col-12 col-md-10 col-lg-8 col-xl-7">
|
<div class="col-12 col-md-10 col-lg-8 col-xl-7">
|
||||||
<locationAlerts cmsWidgetPrefix="LocationAlert-" ref="locationAlerts" />
|
<locationAlerts cmsWidgetPrefix="LocationAlert-" ref="locationAlerts" />
|
||||||
|
<div v-show="appointmentType" class="date-picker-header fw-normal mt-5 mb-5">
|
||||||
|
<textBlock :customText="datePickerHeaderText" typeStyle="button-question" />
|
||||||
|
<durationTextBlock
|
||||||
|
:isSameDay="isSameDay"
|
||||||
|
:appointmentType="appointmentType"
|
||||||
|
:estimatedServiceMinutesMinimum="estimatedServiceMinutesMinimum"
|
||||||
|
:estimatedServiceMinutesMaximum="estimatedServiceMinutesMaximum"
|
||||||
|
:isOvernightDropoff="isOvernightDropoff" />
|
||||||
|
</div>
|
||||||
<datePicker
|
<datePicker
|
||||||
:currentZip="zipCode"
|
:currentZip="zipCode"
|
||||||
:currentProviderNumber="selectedProvider?.providerNumber"
|
:currentProviderNumber="selectedProvider?.providerNumber"
|
||||||
|
|
@ -134,14 +142,25 @@
|
||||||
:pricingByDayBasePrice="pricingByDayBasePrice"
|
:pricingByDayBasePrice="pricingByDayBasePrice"
|
||||||
:pricingByDayUpcharge="pricingByDayUpcharge"
|
:pricingByDayUpcharge="pricingByDayUpcharge"
|
||||||
:showPricingByDay="showPricingByDay"
|
:showPricingByDay="showPricingByDay"
|
||||||
:isPricingByDayExperiment="isPricingByDayExperiment"
|
:isPricingByDayExperiment="isPricingByDayExperiment" />
|
||||||
:timeSlotsForSelectedDate="timeSlotsForSelectedDate"
|
<timeSlotQuestion
|
||||||
|
ref="timeSlotModalQuestion"
|
||||||
|
v-if="showTimeSlotQuestion"
|
||||||
|
@time-slot-selection-changed="updateTimeSlot"
|
||||||
|
waitListLabelWidget="WaitListLabelWidget"
|
||||||
|
waitListQuestionWidget="WaitListQuestionWidget"
|
||||||
|
mobilePremiumCmsWidgetName="MobilePremiumTimeSlotModal"
|
||||||
|
mobileCmsWidgetName="MobileTimeSlotModal"
|
||||||
|
dropoffCmsWidgetName="DropOffTimeSlotModal"
|
||||||
|
sameDayDropOffCmsWidgetName="SameDayDropOffTimeSlotModal"
|
||||||
|
overnightDropOffCmsWidgetName="OvernightDropOffTimeSlotModal"
|
||||||
|
dropOffOrPickATimeQuestionCmsWidgetName="DropOffOrPickATimeQuestionWidget"
|
||||||
:appointmentType="appointmentType"
|
:appointmentType="appointmentType"
|
||||||
:premiumAppointmentFee="mobilePremiumAppointmentFee"
|
:premiumAppointmentFee="mobilePremiumAppointmentFee"
|
||||||
:estimatedServiceMinutesMinimum="getServiceMinutesMin"
|
|
||||||
:estimatedServiceMinutesMaximum="getServiceMinutesMax"
|
|
||||||
@timeSlotSelected="updateTimeSlot"
|
|
||||||
:displayWaitList="displayWaitList"
|
:displayWaitList="displayWaitList"
|
||||||
|
:timeSlotsForSelectedDate="timeSlotsForSelectedDate"
|
||||||
|
:isSameDay="isSameDay"
|
||||||
|
:selectedRouteCodeData="selectedRouteCodeData"
|
||||||
@waitListRequested="handleWaitListRequested" />
|
@waitListRequested="handleWaitListRequested" />
|
||||||
<navbar
|
<navbar
|
||||||
cmsWidgetName="FunnelFooterWidget"
|
cmsWidgetName="FunnelFooterWidget"
|
||||||
|
|
@ -160,9 +179,14 @@
|
||||||
import alert from "@/ux-components/alert/alert";
|
import alert from "@/ux-components/alert/alert";
|
||||||
import serviceZipModalQuestion from "@/layouts/service-location/service-zip-modal-question/service-zip-modal-question";
|
import serviceZipModalQuestion from "@/layouts/service-location/service-zip-modal-question/service-zip-modal-question";
|
||||||
import appointmentTypeQuestion from "@/layouts/service-location/appointment-type-question/appointment-type-question";
|
import appointmentTypeQuestion from "@/layouts/service-location/appointment-type-question/appointment-type-question";
|
||||||
import shopQuestion from "@/layouts/service-location/shop-question/shop-question";
|
|
||||||
import shopListButton from "@/layouts/service-location/shop-question/shop-list-button/shop-list-button";
|
import shopListButton from "@/layouts/service-location/shop-question/shop-list-button/shop-list-button";
|
||||||
import shopLocation from "@/layouts/schedule/shop-location/shop-location";
|
import shopLocation from "@/layouts/schedule/shop-location/shop-location";
|
||||||
|
import timeSlotQuestion from "@/layouts/schedule/time-slot-question/time-slot-question.vue";
|
||||||
|
import durationTextBlock from "@/layouts/schedule/duration-text-block/duration-text-block.vue";
|
||||||
|
|
||||||
|
import shopQuestion from "@/layouts/service-location/shop-question/shop-question";
|
||||||
|
import shopQuestionPopup from "@/layouts/service-location/shop-question/shop-question-popup";
|
||||||
|
import buttonQuestion from "@/digital-components/button-question/button-question.vue";
|
||||||
|
|
||||||
import funnelHeader from "@/fmg-components/funnel-header/funnel-header";
|
import funnelHeader from "@/fmg-components/funnel-header/funnel-header";
|
||||||
import navbar from "@/fmg-components/nav-bar/nav-bar";
|
import navbar from "@/fmg-components/nav-bar/nav-bar";
|
||||||
|
|
@ -174,6 +198,7 @@ import datePicker from "@/digital-components/date-picker/date-picker";
|
||||||
import locationAlerts from "@/layouts/schedule/location-alerts/location-alerts";
|
import locationAlerts from "@/layouts/schedule/location-alerts/location-alerts";
|
||||||
import contentGroupModal from "@/fmg-components/content-group-modal/content-group-modal";
|
import contentGroupModal from "@/fmg-components/content-group-modal/content-group-modal";
|
||||||
import textBlock from "@/digital-components/text-block/text-block";
|
import textBlock from "@/digital-components/text-block/text-block";
|
||||||
|
import mobileFeeWaiverAlert from "./mobile-fee-waiver-alert/mobile-fee-waiver-alert.vue";
|
||||||
|
|
||||||
// Supporting files
|
// Supporting files
|
||||||
import baseMixin from "@/mixins/base-mixin.js";
|
import baseMixin from "@/mixins/base-mixin.js";
|
||||||
|
|
@ -626,6 +651,16 @@ export default {
|
||||||
return this.isGlassServiceableMobile;
|
return this.isGlassServiceableMobile;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
showTimeSlotQuestion() {
|
||||||
|
if (
|
||||||
|
this.selectedDate &&
|
||||||
|
this.selectedDate.includes("mobile") &&
|
||||||
|
!this.isMobileSelected
|
||||||
|
) {
|
||||||
|
this.getNewSelectedDate();
|
||||||
|
}
|
||||||
|
return this.selectedDate && this.appointmentTypeFromAppointmentTypeQuestion;
|
||||||
|
},
|
||||||
isMobileStaticRecalibrationApplicable() {
|
isMobileStaticRecalibrationApplicable() {
|
||||||
return (
|
return (
|
||||||
this.displayMSR &&
|
this.displayMSR &&
|
||||||
|
|
@ -785,6 +820,21 @@ export default {
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
},
|
},
|
||||||
|
selectedRouteCodeData() {
|
||||||
|
if (!this.selectedTimeSlotInfo?.timeSlot?.routeCode) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
routeCode: this.selectedTimeSlotInfo.timeSlot.routeCode,
|
||||||
|
isPremiumAppointment: this.selectedTimeSlotInfo.isPremiumAppointment,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
questionText() {
|
||||||
|
return this.getCmsContent("YourSafeliteShopWidget", "QuestionText");
|
||||||
|
},
|
||||||
|
datePickerHeaderText() {
|
||||||
|
return this.getCmsContent("DatePickerSubHeaderWidget", "HeaderText");
|
||||||
|
},
|
||||||
appointmentTypeStrings() {
|
appointmentTypeStrings() {
|
||||||
return AppointmentTypeStrings;
|
return AppointmentTypeStrings;
|
||||||
},
|
},
|
||||||
|
|
@ -795,12 +845,12 @@ export default {
|
||||||
// Splits content when brackets are found in text so that text can be looped through and router-link can be injected when needed
|
// Splits content when brackets are found in text so that text can be looped through and router-link can be injected when needed
|
||||||
return this.splitCopyOnCMSPlaceHolder(this.ChangeShopLinkText);
|
return this.splitCopyOnCMSPlaceHolder(this.ChangeShopLinkText);
|
||||||
},
|
},
|
||||||
getServiceMinutesMin() {
|
estimatedServiceMinutesMinimum() {
|
||||||
return this.isMobileSelected
|
return this.isMobileSelected
|
||||||
? this.selectableDatesMobile.estimatedServiceMinutesMinimum
|
? this.selectableDatesMobile.estimatedServiceMinutesMinimum
|
||||||
: this.selectableDatesInshop.estimatedServiceMinutesMinimum;
|
: this.selectableDatesInshop.estimatedServiceMinutesMinimum;
|
||||||
},
|
},
|
||||||
getServiceMinutesMax() {
|
estimatedServiceMinutesMaximum() {
|
||||||
return this.isMobileSelected
|
return this.isMobileSelected
|
||||||
? this.selectableDatesMobile.estimatedServiceMinutesMaximum
|
? this.selectableDatesMobile.estimatedServiceMinutesMaximum
|
||||||
: this.selectableDatesInshop.estimatedServiceMinutesMaximum;
|
: this.selectableDatesInshop.estimatedServiceMinutesMaximum;
|
||||||
|
|
@ -820,6 +870,18 @@ export default {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
isSameDay() {
|
||||||
|
const todaysDate = new Date().toISOString().split("T")[0];
|
||||||
|
return this.selectedDate === todaysDate;
|
||||||
|
},
|
||||||
|
isOvernightDropoff() {
|
||||||
|
return (
|
||||||
|
this.selectedTimeSlotInfo?.timeSlot?.routeCode &&
|
||||||
|
this.selectedTimeSlotInfo.timeSlot.routeCode.includes(
|
||||||
|
RouteCodeFlags.OVERNIGHT_DROP_OFF
|
||||||
|
)
|
||||||
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
splitCopyOnCMSPlaceHolder,
|
splitCopyOnCMSPlaceHolder,
|
||||||
|
|
@ -1558,22 +1620,46 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
updateTimeSlot(timeSlotObj) {
|
updateTimeSlot(timeSlotObj) {
|
||||||
if (this.preSelectedDate && !timeSlotObj?.timeSlot?.date) return;
|
if (!timeSlotObj?.routeCode) {
|
||||||
this.selectedTimeSlotInfo = timeSlotObj;
|
this.appointmentType = AppointmentTypeStrings.IN_SHOP_OR_DROP_OFF;
|
||||||
|
this.selectedTimeSlotInfo = {
|
||||||
|
timeSlot: {
|
||||||
|
date: null,
|
||||||
|
routeCode: null,
|
||||||
|
startTime: null,
|
||||||
|
endTime: null,
|
||||||
|
jobMaxMinutes: null,
|
||||||
|
jobMinMinutes: null,
|
||||||
|
},
|
||||||
|
isPremiumAppointment: null,
|
||||||
|
};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const timeSlot = this.timeSlotsForSelectedDate?.timeSlots?.find(
|
||||||
|
(slot) => slot.id == timeSlotObj.routeCode
|
||||||
|
);
|
||||||
|
|
||||||
|
this.selectedTimeSlotInfo = {
|
||||||
|
timeSlot: {
|
||||||
|
date: this.timeSlotsForSelectedDate.date,
|
||||||
|
routeCode: timeSlotObj.routeCode,
|
||||||
|
startTime: timeSlot.startTime,
|
||||||
|
endTime: timeSlot.endTime,
|
||||||
|
jobMaxMinutes: this.estimatedServiceMinutesMaximum.toString(),
|
||||||
|
jobMinMinutes: this.estimatedServiceMinutesMinimum.toString(),
|
||||||
|
},
|
||||||
|
isPremiumAppointment: timeSlotObj.isPremiumAppointment ? true : false,
|
||||||
|
};
|
||||||
|
|
||||||
if (
|
if (
|
||||||
this.appointmentType !== AppointmentTypeStrings.MOBILE &&
|
this.appointmentType !== AppointmentTypeStrings.MOBILE &&
|
||||||
this.appointmentType !== null
|
this.appointmentType !== null
|
||||||
) {
|
) {
|
||||||
// update apptType based on routeCode to determine if it should be dropoff or inshop
|
// update apptType based on routeCode to determine if it should be dropoff or inshop
|
||||||
debugLog(`Updating appointment type based on route code.`);
|
debugLog(`Updating appointment type based on route code.`);
|
||||||
debugLog(`Route code =`, timeSlotObj.timeSlot.routeCode);
|
debugLog(`Route code =`, timeSlotObj.routeCode);
|
||||||
debugLog(
|
debugLog(`Parsed type = `, this.getInShopOrDropOffApptType(timeSlotObj.routeCode));
|
||||||
`Parsed type = `,
|
this.appointmentType = this.getInShopOrDropOffApptType(timeSlotObj.routeCode);
|
||||||
this.getInShopOrDropOffApptType(timeSlotObj.timeSlot.routeCode)
|
|
||||||
);
|
|
||||||
this.appointmentType = this.getInShopOrDropOffApptType(
|
|
||||||
timeSlotObj.timeSlot.routeCode
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getInShopOrDropOffApptType(routeCode) {
|
getInShopOrDropOffApptType(routeCode) {
|
||||||
|
|
@ -1607,8 +1693,13 @@ export default {
|
||||||
},
|
},
|
||||||
updateSelectedProvider(newProvider) {
|
updateSelectedProvider(newProvider) {
|
||||||
if (newProvider) {
|
if (newProvider) {
|
||||||
|
const didProviderNumberChange =
|
||||||
|
this.selectedProvider.providerNumber !== newProvider.providerNumber;
|
||||||
this.selectedProvider = newProvider;
|
this.selectedProvider = newProvider;
|
||||||
this.initializeDatePicker();
|
|
||||||
|
if (didProviderNumberChange) {
|
||||||
|
this.initializeDatePicker();
|
||||||
|
}
|
||||||
} else if (this.appointmentType === this.appointmentTypeStrings.MOBILE) {
|
} else if (this.appointmentType === this.appointmentTypeStrings.MOBILE) {
|
||||||
this.selectedProvider = {
|
this.selectedProvider = {
|
||||||
providerNumber: this.shopProviderData.mobileProviderNumber.toString(),
|
providerNumber: this.shopProviderData.mobileProviderNumber.toString(),
|
||||||
|
|
@ -1697,6 +1788,9 @@ export default {
|
||||||
this.selectedDate = this.getSelectedDate();
|
this.selectedDate = this.getSelectedDate();
|
||||||
this.setDisplayWaitList();
|
this.setDisplayWaitList();
|
||||||
},
|
},
|
||||||
|
getNewSelectedDate() {
|
||||||
|
this.selectedDate = this.getSelectedDate();
|
||||||
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
appointmentTypeFromAppointmentTypeQuestion: {
|
appointmentTypeFromAppointmentTypeQuestion: {
|
||||||
|
|
@ -1740,12 +1834,15 @@ export default {
|
||||||
datePicker,
|
datePicker,
|
||||||
locationAlerts,
|
locationAlerts,
|
||||||
textBlock,
|
textBlock,
|
||||||
|
timeSlotQuestion,
|
||||||
|
|
||||||
alert,
|
alert,
|
||||||
serviceZipModalQuestion,
|
serviceZipModalQuestion,
|
||||||
appointmentTypeQuestion,
|
appointmentTypeQuestion,
|
||||||
contentGroupModal,
|
contentGroupModal,
|
||||||
|
durationTextBlock,
|
||||||
shopLocation,
|
shopLocation,
|
||||||
|
mobileFeeWaiverAlert,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -1780,12 +1877,17 @@ export default {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.funnel-sub-header:not(.mb-5)) {
|
:deep(.funnel-sub-header:not(#schedule-your-service)) {
|
||||||
h5.dark-header {
|
h5.dark-header {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
:deep(.funnel-sub-header#schedule-your-service) {
|
||||||
|
h5.dark-header {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.col-12 a {
|
.col-12 a {
|
||||||
font-family: UrbanistSemibold;
|
font-family: UrbanistSemibold;
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
<buttonQuestion
|
<buttonQuestion
|
||||||
v-if="isDropOffAppointmentAvailable"
|
v-if="isDropOffAppointmentAvailable"
|
||||||
v-model="selectedAnswerForDropOffOrInshop"
|
v-model="selectedAnswerForDropOffOrInshop"
|
||||||
|
@update:modelValue="dropOffSelectionChanged"
|
||||||
:questionText="dropOffOrPickATimeQuestionLabelText"
|
:questionText="dropOffOrPickATimeQuestionLabelText"
|
||||||
:questionTextDescription="dropOffOrPickATimeQuestionDescription"
|
:questionTextDescription="dropOffOrPickATimeQuestionDescription"
|
||||||
:answers="answersForDropOffQuestion"
|
:answers="answersForDropOffQuestion"
|
||||||
|
|
@ -24,6 +25,7 @@
|
||||||
<buttonQuestion
|
<buttonQuestion
|
||||||
ref="buttonQuestion"
|
ref="buttonQuestion"
|
||||||
v-if="shouldDisplayTimeSlotQuestion"
|
v-if="shouldDisplayTimeSlotQuestion"
|
||||||
|
@update:modelValue="timeSlotSelectionChanged"
|
||||||
buttonTypeString="timeSlotModalListButton"
|
buttonTypeString="timeSlotModalListButton"
|
||||||
:buttonTypeObject="timeSlotModalListButton"
|
:buttonTypeObject="timeSlotModalListButton"
|
||||||
class="mt-4 timeslots"
|
class="mt-4 timeslots"
|
||||||
|
|
@ -44,7 +46,7 @@
|
||||||
v-if="supplementalInformationBlock"
|
v-if="supplementalInformationBlock"
|
||||||
v-html="supplementalInformationBlock" />
|
v-html="supplementalInformationBlock" />
|
||||||
<div
|
<div
|
||||||
v-if="hasWaitListExperiment && displayWaitListFeature && selectedDate"
|
v-if="hasWaitListExperiment && displayWaitListFeature"
|
||||||
class="mt-5 bg-light rounded waitlist">
|
class="mt-5 bg-light rounded waitlist">
|
||||||
<textBlock
|
<textBlock
|
||||||
cmsWidgetName="WaitListLabelWidget"
|
cmsWidgetName="WaitListLabelWidget"
|
||||||
|
|
@ -59,7 +61,7 @@
|
||||||
@click="waitListChecked" />
|
@click="waitListChecked" />
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="waitListRequested && displayWaitList && selectedDate"
|
v-if="waitListRequested && displayWaitList"
|
||||||
class="rounded waitlist-success"
|
class="rounded waitlist-success"
|
||||||
ref="waitlistSuccessMessage">
|
ref="waitlistSuccessMessage">
|
||||||
<img :src="waitListSuccessImage" class="success-image" />
|
<img :src="waitListSuccessImage" class="success-image" />
|
||||||
|
|
@ -92,7 +94,6 @@ import {
|
||||||
isDropOffRouteCode,
|
isDropOffRouteCode,
|
||||||
militaryToTwelveHourTime,
|
militaryToTwelveHourTime,
|
||||||
} from "@/layouts/schedule/helpers/schedule-helper";
|
} from "@/layouts/schedule/helpers/schedule-helper";
|
||||||
import { getDisplayTextForDurationLength } from "@/helpers/duration-length-helper";
|
|
||||||
|
|
||||||
// Validation
|
// Validation
|
||||||
import { defineRule, useField } from "vee-validate";
|
import { defineRule, useField } from "vee-validate";
|
||||||
|
|
@ -116,23 +117,8 @@ defineRule("time-slot-required", required(errorMessages.OPTION_REQUIRED));
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "time-slot-question",
|
name: "time-slot-question",
|
||||||
emits: ["update:modelValue", "TimeSlotSelected", "click-event"],
|
emits: ["update:modelValue", "time-slot-selection-changed"],
|
||||||
props: {
|
props: {
|
||||||
modelValue: {
|
|
||||||
type: Object,
|
|
||||||
default: () => ({
|
|
||||||
timeSlot: {
|
|
||||||
routeCode: null,
|
|
||||||
date: null,
|
|
||||||
startTime: null,
|
|
||||||
endTime: null,
|
|
||||||
jobMaxMinutes: null,
|
|
||||||
jobMinMinutes: null,
|
|
||||||
},
|
|
||||||
isPremiumAppointment: null,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
cmsWidgetName: String,
|
|
||||||
mobileCmsWidgetName: String,
|
mobileCmsWidgetName: String,
|
||||||
mobilePremiumCmsWidgetName: String,
|
mobilePremiumCmsWidgetName: String,
|
||||||
dropoffCmsWidgetName: String,
|
dropoffCmsWidgetName: String,
|
||||||
|
|
@ -142,21 +128,23 @@ export default {
|
||||||
appointmentType: String,
|
appointmentType: String,
|
||||||
timeSlotsForSelectedDate: Object,
|
timeSlotsForSelectedDate: Object,
|
||||||
premiumAppointmentFee: Object,
|
premiumAppointmentFee: Object,
|
||||||
estimatedServiceMinutesMinimum: Number,
|
|
||||||
estimatedServiceMinutesMaximum: Number,
|
|
||||||
validationRules: String,
|
validationRules: String,
|
||||||
customComponentId: String,
|
customComponentId: String,
|
||||||
selectedDate: String,
|
isSameDay: Boolean,
|
||||||
displayWaitList: Boolean,
|
displayWaitList: Boolean,
|
||||||
|
selectedRouteCodeData: Object,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
selectedRouteCode: null,
|
|
||||||
timeSlotModalListButton: timeSlotModalListButton,
|
timeSlotModalListButton: timeSlotModalListButton,
|
||||||
selectedAnswerForDropOffOrInshop: null,
|
|
||||||
selectedAnswerForTimeSlots: null,
|
selectedAnswerForTimeSlots: null,
|
||||||
|
selectedAnswerForDropOffOrInshop: null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
mounted() {
|
||||||
|
this.updateDropoffAndTimeSlotAnswersFromSelectedRouteCodeData(this.selectedRouteCodeData);
|
||||||
|
this.autoSelectTimeSlotIfOnlyOneIsAvailable();
|
||||||
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const uuid = uuidv4();
|
const uuid = uuidv4();
|
||||||
const componentId = !props.customComponentId
|
const componentId = !props.customComponentId
|
||||||
|
|
@ -186,20 +174,33 @@ export default {
|
||||||
errors,
|
errors,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
waitListRequested(newVal) {
|
||||||
|
if (newVal) {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.scrollToSuccessMessage();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
selectedRouteCodeData(newValue) {
|
||||||
|
this.updateDropoffAndTimeSlotAnswersFromSelectedRouteCodeData(newValue);
|
||||||
|
},
|
||||||
|
timeSlotsForSelectedDate() {
|
||||||
|
this.selectedAnswerForDropOffOrInshop = null;
|
||||||
|
this.selectedAnswerForTimeSlots = null;
|
||||||
|
this.autoSelectTimeSlotIfOnlyOneIsAvailable();
|
||||||
|
},
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
supplementalInformationBlock() {
|
supplementalInformationBlock() {
|
||||||
let appointmentTypeCmsWidgetName;
|
let appointmentTypeCmsWidgetName;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
this.selectedDate == null ||
|
|
||||||
this.appointmentType === AppointmentTypeStrings.IN_SHOP_OR_DROP_OFF ||
|
this.appointmentType === AppointmentTypeStrings.IN_SHOP_OR_DROP_OFF ||
|
||||||
this.appointmentType === AppointmentTypeStrings.IN_SHOP ||
|
this.appointmentType === AppointmentTypeStrings.IN_SHOP ||
|
||||||
this.appointmentType === AppointmentTypeStrings.DROP_OFF ||
|
this.appointmentType === AppointmentTypeStrings.DROP_OFF ||
|
||||||
!this.availableTimeSlots
|
!this.availableTimeSlots
|
||||||
) {
|
) {
|
||||||
if (!this.selectedAnswerForTimeSlots && !this.selectedAnswerForDropOffOrInshop) {
|
|
||||||
this.autoSelectTimeSlotIfOnlyOneIsAvailable();
|
|
||||||
}
|
|
||||||
return null;
|
return null;
|
||||||
// This is planned to be used again for drop-off
|
// This is planned to be used again for drop-off
|
||||||
// Wrote this to be ready for that eventuality, is untested and no HTML work done yet
|
// Wrote this to be ready for that eventuality, is untested and no HTML work done yet
|
||||||
|
|
@ -214,11 +215,7 @@ export default {
|
||||||
// );
|
// );
|
||||||
// }
|
// }
|
||||||
} else {
|
} else {
|
||||||
if (!this.selectedAnswerForTimeSlots && this.selectedRouteCode) {
|
appointmentTypeCmsWidgetName = this.selectedRouteCodeData?.routeCode?.includes(
|
||||||
// Clearing selectedRouteCode if no time slot is selected
|
|
||||||
this.resetSelectedTimeSlot();
|
|
||||||
}
|
|
||||||
appointmentTypeCmsWidgetName = this.selectedRouteCode?.includes(
|
|
||||||
PREMIUM_TIME_SLOT_ID_FLAG
|
PREMIUM_TIME_SLOT_ID_FLAG
|
||||||
)
|
)
|
||||||
? this.mobilePremiumCmsWidgetName
|
? this.mobilePremiumCmsWidgetName
|
||||||
|
|
@ -299,13 +296,6 @@ export default {
|
||||||
// return null;
|
// return null;
|
||||||
// }
|
// }
|
||||||
// },
|
// },
|
||||||
isSameDay() {
|
|
||||||
if (!this.timeSlotsForSelectedDate) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const todaysDate = new Date().toISOString().split("T")[0];
|
|
||||||
return this.selectedDate === todaysDate;
|
|
||||||
},
|
|
||||||
availableTimeSlots() {
|
availableTimeSlots() {
|
||||||
if (!this.timeSlotsForSelectedDate) {
|
if (!this.timeSlotsForSelectedDate) {
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -407,29 +397,60 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
initializeComponent() {
|
updateDropoffAndTimeSlotAnswersFromSelectedRouteCodeData(selectedRouteCodeData) {
|
||||||
this.setSelectedRouteCodeFromParent();
|
if (selectedRouteCodeData?.routeCode) {
|
||||||
},
|
let routeCode = selectedRouteCodeData.routeCode;
|
||||||
async setSelectedTimeSlot() {
|
if (isDropOffRouteCode(routeCode)) {
|
||||||
this.selectedRouteCode =
|
this.selectedAnswerForDropOffOrInshop = routeCode;
|
||||||
this.selectedAnswerForTimeSlots || this.selectedAnswerForDropOffOrInshop;
|
} else {
|
||||||
this.$emit(
|
this.selectedAnswerForDropOffOrInshop = PICK_A_TIME_BUTTON_VALUE;
|
||||||
"update:modelValue",
|
if (this.selectedRouteCodeData.isPremiumAppointment) {
|
||||||
this.getSelectedTimeSlotInfoObject(this.selectedRouteCode)
|
routeCode = this.addPremiumFlagToInput(routeCode);
|
||||||
);
|
}
|
||||||
},
|
this.selectedAnswerForTimeSlots = routeCode;
|
||||||
getRelevantDropOffCmsWidgetNameForSelectedTimeSlot(
|
}
|
||||||
selectedRouteCode,
|
} else {
|
||||||
isSameDayRelevant = false
|
this.selectedAnswerForDropOffOrInshop = null;
|
||||||
) {
|
this.selectedAnswerForTimeSlots = null;
|
||||||
if (selectedRouteCode.includes(RouteCodeFlags.OVERNIGHT_DROP_OFF)) {
|
|
||||||
return this.overnightDropOffCmsWidgetName;
|
|
||||||
} else if (selectedRouteCode.includes(RouteCodeFlags.ALL_DAY_DROP_OFF)) {
|
|
||||||
return this.isSameDay && isSameDayRelevant
|
|
||||||
? this.sameDayDropOffCmsWidgetName
|
|
||||||
: this.dropoffCmsWidgetName;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
dropOffSelectionChanged(newValue) {
|
||||||
|
let newSelectedRouteCodeData = null;
|
||||||
|
if (newValue && newValue != PICK_A_TIME_BUTTON_VALUE) {
|
||||||
|
newSelectedRouteCodeData = {
|
||||||
|
routeCode: newValue,
|
||||||
|
isPremiumAppointment: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
this.selectedAnswerForTimeSlots = null;
|
||||||
|
this.$emit("time-slot-selection-changed", newSelectedRouteCodeData);
|
||||||
|
},
|
||||||
|
timeSlotSelectionChanged(newValue) {
|
||||||
|
if (newValue) {
|
||||||
|
let routeCode = newValue;
|
||||||
|
let isPremiumAppointment = routeCode.includes(PREMIUM_TIME_SLOT_ID_FLAG);
|
||||||
|
if (isPremiumAppointment) {
|
||||||
|
routeCode = this.removePremiumFlagFromInput(routeCode);
|
||||||
|
}
|
||||||
|
this.$emit("time-slot-selection-changed", {
|
||||||
|
routeCode: routeCode,
|
||||||
|
isPremiumAppointment: isPremiumAppointment,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// This is unused but planned to be used again
|
||||||
|
// getRelevantDropOffCmsWidgetNameForSelectedTimeSlot(
|
||||||
|
// selectedRouteCode,
|
||||||
|
// isSameDayRelevant = false
|
||||||
|
// ) {
|
||||||
|
// if (selectedRouteCode.includes(RouteCodeFlags.OVERNIGHT_DROP_OFF)) {
|
||||||
|
// return this.overnightDropOffCmsWidgetName;
|
||||||
|
// } else if (selectedRouteCode.includes(RouteCodeFlags.ALL_DAY_DROP_OFF)) {
|
||||||
|
// return this.isSameDay && isSameDayRelevant
|
||||||
|
// ? this.sameDayDropOffCmsWidgetName
|
||||||
|
// : this.dropoffCmsWidgetName;
|
||||||
|
// }
|
||||||
|
// },
|
||||||
getAvailableTimeSlotsForInshop(timeSlotsForSelectedDate) {
|
getAvailableTimeSlotsForInshop(timeSlotsForSelectedDate) {
|
||||||
return timeSlotsForSelectedDate
|
return timeSlotsForSelectedDate
|
||||||
.filter((timeSlot) => {
|
.filter((timeSlot) => {
|
||||||
|
|
@ -463,8 +484,6 @@ export default {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.resetSelectedTimeSlotIfDateChange(timeSlotsForSelectedDate);
|
|
||||||
|
|
||||||
return availableTimeSlots;
|
return availableTimeSlots;
|
||||||
},
|
},
|
||||||
getPremiumAppointmentTimeSlot(timeSlotData) {
|
getPremiumAppointmentTimeSlot(timeSlotData) {
|
||||||
|
|
@ -480,35 +499,19 @@ export default {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
setSelectedRouteCodeFromParent() {
|
|
||||||
if (!this.modelValue?.timeSlot?.routeCode) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const routeCodeFromParent = this.modelValue.timeSlot.routeCode;
|
|
||||||
if (this.modelValue?.isPremiumAppointment) {
|
|
||||||
// Mobile Only
|
|
||||||
this.selectedAnswerForTimeSlots = this.addPremiumFlagToInput(routeCodeFromParent);
|
|
||||||
} else {
|
|
||||||
if (isDropOffRouteCode(routeCodeFromParent)) {
|
|
||||||
this.selectedAnswerForDropOffOrInshop = routeCodeFromParent;
|
|
||||||
} else {
|
|
||||||
this.selectedAnswerForDropOffOrInshop = PICK_A_TIME_BUTTON_VALUE;
|
|
||||||
this.selectedAnswerForTimeSlots = routeCodeFromParent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
getWaitListRequestedFromStore() {
|
getWaitListRequestedFromStore() {
|
||||||
return store.getters.order.customer?.waitListRequested;
|
return store.getters.order.customer?.waitListRequested;
|
||||||
},
|
},
|
||||||
autoSelectTimeSlotIfOnlyOneIsAvailable() {
|
autoSelectTimeSlotIfOnlyOneIsAvailable() {
|
||||||
const numberOfOptions = this.timeSlotsForSelectedDate?.timeSlots?.length;
|
const numberOfOptions = this.timeSlotsForSelectedDate?.timeSlots?.length;
|
||||||
if (this.appointmentType && numberOfOptions === 1) {
|
if (numberOfOptions === 1) {
|
||||||
if (this.availableTimeSlots.length > 0) {
|
if (this.availableTimeSlots.length > 0) {
|
||||||
|
// this.availableTimeSlots only returns mobile/inshop slots so we know
|
||||||
|
// the only available slot is not dropOFf
|
||||||
this.selectedAnswerForTimeSlots = this.availableTimeSlots[0].value;
|
this.selectedAnswerForTimeSlots = this.availableTimeSlots[0].value;
|
||||||
} else {
|
} else {
|
||||||
this.selectedAnswerForDropOffOrInshop = this.answersForDropOffQuestion[0].value;
|
this.selectedAnswerForDropOffOrInshop = this.answersForDropOffQuestion[0].value;
|
||||||
}
|
}
|
||||||
this.setSelectedTimeSlot();
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
addPremiumFlagToInput(routeCode) {
|
addPremiumFlagToInput(routeCode) {
|
||||||
|
|
@ -517,41 +520,6 @@ export default {
|
||||||
removePremiumFlagFromInput(routeCode) {
|
removePremiumFlagFromInput(routeCode) {
|
||||||
return routeCode.replace(PREMIUM_TIME_SLOT_ID_FLAG, "");
|
return routeCode.replace(PREMIUM_TIME_SLOT_ID_FLAG, "");
|
||||||
},
|
},
|
||||||
getSelectedTimeSlotInfoObject(routeCode) {
|
|
||||||
const routeCodeIncludesPremium = routeCode?.includes(PREMIUM_TIME_SLOT_ID_FLAG);
|
|
||||||
if (routeCodeIncludesPremium) {
|
|
||||||
routeCode = this.removePremiumFlagFromInput(routeCode);
|
|
||||||
}
|
|
||||||
const timeSlot = this.timeSlotsForSelectedDate?.timeSlots?.find(
|
|
||||||
(slot) => slot.id == routeCode
|
|
||||||
);
|
|
||||||
|
|
||||||
if (timeSlot) {
|
|
||||||
return {
|
|
||||||
timeSlot: {
|
|
||||||
date: this.timeSlotsForSelectedDate.date,
|
|
||||||
routeCode: timeSlot.id,
|
|
||||||
startTime: timeSlot.startTime,
|
|
||||||
endTime: timeSlot.endTime,
|
|
||||||
jobMaxMinutes: this.estimatedServiceMinutesMaximum?.toString() || null,
|
|
||||||
jobMinMinutes: this.estimatedServiceMinutesMinimum?.toString() || null,
|
|
||||||
},
|
|
||||||
isPremiumAppointment: routeCodeIncludesPremium ? true : false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
timeSlot: {
|
|
||||||
date: null,
|
|
||||||
routeCode: null,
|
|
||||||
startTime: null,
|
|
||||||
endTime: null,
|
|
||||||
jobMaxMinutes: null,
|
|
||||||
jobMinMinutes: null,
|
|
||||||
},
|
|
||||||
isPremiumAppointment: null,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
waitListChecked(event) {
|
waitListChecked(event) {
|
||||||
this.$emit("waitListRequested", event.target.checked);
|
this.$emit("waitListRequested", event.target.checked);
|
||||||
},
|
},
|
||||||
|
|
@ -561,65 +529,6 @@ export default {
|
||||||
successMessageElement.scrollIntoView({ behavior: "smooth" });
|
successMessageElement.scrollIntoView({ behavior: "smooth" });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
resetSelectedTimeSlotIfDateChange(timeSlotsForSelectedDate) {
|
|
||||||
if (this.selectedRouteCode) {
|
|
||||||
const selectedRouteCodeString = this.selectedRouteCode.replace(
|
|
||||||
PREMIUM_TIME_SLOT_ID_FLAG,
|
|
||||||
""
|
|
||||||
);
|
|
||||||
const matchingTimeSlot = timeSlotsForSelectedDate.find((slot) => {
|
|
||||||
return slot.id === selectedRouteCodeString;
|
|
||||||
});
|
|
||||||
if (!matchingTimeSlot) {
|
|
||||||
this.resetSelectedTimeSlot();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
resetSelectedTimeSlot() {
|
|
||||||
this.selectedRouteCode = null;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
waitListRequested(newVal) {
|
|
||||||
if (newVal) {
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.scrollToSuccessMessage();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
modelValue: {
|
|
||||||
handler(newValue) {
|
|
||||||
this.handleChange(newValue);
|
|
||||||
},
|
|
||||||
deep: true,
|
|
||||||
},
|
|
||||||
selectedDate: {
|
|
||||||
handler() {
|
|
||||||
this.resetSelectedTimeSlot();
|
|
||||||
this.selectedAnswerForDropOffOrInshop = null;
|
|
||||||
this.selectedAnswerForTimeSlots = null;
|
|
||||||
this.autoSelectTimeSlotIfOnlyOneIsAvailable();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
selectedRouteCode(newValue) {
|
|
||||||
if (newValue) {
|
|
||||||
this.setSelectedTimeSlot();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
selectedAnswerForDropOffOrInshop(newValue) {
|
|
||||||
if (newValue == PICK_A_TIME_BUTTON_VALUE) {
|
|
||||||
this.selectedRouteCode = null;
|
|
||||||
this.setSelectedTimeSlot();
|
|
||||||
} else {
|
|
||||||
this.selectedAnswerForTimeSlots = null;
|
|
||||||
this.selectedRouteCode = newValue;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
selectedAnswerForTimeSlots(newValue) {
|
|
||||||
if (newValue) {
|
|
||||||
this.selectedRouteCode = newValue;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
textBlock,
|
textBlock,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
|
import { applicationConfig } from "@/constants/application-config";
|
||||||
|
|
||||||
const externalUrls = {
|
const externalUrls = {
|
||||||
HERITAGE_FUNNEL: process.env.VUE_APP_HERITAGE_FUNNEL,
|
HERITAGE_FUNNEL: applicationConfig.HERITAGE_FUNNEL,
|
||||||
SAFELITE_HOP: process.env.VUE_APP_SAFELITE_HOP,
|
SAFELITE_HOP: applicationConfig.SAFELITE_HOP,
|
||||||
};
|
};
|
||||||
|
|
||||||
export { externalUrls };
|
export { externalUrls };
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,5 @@
|
||||||
process.env.VUE_APP_CONSUMER_CF_DISTRO = "https://digitalapi.dev.safelite.io";
|
const webpack = require('webpack');
|
||||||
//process.env.VUE_APP_HERITAGE_FUNNEL = "http://localhost:38000/default.aspx";
|
const { environmentVariablesWithDefaults } = require('./environment-variables.js');
|
||||||
process.env.VUE_APP_HERITAGE_FUNNEL = "https://fixmyglassdev.safelite.com/default.aspx";
|
|
||||||
process.env.VUE_APP_GOOGLE_PLACES_API_KEY = "AIzaSyDptGCkOPgN2uWJOy4ou4M33phRD4MAoJo";
|
|
||||||
process.env.VUE_APP_CURRENT_ENVIRONMENT = "Localhost";
|
|
||||||
process.env.VUE_APP_MY_ACCOUNT = "https://myaccountdev.safelite.com/";
|
|
||||||
//process.env.VUE_APP_SAFELITE_HOP = "http://localhost:60966/fmgCheckoutShared.aspx";
|
|
||||||
process.env.VUE_APP_SAFELITE_HOP = "https://sv2-safelitehop-dev.safelite.com/fmgCheckoutShared.aspx";
|
|
||||||
process.env.VUE_APP_SOLARWINDS_MONITORING = "";
|
|
||||||
process.env.VUE_APP_SESSION_TIMEOUT_MINUTES = 30;
|
|
||||||
|
|
||||||
// GA & GTM
|
|
||||||
process.env.VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY =
|
|
||||||
"(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src='https://www.googletagmanager.com/gtm.js?id='+i+dl+ '>m_auth=amlAYNhxUxuskQo7jmjadg>m_preview=env-38>m_cookies_win=x';f.parentNode.insertBefore(j,f);})(window,document,'script','dataLayer','GTM-M6XCRH');";
|
|
||||||
|
|
||||||
process.env.VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC =
|
|
||||||
"https://www.googletagmanager.com/ns.html?id=GTM-M6XCRH>m_auth=amlAYNhxUxuskQo7jmjadg>m_preview=env-38>m_cookies_win=x";
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
outputDir: "dist/fmg",
|
outputDir: "dist/fmg",
|
||||||
|
|
@ -36,5 +21,8 @@ module.exports = {
|
||||||
},
|
},
|
||||||
configureWebpack: {
|
configureWebpack: {
|
||||||
devtool: "source-map",
|
devtool: "source-map",
|
||||||
|
plugins: [
|
||||||
|
new webpack.EnvironmentPlugin(environmentVariablesWithDefaults),
|
||||||
|
],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,5 @@
|
||||||
process.env.VUE_APP_CONSUMER_CF_DISTRO = "__VUE_APP_CONSUMER_CF_DISTRO__";
|
const webpack = require('webpack');
|
||||||
process.env.VUE_APP_GOOGLE_PLACES_API_KEY = "__VUE_APP_GOOGLE_PLACES_API_KEY__";
|
const { environmentVariablesWithoutDefaults } = require('./environment-variables.js');
|
||||||
process.env.VUE_APP_HERITAGE_FUNNEL = "__VUE_APP_HERITAGE_FUNNEL__";
|
|
||||||
process.env.VUE_APP_CURRENT_ENVIRONMENT = "__VUE_APP_CURRENT_ENVIRONMENT__";
|
|
||||||
process.env.VUE_APP_MY_ACCOUNT = "__VUE_APP_MY_ACCOUNT__";
|
|
||||||
process.env.VUE_APP_SAFELITE_HOP = "__VUE_APP_SAFELITE_HOP__";
|
|
||||||
process.env.VUE_APP_SOLARWINDS_MONITORING_SCRIPT = "__VUE_APP_SOLARWINDS_MONITORING_SCRIPT__";
|
|
||||||
process.env.VUE_APP_SESSION_TIMEOUT_MINUTES = 30;
|
|
||||||
|
|
||||||
// GA & GTM
|
|
||||||
process.env.VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY = "__VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY__";
|
|
||||||
process.env.VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC =
|
|
||||||
"__VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC__";
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
outputDir: "dist/fmg",
|
outputDir: "dist/fmg",
|
||||||
|
|
@ -30,4 +19,9 @@ module.exports = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
configureWebpack: {
|
||||||
|
plugins: [
|
||||||
|
new webpack.EnvironmentPlugin(environmentVariablesWithoutDefaults),
|
||||||
|
],
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue