Merge pull request #1049 from Safelite/feature/CSR-1129-create-schedule-page

CSR-1129: create schedule page, put date picker on it
This commit is contained in:
AdamCaouetteSafelite 2023-04-05 14:00:33 -04:00 committed by GitHub
commit e4a391472a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 398 additions and 235 deletions

View file

@ -18,6 +18,7 @@ module.exports = {
"!src/common-components/date-picker/**/*.vue", // Temp until unit tests completed
"!src/layouts/vin-lookup/**/*.vue", //Temporary for Quote page testing
"!src/common-components/funnel-header/menu-modal/**/*.vue",
"!src/layouts/schedule/*.vue", // Temp test exclusion while in development
// END
], // ! means exclude from coverage.
testMatch: ["**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)"],

View file

@ -26,6 +26,7 @@ const errorMessages = {
OPTION_REQUIRED: "Please select an option",
VEHICLE_REQUIRED: "Please select a vehicle",
MOBILE_LOCATION_REQUIRED: "Please enter your service address",
DATE_REQUIRED: "Please select a date",
};
export { errorMessages };

View file

@ -1,5 +1,5 @@
<template>
<div class="date-picker" :class="calendarViewDirection">
<div class="date-picker text-center" :class="calendarViewDirection">
<fieldset v-if="months">
<legend class="sr-only">Date Picker</legend>
<div
@ -51,7 +51,7 @@
<button
v-if="calendarViewDirection === 'future'"
type="button"
class="btn-link"
class="btn btn-link"
@click="goForward">
View more dates
</button>
@ -371,92 +371,106 @@ export default {
</script>
<style lang="scss" scoped>
.calendar-grid-container {
margin: 0 auto;
max-width: 414px;
display: grid;
grid-template-columns: repeat(7, 1fr);
justify-content: center;
align-items: center;
padding: 4px;
.grid-item {
text-align: center;
margin: 10%;
&.first-day-,
&.first-day-0 {
grid-column-start: 1;
}
&.first-day-1 {
grid-column-start: 2;
}
&.first-day-2 {
grid-column-start: 3;
}
&.first-day-3 {
grid-column-start: 4;
}
&.first-day-4 {
grid-column-start: 5;
}
&.first-day-5 {
grid-column-start: 6;
}
&.first-day-6 {
grid-column-start: 7;
}
}
.month-year {
grid-area: 1 / 1 / 2 / 5;
text-transform: uppercase;
font-weight: 300;
letter-spacing: 0.75px;
}
.legend {
grid-area: 1 / 5 / 2 / 8;
.legend-circle {
border-radius: 50%;
width: 16px;
height: 16px;
background-color: $blue-100;
border: 1px solid $blue;
}
}
.nav-back,
.nav-forward {
display: none;
}
.radio-wrapper {
position: relative;
display: flex;
.date-picker {
.calendar-grid-container {
margin: 0 auto;
max-width: 414px;
display: grid;
grid-template-columns: repeat(7, 1fr);
justify-content: center;
align-items: center;
outline: none;
padding: 4px;
input[type="radio"] {
position: absolute; //override bootstrap
height: 0;
opacity: 0;
.grid-item {
text-align: center;
margin: 10%;
&:focus-visible + label {
box-shadow: 0 0 0 2.5px $blue;
&.first-day-,
&.first-day-0 {
grid-column-start: 1;
}
&.first-day-1 {
grid-column-start: 2;
}
&.first-day-2 {
grid-column-start: 3;
}
&.first-day-3 {
grid-column-start: 4;
}
&.first-day-4 {
grid-column-start: 5;
}
&.first-day-5 {
grid-column-start: 6;
}
&.first-day-6 {
grid-column-start: 7;
}
}
&:focus + label,
&:checked:focus + label {
box-shadow: 0 0 0 3px #fff, 0 0 0 5.5px #1574a1;
background-color: $blue;
color: $white;
&.past-day {
box-shadow: none;
background-color: transparent;
color: $gray-500;
font-weight: normal;
.month-year {
grid-area: 1 / 1 / 2 / 5;
text-transform: uppercase;
font-weight: 300;
letter-spacing: 0.75px;
}
.legend {
grid-area: 1 / 5 / 2 / 8;
.legend-circle {
border-radius: 50%;
width: 16px;
height: 16px;
background-color: $blue-100;
border: 1px solid $blue;
}
}
.nav-back,
.nav-forward {
display: none;
}
.radio-wrapper {
position: relative;
display: flex;
justify-content: center;
align-items: center;
outline: none;
input[type="radio"] {
position: absolute; //override bootstrap
height: 0;
opacity: 0;
&:focus-visible + label {
box-shadow: 0 0 0 2.5px $blue;
}
&.current-day {
&:focus + label,
&:checked:focus + label {
box-shadow: 0 0 0 3px #fff, 0 0 0 5.5px #1574a1;
background-color: $blue;
color: $white;
&.past-day {
box-shadow: none;
background-color: transparent;
color: $gray-500;
font-weight: normal;
}
&.current-day {
&:after {
background-color: $white;
}
.first-day {
color: $white;
}
}
}
&:checked + label {
color: $white;
background: $blue;
box-shadow: 0 0 0 3px $white, 0 0 0 5.5px $blue;
&:after {
background-color: $white;
}
@ -466,166 +480,158 @@ export default {
}
}
&:checked + label {
color: $white;
background: $blue;
box-shadow: 0 0 0 3px $white, 0 0 0 5.5px $blue;
&:after {
background-color: $white;
}
.first-day {
color: $white;
}
}
}
.first-day {
position: absolute;
font-size: 9px;
font-weight: 500;
color: $gray-600;
top: 0;
z-index: 1;
}
label {
position: relative;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
width: 40px;
height: 40px;
min-width: 40px;
border-radius: 50%;
span {
&.small {
font-size: 0.75rem;
color: $gray-550;
}
.first-day {
position: absolute;
font-size: 9px;
font-weight: 500;
color: $gray-600;
top: 0;
z-index: 1;
}
&:hover,
&:checked {
@include media-breakpoint-up(sm) {
box-shadow: 0 0 0 4px transparent;
background-color: $blue;
color: $white;
&:after {
background-color: $white;
}
}
cursor: pointer;
&.current-day {
+ .first-day {
color: $white;
}
}
.first-day {
color: $white;
}
}
+ p {
display: none;
}
}
&.selectable-day {
label {
color: $blue;
background-color: $blue-100;
border: 1px solid $blue;
}
}
&.past-day,
&.future-day,
&.unavailable-day {
label {
color: $gray-500;
background-color: transparent;
border: 1px solid transparent;
pointer-events: none;
}
}
&.current-day {
label {
&:after {
content: "";
width: 0.25rem;
height: 0.25rem;
border-radius: 50%;
background-color: $blue;
position: absolute;
top: 28px;
}
.first-day {
color: $blue;
}
}
}
}
&.initial-view {
&.month-hidden {
display: none;
}
.day-hidden {
display: none;
}
}
}
.past {
.calendar-grid-container {
.month-year {
grid-area: 1 / 1 / 2 / 6;
}
.nav-forward {
grid-area: 1 / 7 / 2 / 8;
display: flex;
justify-content: flex-end;
button {
&:after {
content: "";
position: absolute;
width: 7px;
height: 12px;
background-image: url("data:image/svg+xml,%3Csvg width='7' height='12' viewBox='0 0 7 12' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M6.66831 5.99879C6.66955 6.17554 6.60074 6.34558 6.47695 6.47168L1.15501 11.8017C1.02812 11.9287 0.85603 12 0.676587 12C0.497145 12 0.325053 11.9287 0.198168 11.8017C0.0712831 11.6748 7.268e-09 11.5026 8.07183e-09 11.3231C8.87567e-09 11.1436 0.0712831 10.9714 0.198168 10.8445L5.05126 5.99879L0.198168 1.14736C0.0725521 1.02042 0.00248585 0.848755 0.00338306 0.670131C0.00428028 0.491506 0.0760674 0.320554 0.202952 0.194881C0.329837 0.0692091 0.501426 -0.000888818 0.679971 9.54485e-06C0.858515 0.000906955 1.02939 0.0727263 1.15501 0.199668L6.47312 5.52399C6.59843 5.65017 6.66862 5.82092 6.66831 5.99879Z' fill='%231574A1'/%3E%3C/svg%3E%0A");
}
}
}
.nav-back {
grid-area: 1 / 6 / 2 / 7;
display: flex;
justify-content: flex-end;
button {
&:after {
content: "";
position: absolute;
width: 7px;
height: 12px;
background-image: url("data:image/svg+xml,%3Csvg width='7' height='12' viewBox='0 0 7 12' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0.331685 6.00121C0.330445 5.82446 0.399256 5.65442 0.523053 5.52832L5.84499 0.198256C5.97188 0.0713149 6.14397 -8.63821e-08 6.32341 -6.72174e-08C6.50285 -4.80527e-08 6.67495 0.071315 6.80183 0.198256C6.92872 0.325198 7 0.497368 7 0.67689C7 0.856412 6.92872 1.02858 6.80183 1.15552L1.94874 6.00121L6.80183 10.8526C6.92745 10.9796 6.99751 11.1512 6.99662 11.3299C6.99572 11.5085 6.92393 11.6794 6.79705 11.8051C6.67016 11.9308 6.49857 12.0009 6.32003 12C6.14148 11.9991 5.97061 11.9273 5.84499 11.8003L0.526881 6.47601C0.401568 6.34983 0.331375 6.17908 0.331685 6.00121Z' fill='%231574A1'/%3E%3C/svg%3E%0A");
}
}
}
.nav-back,
.nav-forward {
button {
position: relative;
border-radius: 50%;
width: 28px;
height: 28px;
background-color: $blue-100;
border: 1px solid $blue;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
&:focus,
&:focus-visible {
border: 2.5px solid $blue;
box-shadow: none;
width: 40px;
height: 40px;
min-width: 40px;
border-radius: 50%;
span {
&.small {
font-size: 0.75rem;
color: $gray-550;
}
}
&:hover,
&:checked {
@include media-breakpoint-up(sm) {
box-shadow: 0 0 0 4px transparent;
background-color: $blue;
color: $white;
&:after {
background-color: $white;
}
}
cursor: pointer;
&.current-day {
+ .first-day {
color: $white;
}
}
.first-day {
color: $white;
}
}
+ p {
display: none;
}
}
&.selectable-day {
label {
color: $blue;
background-color: $blue-100;
color: $white;
border: 1px solid $blue;
}
}
&.past-day,
&.future-day,
&.unavailable-day {
label {
color: $gray-500;
background-color: transparent;
border: 1px solid transparent;
pointer-events: none;
}
}
&.current-day {
label {
&:after {
content: "";
width: 0.25rem;
height: 0.25rem;
border-radius: 50%;
background-color: $blue;
position: absolute;
top: 28px;
}
.first-day {
color: $blue;
}
}
}
}
&.initial-view {
&.month-hidden {
display: none;
}
.day-hidden {
display: none;
}
}
}
.btn-link {
font-weight: 500;
text-underline-offset: 4px;
}
.past {
.calendar-grid-container {
.month-year {
grid-area: 1 / 1 / 2 / 6;
}
.nav-forward {
grid-area: 1 / 7 / 2 / 8;
display: flex;
justify-content: flex-end;
button {
&:after {
content: "";
position: absolute;
width: 7px;
height: 12px;
background-image: url("data:image/svg+xml,%3Csvg width='7' height='12' viewBox='0 0 7 12' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M6.66831 5.99879C6.66955 6.17554 6.60074 6.34558 6.47695 6.47168L1.15501 11.8017C1.02812 11.9287 0.85603 12 0.676587 12C0.497145 12 0.325053 11.9287 0.198168 11.8017C0.0712831 11.6748 7.268e-09 11.5026 8.07183e-09 11.3231C8.87567e-09 11.1436 0.0712831 10.9714 0.198168 10.8445L5.05126 5.99879L0.198168 1.14736C0.0725521 1.02042 0.00248585 0.848755 0.00338306 0.670131C0.00428028 0.491506 0.0760674 0.320554 0.202952 0.194881C0.329837 0.0692091 0.501426 -0.000888818 0.679971 9.54485e-06C0.858515 0.000906955 1.02939 0.0727263 1.15501 0.199668L6.47312 5.52399C6.59843 5.65017 6.66862 5.82092 6.66831 5.99879Z' fill='%231574A1'/%3E%3C/svg%3E%0A");
}
}
}
.nav-back {
grid-area: 1 / 6 / 2 / 7;
display: flex;
justify-content: flex-end;
button {
&:after {
content: "";
position: absolute;
width: 7px;
height: 12px;
background-image: url("data:image/svg+xml,%3Csvg width='7' height='12' viewBox='0 0 7 12' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0.331685 6.00121C0.330445 5.82446 0.399256 5.65442 0.523053 5.52832L5.84499 0.198256C5.97188 0.0713149 6.14397 -8.63821e-08 6.32341 -6.72174e-08C6.50285 -4.80527e-08 6.67495 0.071315 6.80183 0.198256C6.92872 0.325198 7 0.497368 7 0.67689C7 0.856412 6.92872 1.02858 6.80183 1.15552L1.94874 6.00121L6.80183 10.8526C6.92745 10.9796 6.99751 11.1512 6.99662 11.3299C6.99572 11.5085 6.92393 11.6794 6.79705 11.8051C6.67016 11.9308 6.49857 12.0009 6.32003 12C6.14148 11.9991 5.97061 11.9273 5.84499 11.8003L0.526881 6.47601C0.401568 6.34983 0.331375 6.17908 0.331685 6.00121Z' fill='%231574A1'/%3E%3C/svg%3E%0A");
}
}
}
.nav-back,
.nav-forward {
button {
position: relative;
border-radius: 50%;
width: 28px;
height: 28px;
background-color: $blue-100;
border: 1px solid $blue;
display: flex;
justify-content: center;
align-items: center;
&:focus,
&:focus-visible {
border: 2.5px solid $blue;
box-shadow: none;
background-color: $blue-100;
color: $white;
}
}
}
}

View file

@ -0,0 +1,136 @@
<template>
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }">
<div class="page-container-grouped-styles">
<loadingModal ref="loadingModal" />
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" class="mt-5" />
<div class="text-center mt-1 mb-3" v-if="ChangeShopLink.length">
<span v-for="copy in ChangeShopLink" :key="copy">
<span v-if="doesCopyContainRouterLink(copy)" class="text-body">
<router-link
:to="{
query: { fmgPage: `${getRouterLinkRouteFromCopy(copy)}` },
name: 'root',
}"
>{{ getRouterLinkDisplayTextFromCopy(copy) }}</router-link
>
</span>
<span v-else class="m-0 text-body" v-html="copy"></span>
</span>
</div>
<date-picker
selectableDates="custom"
v-model="selectedDate"
:customSelectableDatesCallback="getAvailableDates" />
<funnel-footer
cmsWidgetName="FunnelFooterWidget"
ref="funnelFooter"
:isForwardActionDisabled="!meta.valid"
@back-clicked="backButtonAction"
@ForwardClicked="forwardButtonAction" />
</div>
</Form>
</template>
<script>
// Components
import funnelHeader from "@/fmg-components/funnel-header/funnel-header";
import funnelFooter from "@/fmg-components/funnel-footer/funnel-footer";
import funnelSubHeader from "@/fmg-components/funnel-sub-header/funnel-sub-header";
import loadingModal from "@/fmg-components/loading-modal/loading-modal.vue";
import { Form, defineRule } from "vee-validate";
import datePicker from "@/digital-components/date-picker/date-picker";
// Supporting files
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
import { settleAllPromises } from "@/helpers/layout-helper";
import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
import {
doesCopyContainRouterLink,
splitCopyOnCMSPlaceHolder,
getRouterLinkRouteFromCopy,
getRouterLinkDisplayTextFromCopy,
} from "@/helpers/cms-content-helper";
import { errorMessages } from "@/constants/error-messages";
import { required } from "@/helpers/validation-rules";
defineRule("date-required", required(errorMessages.DATE_REQUIRED));
export default {
name: "schedule",
data() {
return {
selectedDate: null,
mockSelectableDatesData: [
{ year: 2023, month: 4, date: 13 },
{ year: 2023, month: 4, date: 14 },
{ year: 2023, month: 4, date: 26 },
{ year: 2023, month: 5, date: 4 },
{ year: 2023, month: 5, date: 5 },
{ year: 2023, month: 5, date: 14 },
{ year: 2023, month: 5, date: 21 },
{ year: 2023, month: 5, date: 23 },
{ year: 2023, month: 5, date: 25 },
{ year: 2023, month: 6, date: 11 },
],
};
},
async beforeRouteEnter(to, from, next) {
// Call APIs
const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage);
// Settle promises and get results
const promiseResultMap = [
{
resultKey: "cmsContent",
promise: cmsContentPromise,
},
];
const resultMap = await settleAllPromises(promiseResultMap);
// Call the "next" function to complete the transition to this page.
next((vm) => {
vm.setCmsContent(resultMap.cmsContent);
});
},
computed: {
ChangeShopLinkText() {
return this.getCmsContent("ChangeShopLink", "Text");
},
ChangeShopLink() {
// 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);
},
},
methods: {
doesCopyContainRouterLink,
splitCopyOnCMSPlaceHolder,
getRouterLinkRouteFromCopy,
getRouterLinkDisplayTextFromCopy,
arePagePrerequisitesValid() {
return true;
// NEED TODO - WHAT ARE PAGE REQ'S FOR THIS PAGE?
},
getAvailableDates(startDate, endDate) {
return this.mockSelectableDatesData;
},
backButtonAction() {
this.$router.navigateWithoutSaving(this.navigationScenarios.CLICKED_BACK, this.$route);
},
forwardButtonAction() {
navigateToHeritageFunnel({ loadingModal: this.$refs.loadingModal });
},
},
components: {
funnelHeader,
funnelFooter,
funnelSubHeader,
Form,
loadingModal,
datePicker,
},
};
</script>
<style lang="scss"></style>

View file

@ -292,7 +292,10 @@ export default {
this.$router.navigateWithoutSaving(this.navigationScenarios.CLICKED_BACK, this.$route);
},
forwardButtonAction() {
navigateToHeritageFunnel({ loadingModal: this.$refs.loadingModal });
this.$router.navigateWithoutSaving(
this.navigationScenarios.SELECTED_LOCATION,
this.$route
);
},
},
components: {

View file

@ -29,7 +29,6 @@ import { experimentTriggers } from "../constants/experiments";
import { applicationConfig } from "../constants/application-config";
// Components
import quote from "@/layouts/quote/quote.vue";
import datePicker from "@/digital-components/date-picker/date-picker.vue";
import demoDatePicker from "@/layouts/demo-date-picker/demo-date-picker.vue";

View file

@ -16,6 +16,7 @@ const fmgPageValues = {
QUOTE: "quote",
SERVICE_LOCATION: "service-location",
HERITAGE: "heritage",
SCHEDULE: "schedule",
};
export { fmgPageValues };

View file

@ -45,6 +45,9 @@ const navigationScenarios = {
CLICKED_BACK_WITH_CAPABILITY_QUESTIONS: "CLICKED_BACK_WITH_CAPABILITY_QUESTIONS",
CLICKED_BACK_WITH_VIN_AND_NO_MORE_QUESTIONS: "CLICKED_BACK_WITH_VIN_AND_NO_MORE_QUESTIONS",
CLICKED_BACK_WITH_NO_VIN_NOR_QUESTIONS: "CLICKED_BACK_WITH_NO_VIN_NOR_QUESTIONS",
// Scheduling
SELECTED_LOCATION: "SELECTED_LOCATION",
};
export { navigationScenarios };

View file

@ -420,6 +420,19 @@ const routingTable = function (store) {
scenario: navigationScenarios.CLICKED_BACK,
destinationFmgPageValue: fmgPageValues.QUOTE,
},
{
scenario: navigationScenarios.SELECTED_LOCATION,
destinationFmgPageValue: fmgPageValues.SCHEDULE,
},
],
},
{
fmgPageValue: fmgPageValues.SCHEDULE,
maps: [
{
scenario: navigationScenarios.CLICKED_BACK,
destinationFmgPageValue: fmgPageValues.SERVICE_LOCATION,
},
],
},
];