diff --git a/src/digital-components/date-picker/date-picker.vue b/src/digital-components/date-picker/date-picker.vue index 9154383cd..bd9ae6d9a 100644 --- a/src/digital-components/date-picker/date-picker.vue +++ b/src/digital-components/date-picker/date-picker.vue @@ -22,7 +22,6 @@
-
SundayS
MondayM
TuesdayT
@@ -84,7 +83,6 @@ export default { months: null, disableViewMoreDatesButton: false, selectableDatesData: [], // NOTE: uses monthNum (1-based), NOT monthIndex (0-based) - today: null, hideSomeDaysForInitialView: null, }; }, @@ -112,6 +110,12 @@ export default { }, }, computed: { + today() { + if (this.todayOverrideDateString) { + return new Date(this.todayOverrideDateString); + } + return new Date(); + }, todayMonthIndex() { return this.today.getMonth() + 1; }, @@ -151,39 +155,31 @@ export default { this.$emit("date-clicked"); }, getWeekStartDate(date) { - // Get the day of the week for date - let dayOfWeek = date.getDay(); - + const dayOfWeek = date.getDay(); // Subtract the day of the week from date to get the date of Sunday - let sunday = new Date(date); + const sunday = new Date(date); sunday.setDate(sunday.getDate() - dayOfWeek); - - // Return the date of Sunday return sunday; }, getWeekEndDate(date) { - const currentDay = date.getDay(); // Get the day of the week (0 = Sunday, 1 = Monday, etc.) - const daysUntilSaturday = 6 - currentDay; // Calculate the number of days until Saturday - + const dayOfWeek = date.getDay(); + const daysUntilSaturday = 6 - dayOfWeek; // Calculate the number of days until Saturday // Clone the given date and add the remaining days until Saturday const saturday = new Date(date); saturday.setDate(date.getDate() + daysUntilSaturday); - return saturday; }, getNextWeekSunday(date) { - const currentDay = date.getDay(); // Get the day of the week (0 = Sunday, 1 = Monday, etc.) - const daysUntilNextSunday = currentDay === 0 ? 7 : 7 - currentDay; // Calculate the number of days until the next Sunday - + const dayOfWeek = date.getDay(); + const daysUntilNextSunday = 7 - dayOfWeek; // Calculate the number of days until the next Sunday // Clone the given date and add the remaining days until Sunday const nextSunday = new Date(date); nextSunday.setDate(date.getDate() + daysUntilNextSunday); - return nextSunday; }, getInitialViewWeeks(today, initialViewRowsToShow) { - // TODO: this only is for future direction; create logic for past direction - let weeks = []; + // TODO: this only is for future direction; need to create logic for past direction + const weeks = []; let weekStartDate = this.getWeekStartDate(today); let weekEndDate = this.getWeekEndDate(today); for (let i = 0; i < initialViewRowsToShow; i++) { @@ -200,14 +196,19 @@ export default { return weeks; }, async loadInitialData(config) { - // CALLED FROM CONSUMING COMPONENT BEFORE DATE-PICKER APPEARS - const todayDate = config.todayOverrideDateString - ? new Date(config.todayOverrideDateString) - : new Date(); + let todayDate; + if (this.today) { + todayDate = this.today; + } else if (config.todayOverrideDateString) { + todayDate = new Date(config.todayOverrideDateString); + } else { + todayDate = new Date(); + } - let todayMonthIndex = todayDate.getMonth() + 1; - let todayYearNum = todayDate.getFullYear(); - let currentMonthStart = new Date(todayYearNum, todayMonthIndex - 1, 1); + const todayMonthIndex = todayDate.getMonth() + 1; + const todayYearNum = todayDate.getFullYear(); + // TODO - set up currentMonthStart if direction is PAST: + // let currentMonthStart = new Date(todayYearNum, todayMonthIndex - 1, 1); let currentMonthEnd = new Date(todayYearNum, todayMonthIndex, 0); let calendarViewDirection = "none"; @@ -219,10 +220,10 @@ export default { config.initialViewRowsToShow ); - let initialViewStartDate = todayDate; - let initialViewEndDate = initialViewWeeks[initialViewWeeks.length - 1].weekEndDate; - let saturday1month = initialViewWeeks[0].weekEndDate.getMonth(); - let sunday5month = + const initialViewStartDate = todayDate; + const initialViewEndDate = initialViewWeeks[initialViewWeeks.length - 1].weekEndDate; + const saturday1month = initialViewWeeks[0].weekEndDate.getMonth(); + const sunday5month = initialViewWeeks[initialViewWeeks.length - 1].weekStartDate.getMonth(); let hideSomeDaysForInitialView = false; @@ -284,9 +285,9 @@ export default { // Growing from 0 to 1 time = Math.min(1, (timestamp - start) / duration); - let percentageNew = timingFunc(time); - let distanceToGo = targetY; - let thisDistance = percentageNew * distanceToGo; + const percentageNew = timingFunc(time); + const distanceToGo = targetY; + const thisDistance = percentageNew * distanceToGo; wrapper.scrollTo(0, initY + thisDistance); @@ -305,13 +306,12 @@ export default { }, async setCalendarData(config = {}) { - this.today = config.todayDate; this.hideSomeDaysForInitialView = config.hideSomeDaysForInitialView; - let hideSecondMonth = config.hideSecondMonth; + const hideSecondMonth = config.hideSecondMonth; const direction = config.calendarViewDirection; - const monthsAfterToLoadOffset = 12; // TO BE MADE "CONSTANTS" - const monthsBeforeToLoadOffset = 36; // TO BE MADE "CONSTANTS" + const monthsAfterToLoadOffset = 12; + const monthsBeforeToLoadOffset = 36; config.initialShopTimeSlotsResponse.days.forEach((selectableDate) => { this.selectableDatesData.push(selectableDate); }); @@ -382,25 +382,25 @@ export default { } } - const monthEndDate = new Date(yearNum, monthIndex, 0); // BOTH - let monthEndDateNum = monthEndDate.getDate(); // BOTH + const monthEndDate = new Date(yearNum, monthIndex, 0); + let monthEndDateNum = monthEndDate.getDate(); if ( offset === 0 && calendarViewDirection === "past" && monthEndDateNum > this.currentWeekEndDateNum ) { - monthEndDateNum = this.currentWeekEndDateNum; // PAST + monthEndDateNum = this.currentWeekEndDateNum; } const monthStartDateNum = offset === 0 && calendarViewDirection === "future" ? this.currentWeekStartDateNum - : 1; // FUTURE - const monthStartDate = new Date(yearNum, monthIndex - 1, monthStartDateNum); // BOTH + : 1; + const monthStartDate = new Date(yearNum, monthIndex - 1, monthStartDateNum); - const startDateDayIndex = monthStartDate.getDay(); // FUTURE - const endDateDayIndex = monthEndDate.getDay(); // PAST + const startDateDayIndex = monthStartDate.getDay(); + const endDateDayIndex = monthEndDate.getDay(); if (Math.abs(offset) === 1 && hideSecondMonth) { monthClass = monthClass + " month-hidden"; @@ -424,7 +424,7 @@ export default { // populate dates array for (let i = monthStartDateNum; i <= monthEndDateNum; i++) { let dayClasses = ""; - let dateString = + const dateString = yearNum.toString() + "-" + forceTwoDigitString(monthIndex) + @@ -514,7 +514,7 @@ export default { monthToShow.dates[monthToShow.dates.length - 1].inputValue.dateString ); this.isLoading = false; - this.hideSomeDaysForInitialView = false; // if hid days on initial partial view, this removes hidden styling on days + this.hideSomeDaysForInitialView = false; // if hid days on initial partial view, this will reveal those days monthToShow.monthClass = monthToShow.monthClass.replace(" month-hidden", ""); this.scrollToElement(monthToShow.monthString); diff --git a/src/digital-components/text-block/text-block.vue b/src/digital-components/text-block/text-block.vue index 3a608f3a0..2deb126e5 100644 --- a/src/digital-components/text-block/text-block.vue +++ b/src/digital-components/text-block/text-block.vue @@ -1,7 +1,7 @@ @@ -10,12 +10,7 @@ export default { name: "textBlock", props: { customText: String, // used to allow the insert of token values into textblock - justifyText: String, // left, right, center - margin: { - // bootstrap margin to apply to the block. - type: String, - default: "mt-2", - }, + justifyText: String, // right, center (left is default) typeStyle: String, // h1-h6, body, small, label, caption (see Figma or Confluence documentation) fontWeight: String, // bold=500, default is 400 cmsWidgetName: String, @@ -33,15 +28,11 @@ export default { diff --git a/src/layouts/quote/cash-or-insurance-question/cash-or-insurance-question.vue b/src/layouts/quote/cash-or-insurance-question/cash-or-insurance-question.vue index 45fef683a..a2afb1a7c 100644 --- a/src/layouts/quote/cash-or-insurance-question/cash-or-insurance-question.vue +++ b/src/layouts/quote/cash-or-insurance-question/cash-or-insurance-question.vue @@ -5,7 +5,7 @@ :groupName="groupName" buttonTypeString="listButtonHorizontal" v-model="selectedValues" - :additionalButtonData="{ additionalButtonStyling: 'listButtonHorizontalStrong' }" + :additionalButtonData="additionalButtonData" isRequired /> @@ -27,6 +27,11 @@ export default { answersFromCms() { return this.getCmsContent(this.cmsWidgetName, "Answers"); }, + additionalButtonData() { + return { + additionalButtonStyling: "listButtonHorizontalStrong", + }; + }, selectedValues: { get: function () { // Convert to CMS answer name from bool diff --git a/src/layouts/review/review.spec.js b/src/layouts/review/review.spec.js deleted file mode 100644 index 674dc71f7..000000000 --- a/src/layouts/review/review.spec.js +++ /dev/null @@ -1,3 +0,0 @@ -describe("Review Page", () => { - test.todo("Add more tests as specific functionality is added."); -}); diff --git a/src/layouts/review/review.vue b/src/layouts/review/review.vue deleted file mode 100644 index 9bccb2014..000000000 --- a/src/layouts/review/review.vue +++ /dev/null @@ -1,102 +0,0 @@ - - - - - diff --git a/src/layouts/schedule/constants/schedule-constants.js b/src/layouts/schedule/constants/schedule-constants.js new file mode 100644 index 000000000..89b4a4c1a --- /dev/null +++ b/src/layouts/schedule/constants/schedule-constants.js @@ -0,0 +1,11 @@ +const AppointmentTypeStrings = { + IN_SHOP: "Inshop", + MOBILE: "Mobile", + DROP_OFF: "Dropoff", +}; + +const PREMIUM_TIME_SLOT_ID_FLAG = "-premium"; + +const PREMIUM_FEE_PART_TYPE = "EARLY BIRD"; + +export { AppointmentTypeStrings, PREMIUM_TIME_SLOT_ID_FLAG, PREMIUM_FEE_PART_TYPE }; diff --git a/src/layouts/schedule/schedule.vue b/src/layouts/schedule/schedule.vue index d5e3b4c28..654f35a15 100644 --- a/src/layouts/schedule/schedule.vue +++ b/src/layouts/schedule/schedule.vue @@ -25,17 +25,17 @@ v-model="selectedDate" :customSelectableDatesCallback="getAvailableDatesMethod" @date-clicked="openInshopTimeSlotsModal" /> - @@ -49,15 +49,17 @@ import { defineRule, useField } from "vee-validate"; import { errorMessages } from "@/constants/error-messages"; import { required } from "@/helpers/validation-rules"; +// Constants +import { + AppointmentTypeStrings, + PREMIUM_TIME_SLOT_ID_FLAG, + PREMIUM_FEE_PART_TYPE, +} from "../constants/schedule-constants"; + // Validation for the modal button defineRule("time-slot-required", required(errorMessages.OPTION_REQUIRED)); // Constants -const AppointmentTypeStrings = { - IN_SHOP: "Inshop", - MOBILE: "Mobile", - DROP_OFF: "Dropoff", -}; export default { name: "timeSlotModalQuestion", @@ -65,18 +67,20 @@ export default { modelValue: Object, cmsWidgetName: String, mobileCmsWidgetName: String, - earlyBirdCmsWidgetName: String, + mobilePremiumCmsWidgetName: String, dropoffCmsWidgetName: String, + sameDayDropOffCmsWidgetName: String, appointmentType: String, dateAndTimeSlotData: Object, - mobileEarlyBirdFee: Object, + premiumAppointmentFee: Object, estimatedServiceMinutesMinimum: Number, estimatedServiceMinutesMaximum: Number, validationRules: String, }, data() { return { - selectedTimeSlot: null, + selectedTimeSlotId: null, + isSelectedAppointmentPremium: null, timeslotModalListButton: timeslotModalListButton, }; }, @@ -89,42 +93,38 @@ export default { }, watch: { modelValue() { - this.selectedTimeSlot = this.modelValue; // Run component validation that is used at parent level - this.handleChange(this.modelValue); + this.handleChange(this.modelValue.id); + }, + dateAndTimeSlotData(newValue, oldValue) { + const numberOfOptions = newValue?.timeSlots.length; + if (numberOfOptions === 1) { + this.selectedTimeSlotId = newValue.timeSlots[0].id; + } }, - // dateAndTimeSlotData(newValue, oldValue) { - // const numberOfOptions = newValue?.timeSlots.length; - // console.log('running'); - // if (numberOfOptions === 1) { - // this.selectedTimeSlot = newValue.timeSlots[0].id; - // } - // } }, computed: { supplementalInformationBlock() { let appointmentTypeCmsWidgetName; - let cmsFieldName = "BodyText"; if (this.appointmentType === AppointmentTypeStrings.IN_SHOP) { return null; } else if (this.appointmentType === AppointmentTypeStrings.MOBILE) { appointmentTypeCmsWidgetName = - this.selectedTimeSlot === this.earlyBirdButtonText - ? this.earlyBirdCmsWidgetName + this.selectedTimeSlotId === this.premiumAppointmentButtonText + ? this.mobilePremiumCmsWidgetName : this.mobileCmsWidgetName; } else { - appointmentTypeCmsWidgetName = this.dropoffCmsWidgetName; - if (this.isSameDay) { - cmsFieldName = "BodyText2"; - } + appointmentTypeCmsWidgetName = this.isSameDay + ? this.sameDayDropOffCmsWidgetName + : this.dropoffCmsWidgetName; } - return this.getCmsContent(appointmentTypeCmsWidgetName, cmsFieldName); + return this.getCmsContent(appointmentTypeCmsWidgetName, "BodyText"); }, footerCloseButtonText() { return this.getCmsContent(this.cmsWidgetName, "FooterText"); }, - earlyBirdButtonText() { - return this.getCmsContent(this.earlyBirdCmsWidgetName, "HeaderText"); + premiumAppointmentButtonText() { + return this.getCmsContent(this.mobilePremiumCmsWidgetName, "HeaderText"); }, dropoffButtonText() { return this.getCmsContent(this.dropoffCmsWidgetName, "HeaderText"); @@ -203,13 +203,20 @@ export default { } if (this.appointmentType === AppointmentTypeStrings.MOBILE) { - const offerPremium = this.dateAndTimeSlotData.timeSlots[0].offerPremium; - const hasEarlyBird = this.mobileEarlyBirdFee?.partType === "EARLY BIRD"; - if (offerPremium && hasEarlyBird) { + const isPremiumTimeSlot = this.dateAndTimeSlotData.timeSlots[0].offerPremium; + const hasPremiumPartAvailable = + this.premiumAppointmentFee?.partType === PREMIUM_FEE_PART_TYPE; + if (isPremiumTimeSlot && hasPremiumPartAvailable) { + const formattedPrice = + "+$" + this.getTotalLineItemPrice(this.premiumAppointmentFee).toFixed(2); availableTimeSlots.unshift({ - value: this.dateAndTimeSlotData.timeSlots[0].id + "-earlybird", - buttonLabel: this.earlyBirdButtonText, - buttonLabelSubCopy: this.getTotalLineItemPrice(this.mobileEarlyBirdFee), + // Unique value is required for each and the premium appoinment shares a timeslot ID + value: this.addPremiumFlagToInput(this.dateAndTimeSlotData.timeSlots[0].id), + buttonLabel: this.premiumAppointmentButtonText, + buttonLabelSubCopy: formattedPrice, + additionalButtonData: { + isPremiumAppointment: true, + }, }); } } @@ -222,12 +229,27 @@ export default { }, // fires any time the footer button is used, is fired before "onModalClosed" closeModal() { - this.$emit("update:modelValue", this.selectedTimeSlot); + if (this.selectedTimeSlotId.toString().includes(PREMIUM_TIME_SLOT_ID_FLAG)) { + this.selectedTimeSlotId = this.removePremiumFlagFromInput(this.selectedTimeSlotId); + this.isSelectedAppointmentPremium = true; + } else { + this.isSelectedAppointmentPremium = false; + } + const selectedTimeSlotData = { + id: this.selectedTimeSlotId, + isPremiumAppointment: this.isSelectedAppointmentPremium, + }; + this.$emit("update:modelValue", selectedTimeSlotData); this.$refs["timeSlots"].closeModal(); }, // fires any time the modal is closed, AFTER "closeModal" fires if footer button is used onModalClosed() { - this.selectedTimeSlot = this.modelValue; + this.isSelectedAppointmentPremium = this.modelValue.isPremiumAppointment; + if (this.isSelectedAppointmentPremium) { + this.selectedTimeSlotId = this.addPremiumFlagToInput(this.modelValue.id); + } else { + this.selectedTimeSlotId = this.modelValue.id; + } this.$emit("time-slot-modal-closed"); }, // Expected input: "HH:MM:SS" @@ -252,6 +274,12 @@ export default { } return displayTextForDurationLength; }, + addPremiumFlagToInput(timeSlotId) { + return (timeSlotId += PREMIUM_TIME_SLOT_ID_FLAG); + }, + removePremiumFlagFromInput(timeSlotId) { + return parseInt(timeSlotId.trim(PREMIUM_TIME_SLOT_ID_FLAG.length)); + }, }, components: { modal, diff --git a/src/layouts/schedule/time-slot-modal-question/timeslot-modal-list-button/timeslot-modal-list-button.vue b/src/layouts/schedule/time-slot-modal-question/timeslot-modal-list-button/timeslot-modal-list-button.vue index 60b2f9e81..4f5b17461 100644 --- a/src/layouts/schedule/time-slot-modal-question/timeslot-modal-list-button/timeslot-modal-list-button.vue +++ b/src/layouts/schedule/time-slot-modal-question/timeslot-modal-list-button/timeslot-modal-list-button.vue @@ -6,12 +6,16 @@
- + {{ buttonLabel }} + + {{ formattedButtonLabelSubCopy }} + - - {{ formattedButtonLabelSubCopy }} - + {{ screenReaderOnlyText }} @@ -28,7 +32,7 @@ import baseInputButton from "@/digital-components/base-input-button/base-input-b import inputButtonWrapperMixin from "@/mixins/input-button-wrapper-mixin"; export default { - name: "timeslotMOdalListButton", + name: "timeslotModalListButton", mixins: [inputButtonWrapperMixin], props: { loaderColor: String, @@ -44,7 +48,7 @@ export default { }, computed: { formattedButtonLabelSubCopy() { - return this.buttonLabelSubCopy?.toFixed(2); + return this.buttonLabelSubCopy; }, }, methods: { @@ -85,6 +89,9 @@ export default { font-weight: 500; background: $blue-100; box-shadow: 0 0 0 1px $blue; + span.premium-appointment-price { + background: $green-200; + } } &:checked:focus + .list-button-content { box-shadow: 0 0 0 2.5px $blue; @@ -109,11 +116,20 @@ export default { width: 100%; outline: none; - span { - &.small { - font-size: 0.75rem; - color: $gray-550; - } + span.premium-appointment-price { + position: absolute; + background: $green-100; + border-radius: 4.5rem; + line-height: 1.25rem; + color: $green-700; + font-size: 0.75rem; + margin-left: 4px; + padding: 2px 8px; + font-weight: 500; } } + +.position-relative { + position: relative; +} diff --git a/src/layouts/service-location/shop-question/shop-question.spec.js b/src/layouts/service-location/shop-question/shop-question.spec.js index c61fe2720..c4efdea91 100644 --- a/src/layouts/service-location/shop-question/shop-question.spec.js +++ b/src/layouts/service-location/shop-question/shop-question.spec.js @@ -179,19 +179,19 @@ describe("shop-question.vue", () => { expect(wrapper.vm.answers).toEqual([ { buttonBodyCopy: "4403 Executive Pkwy, Westerville, OH 43081", - buttonLabel: "4403 Executive Pkwy", + buttonLabel: "Westerville", buttonLabelSubCopy: "5 mi", value: "003335", }, { buttonBodyCopy: "760 Dearborn Park Ln, Worthington, OH 43085", - buttonLabel: "760 Dearborn Park Ln", + buttonLabel: "Worthington", buttonLabelSubCopy: "10.5 mi", value: "001820", }, { buttonBodyCopy: "5015 N High St, Columbus, OH 43214", - buttonLabel: "5015 N High St", + buttonLabel: "Columbus", buttonLabelSubCopy: "11.5 mi", value: "003343", }, @@ -320,19 +320,19 @@ describe("shop-question.vue", () => { const displayedAnswers = [ { buttonBodyCopy: "4403 Executive Pkwy, Westerville, OH 43081", - buttonLabel: "4403 Executive Pkwy", + buttonLabel: "Westerville", buttonLabelSubCopy: "5 mi", value: "003335", }, { buttonBodyCopy: "760 Dearborn Park Ln, Worthington, OH 43085", - buttonLabel: "760 Dearborn Park Ln", + buttonLabel: "Worthington", buttonLabelSubCopy: "10.5 mi", value: "001820", }, { buttonBodyCopy: "5015 N High St, Columbus, OH 43214", - buttonLabel: "5015 N High St", + buttonLabel: "Columbus", buttonLabelSubCopy: "11.5 mi", value: "003343", }, @@ -388,37 +388,37 @@ describe("shop-question.vue", () => { expect(wrapper.vm.answers).toEqual([ { buttonBodyCopy: "4403 Executive Pkwy, Westerville, OH 43081", - buttonLabel: "4403 Executive Pkwy", + buttonLabel: "Westerville", buttonLabelSubCopy: "5 mi", value: "003335", }, { buttonBodyCopy: "760 Dearborn Park Ln, Worthington, OH 43085", - buttonLabel: "760 Dearborn Park Ln", + buttonLabel: "Worthington", buttonLabelSubCopy: "10.5 mi", value: "001820", }, { buttonBodyCopy: "5015 N High St, Columbus, OH 43214", - buttonLabel: "5015 N High St", + buttonLabel: "Columbus", buttonLabelSubCopy: "11.5 mi", value: "003343", }, { buttonBodyCopy: "1670 Harmon Ave, Columbus, OH 43223", - buttonLabel: "1670 Harmon Ave", + buttonLabel: "Columbus", buttonLabelSubCopy: "16 mi", value: "006747", }, { buttonBodyCopy: "3938 Powell Rd, Powell, OH 43065", - buttonLabel: "3938 Powell Rd", + buttonLabel: "Powell", buttonLabelSubCopy: "16.5 mi", value: "003341", }, { buttonBodyCopy: "4580 W Broad St, Columbus, OH 43228", - buttonLabel: "4580 W Broad St", + buttonLabel: "Columbus", buttonLabelSubCopy: "19.5 mi", value: "003342", }, diff --git a/src/layouts/service-location/shop-question/shop-question.vue b/src/layouts/service-location/shop-question/shop-question.vue index fffb36c49..3183cbc9e 100644 --- a/src/layouts/service-location/shop-question/shop-question.vue +++ b/src/layouts/service-location/shop-question/shop-question.vue @@ -143,7 +143,7 @@ export default { const distanceInMiles = Math.round(shopProvider.distanceInMiles * 2) / 2; return { - buttonLabel: streetAddress, + buttonLabel: city, buttonLabelSubCopy: `${distanceInMiles} mi`, buttonBodyCopy: `${streetAddress}, ${city}, ${state} ${zipCode}`, value: shopProvider.providerNumber, diff --git a/src/router/index.js b/src/router/index.js index 86c402187..15ba52647 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -28,27 +28,7 @@ import analyticsMixin from "@/mixins/analytics-mixin"; import { experimentTriggers } from "../constants/experiments"; import { applicationConfig } from "../constants/application-config"; -// Components -import datePicker from "@/digital-components/date-picker/date-picker.vue"; -import demoDatePicker from "@/layouts/demo-date-picker/demo-date-picker.vue"; -import review from "@/layouts/review/review"; - const routes = [ - { - path: "/demo-date-picker", // This is a temporary route for testing. - name: "demo-date-picker", - component: demoDatePicker, - }, - { - path: "/date-picker", // This is a temporary route for testing. - name: "date-picker", - component: datePicker, - }, - { - path: "/review", // This is a temporary route for testing. - name: "review", - component: review, - }, { path: "/", name: "root",