Merge branch 'develop' into feature/CASH-848
This commit is contained in:
commit
455bc44195
35 changed files with 450 additions and 163 deletions
|
|
@ -43,7 +43,7 @@ module.exports = {
|
||||||
testMatch: ["**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)"],
|
testMatch: ["**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)"],
|
||||||
coverageThreshold: {
|
coverageThreshold: {
|
||||||
global: {
|
global: {
|
||||||
statements: 66,
|
statements: 64,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// 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
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ import { VerifyDetailsPage } from "../pages/VerifyDetailsPage"
|
||||||
import { EndorsementsPage } from "../pages/EndorsementsPage"
|
import { EndorsementsPage } from "../pages/EndorsementsPage"
|
||||||
import { PolicyDriverPage } from "../pages/PolicyDriverPage"
|
import { PolicyDriverPage } from "../pages/PolicyDriverPage"
|
||||||
import { ServiceZipPage } from "../pages/ServiceZipPage"
|
import { ServiceZipPage } from "../pages/ServiceZipPage"
|
||||||
|
import { MobileDetailsPage } from "pages/MobileDetailsPage";
|
||||||
|
|
||||||
export interface ITestPages {
|
export interface ITestPages {
|
||||||
capabilityQuestionsPage: CapabilityQuestionsPage,
|
capabilityQuestionsPage: CapabilityQuestionsPage,
|
||||||
|
|
@ -49,6 +50,7 @@ export interface ITestPages {
|
||||||
policyVehiclesPage: PolicyVehiclesPage,
|
policyVehiclesPage: PolicyVehiclesPage,
|
||||||
recalibrationInfoPage: RecalibrationInfoPage,
|
recalibrationInfoPage: RecalibrationInfoPage,
|
||||||
schedulePage: SchedulePage,
|
schedulePage: SchedulePage,
|
||||||
|
mobileDetailsPage: MobileDetailsPage,
|
||||||
serviceLocationPage: ServiceLocationPage,
|
serviceLocationPage: ServiceLocationPage,
|
||||||
servicePackagesPage: ServicePackagesPage,
|
servicePackagesPage: ServicePackagesPage,
|
||||||
serviceZipPage: ServiceZipPage,
|
serviceZipPage: ServiceZipPage,
|
||||||
|
|
@ -83,6 +85,7 @@ export const createTestPages: TestPagesFactory<ITestPages> = (page: Page) => {
|
||||||
policyVehiclesPage: new PolicyVehiclesPage(page),
|
policyVehiclesPage: new PolicyVehiclesPage(page),
|
||||||
recalibrationInfoPage: new RecalibrationInfoPage(page),
|
recalibrationInfoPage: new RecalibrationInfoPage(page),
|
||||||
schedulePage: new SchedulePage(page),
|
schedulePage: new SchedulePage(page),
|
||||||
|
mobileDetailsPage: new MobileDetailsPage(page),
|
||||||
serviceLocationPage: new ServiceLocationPage(page),
|
serviceLocationPage: new ServiceLocationPage(page),
|
||||||
servicePackagesPage: new ServicePackagesPage(page),
|
servicePackagesPage: new ServicePackagesPage(page),
|
||||||
serviceZipPage: new ServiceZipPage(page),
|
serviceZipPage: new ServiceZipPage(page),
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,8 @@ export enum ProgressBarPercentages {
|
||||||
ServicePackagePage = '48%',
|
ServicePackagePage = '48%',
|
||||||
InsuranceCompanyPage = '52%',
|
InsuranceCompanyPage = '52%',
|
||||||
ServiceLocationPage = '60%',
|
ServiceLocationPage = '60%',
|
||||||
SchedulePage = '72%',
|
SchedulePage = '64%',
|
||||||
|
MobileDetailsPage = '76%',
|
||||||
ContactDetailsPage = '84%',
|
ContactDetailsPage = '84%',
|
||||||
PaymentMethodPage = '92%',
|
PaymentMethodPage = '92%',
|
||||||
OrderConfirmationPage = '100%'
|
OrderConfirmationPage = '100%'
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ export class AfterpayPage extends BasePage {
|
||||||
this.expirationDateTextBox = page.getByTestId('payment-method-cardExpiry-input');
|
this.expirationDateTextBox = page.getByTestId('payment-method-cardExpiry-input');
|
||||||
this.cvvTextBox = page.getByTestId('payment-method-cardCvv-input');
|
this.cvvTextBox = page.getByTestId('payment-method-cardCvv-input');
|
||||||
|
|
||||||
this.confirmButton = page.getByRole('button', { name: 'Confirm' });
|
this.confirmButton = page.getByRole('button').filter({hasText: 'Confirm'});
|
||||||
}
|
}
|
||||||
|
|
||||||
async login(password: string) {
|
async login(password: string) {
|
||||||
|
|
@ -47,8 +47,6 @@ export class AfterpayPage extends BasePage {
|
||||||
async executeAfterpayPayment(paymentDetails: IPaymentDetails) {
|
async executeAfterpayPayment(paymentDetails: IPaymentDetails) {
|
||||||
await this.login(paymentDetails.password!);
|
await this.login(paymentDetails.password!);
|
||||||
|
|
||||||
await this.populateCardDetails(paymentDetails);
|
|
||||||
|
|
||||||
await this.confirmButton.click();
|
await this.confirmButton.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -103,32 +103,31 @@ export class BasePage {
|
||||||
}
|
}
|
||||||
|
|
||||||
async mockScheduleResponseForEarlyBird(customerDetails: ICustomerDetails) {
|
async mockScheduleResponseForEarlyBird(customerDetails: ICustomerDetails) {
|
||||||
{
|
|
||||||
const apiUrl = `https://digitalapi.${process.env['NODE_ENV']!.replace('sys', 'test').toLowerCase()}.safelite.io/schedule/api/v1/schedule/mobile-time-slots`;
|
const apiUrl = `https://digitalapi.${process.env['NODE_ENV']!.replace('sys', 'test').toLowerCase()}.safelite.io/schedule/api/v1/schedule/mobile-time-slots`;
|
||||||
await this.page.route(apiUrl, async (route) => {
|
await this.page.route(apiUrl, async (route) => {
|
||||||
const response = await route.fetch();
|
const currentDate = new Date().toISOString().split('T')[0]; // e.g., "2025-07-23"
|
||||||
const responseBody = await response.json();
|
if(route.request().postDataJSON().startDate === currentDate) {
|
||||||
|
const response = await route.fetch();
|
||||||
|
const responseBody = await response.json();
|
||||||
|
|
||||||
|
responseBody.days.forEach((day: any) => {
|
||||||
responseBody.days.forEach((day: any) => {
|
day.timeSlots.forEach((slot: any) => {
|
||||||
day.timeSlots.forEach((slot: any) => {
|
if (slot.id.includes("AM")) {
|
||||||
if (slot.id.includes("AM")) {
|
slot.offerPremium = true;
|
||||||
slot.offerPremium = true;
|
}
|
||||||
}
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
customerDetails.apptDate = responseBody.days.find((day: any) =>
|
customerDetails.apptDate = responseBody.days.find((day: any) => day.timeSlots.some((slot: any) => slot.offerPremium === true)).date || undefined;
|
||||||
day.timeSlots.some((slot: any) => slot.offerPremium === true)
|
|
||||||
).date || undefined;
|
|
||||||
|
|
||||||
// Mock the response
|
// Mock the response
|
||||||
await route.fulfill({
|
await route.fulfill({
|
||||||
response,
|
response,
|
||||||
body: JSON.stringify(responseBody),
|
body: JSON.stringify(responseBody),
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async validateProgressBar(progressPercentage: string, timeout: number = 60000) {
|
async validateProgressBar(progressPercentage: string, timeout: number = 60000) {
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ export class CoverageStatementPage extends InsuranceBasePage {
|
||||||
|
|
||||||
if (await unverifiedDeductibleElement.isVisible() || await this.noCompText.isVisible()) {
|
if (await unverifiedDeductibleElement.isVisible() || await this.noCompText.isVisible()) {
|
||||||
// Click on continue button if unverified header is visible
|
// Click on continue button if unverified header is visible
|
||||||
await this.continueButton.click();
|
// await this.continueButton.click();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
54
playwright-tests/pages/MobileDetailsPage.ts
Normal file
54
playwright-tests/pages/MobileDetailsPage.ts
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
import { type Locator, type Page } from '@playwright/test';
|
||||||
|
import { BasePage } from './BasePage';
|
||||||
|
import { VehicleLookupType } from 'safelite-playwright-core';
|
||||||
|
import { ProgressBarPercentages } from 'framework/localTypes/Enums';
|
||||||
|
import { IVehicleDetails } from 'safelite-playwright-core';
|
||||||
|
import { VinLookupPage } from './VinLookupPage';
|
||||||
|
import { VehicleLookupAddressPage } from './VehicleLookupAddressPage';
|
||||||
|
import { VehicleLookupLicensePage } from './VehicleLookupLicensePage';
|
||||||
|
import { step } from 'framework/localTypes/Step';
|
||||||
|
import { ITestData } from 'framework/TestData';
|
||||||
|
import { faker } from '@faker-js/faker';
|
||||||
|
|
||||||
|
export class MobileDetailsPage extends BasePage {
|
||||||
|
readonly page: Page;
|
||||||
|
readonly streetAddressInputBox: Locator;
|
||||||
|
readonly CityInputBox: Locator;
|
||||||
|
readonly YesButton: Locator;
|
||||||
|
readonly NoButton: Locator;
|
||||||
|
|
||||||
|
|
||||||
|
constructor(page: Page) {
|
||||||
|
super(page);
|
||||||
|
this.page = page;
|
||||||
|
this.streetAddressInputBox = page.locator('#streetAddress');
|
||||||
|
this.CityInputBox = page.locator('#city');
|
||||||
|
this.YesButton = page.locator('label[buttonlabel="Yes"]');
|
||||||
|
this.NoButton = page.locator('label[buttonlabel="No"]');
|
||||||
|
}
|
||||||
|
|
||||||
|
async enterMobileDetails(testData: Partial<ITestData>) {
|
||||||
|
const { appointmentDetails, customerDetails } = testData;
|
||||||
|
if (appointmentDetails?.serviceAddress) {
|
||||||
|
await this.streetAddressInputBox.fill(appointmentDetails.serviceAddress!.street);
|
||||||
|
await this.CityInputBox.fill(appointmentDetails.serviceAddress!.city);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
console.error('ServiceLocationPage >> Please supply an address')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (faker.datatype.boolean()) {
|
||||||
|
await this.YesButton.check();
|
||||||
|
} else {
|
||||||
|
await this.NoButton.check();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@step("MobileDetailsPage >> Enter service address: ")
|
||||||
|
async handleMobileDetailsPage(testData: Partial<ITestData>) {
|
||||||
|
await this.validateProgressBar(ProgressBarPercentages.MobileDetailsPage);
|
||||||
|
await this.enterMobileDetails(testData);
|
||||||
|
await this.nextPage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -206,7 +206,7 @@ export class PaymentMethodPage extends BasePage {
|
||||||
break;
|
break;
|
||||||
case PaymentType.AfterPay:
|
case PaymentType.AfterPay:
|
||||||
await this.payInFourButton.click();
|
await this.payInFourButton.click();
|
||||||
await this.nextPage();
|
await this.continueButton.click();
|
||||||
|
|
||||||
// Capture popup
|
// Capture popup
|
||||||
const afterpayPopup = await browserContext.waitForEvent('page');
|
const afterpayPopup = await browserContext.waitForEvent('page');
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { expect, type Locator, type Page } from '@playwright/test';
|
import { expect, type Locator, type Page } from '@playwright/test';
|
||||||
import { BasePage } from './BasePage';
|
import { BasePage } from './BasePage';
|
||||||
import { IAppointmentDetails } from 'safelite-playwright-core';
|
import { IAppointmentDetails, ServiceLocation } from 'safelite-playwright-core';
|
||||||
import { formatDate, formatTime } from 'safelite-playwright-core';
|
import { formatDate, formatTime } from 'safelite-playwright-core';
|
||||||
import { AppointmentTimeslot } from 'safelite-playwright-core';
|
import { AppointmentTimeslot } from 'safelite-playwright-core';
|
||||||
import { ProgressBarPercentages } from 'framework/localTypes/Enums';
|
import { ProgressBarPercentages } from 'framework/localTypes/Enums';
|
||||||
|
|
@ -10,6 +10,18 @@ import { ITestData } from 'framework/TestData';
|
||||||
|
|
||||||
export class SchedulePage extends BasePage {
|
export class SchedulePage extends BasePage {
|
||||||
readonly page: Page;
|
readonly page: Page;
|
||||||
|
readonly inShopButton: Locator;
|
||||||
|
readonly mobileButton: Locator;
|
||||||
|
readonly RecalWarningMessage1: Locator;
|
||||||
|
readonly RecalWarningMessage2: Locator;
|
||||||
|
readonly militaryWarningMessage: Locator;
|
||||||
|
readonly changeZipButton: Locator;
|
||||||
|
readonly updateZipTextBox: Locator;
|
||||||
|
readonly saveZipButton: Locator;
|
||||||
|
readonly changeShopLocationLink: Locator;
|
||||||
|
readonly selectAShopOptions: Locator;
|
||||||
|
readonly yourSafeliteShop: Locator;
|
||||||
|
|
||||||
readonly firstAvailableDate: Locator;
|
readonly firstAvailableDate: Locator;
|
||||||
readonly firstAvailableTime: Locator;
|
readonly firstAvailableTime: Locator;
|
||||||
readonly modalContinueButton: Locator;
|
readonly modalContinueButton: Locator;
|
||||||
|
|
@ -18,11 +30,28 @@ export class SchedulePage extends BasePage {
|
||||||
readonly viewMoreDatesLink: Locator;
|
readonly viewMoreDatesLink: Locator;
|
||||||
readonly appointmentDuration: Locator;
|
readonly appointmentDuration: Locator;
|
||||||
readonly timeSlots: Locator;
|
readonly timeSlots: Locator;
|
||||||
|
readonly allDayDropOffButton: Locator;
|
||||||
|
readonly pickATimeButton: Locator;
|
||||||
|
|
||||||
constructor(page: Page) {
|
constructor(page: Page) {
|
||||||
super(page);
|
super(page);
|
||||||
this.page = page;
|
this.page = page;
|
||||||
this.firstAvailableDate = this.page.locator('.selectable-day').locator('nth=0');
|
|
||||||
|
this.inShopButton = this.page.getByText(/In-shop/);
|
||||||
|
this.mobileButton = this.page.locator('label[buttonlabel="Mobile"]');
|
||||||
|
this.RecalWarningMessage1 = this.page.getByText(/We're not able to provide mobile service/);
|
||||||
|
this.RecalWarningMessage2 = this.page.getByText(/advanced safety system recalibration needs to be done in our shop./);
|
||||||
|
this.militaryWarningMessage = this.page.locator('[class*="widget-name-AlertMilitaryBaseZipWidget"]');
|
||||||
|
this.changeZipButton = this.page.locator('a:has(span.sr-only:has-text("edit zip code"))');
|
||||||
|
this.updateZipTextBox = this.page.locator('#serviceZipCode').filter({ visible: true });
|
||||||
|
this.saveZipButton = this.page.getByText('Save ZIP code', { exact: true });
|
||||||
|
this.changeShopLocationLink = this.page.locator(".shop-question a").filter({ hasText: "Change shop location " });
|
||||||
|
this.selectAShopOptions = this.page.locator('[class="shop-question"]');
|
||||||
|
this.yourSafeliteShop = this.page.locator("fieldset:has(#chooseShop) label");
|
||||||
|
|
||||||
|
this.allDayDropOffButton = this.page.locator("label[buttonlabel='Drop off all day']");
|
||||||
|
this.pickATimeButton = this.page.locator("label[buttonlabel='Pick a time']");
|
||||||
|
this.firstAvailableDate = this.page.locator('.selectable-day').filter({ visible: true}).locator('nth=0');
|
||||||
this.firstAvailableTime = this.page.locator('label').filter({ hasText: /AM|PM/ }).locator('div').locator('nth=0');
|
this.firstAvailableTime = this.page.locator('label').filter({ hasText: /AM|PM/ }).locator('div').locator('nth=0');
|
||||||
this.modalContinueButton = this.page.getByRole('dialog').getByRole('button', { name: 'Continue' });
|
this.modalContinueButton = this.page.getByRole('dialog').getByRole('button', { name: 'Continue' });
|
||||||
this.dropOffButton = this.page.getByText('Drop off your vehicle', { exact: true });
|
this.dropOffButton = this.page.getByText('Drop off your vehicle', { exact: true });
|
||||||
|
|
@ -32,6 +61,49 @@ export class SchedulePage extends BasePage {
|
||||||
this.timeSlots = this.page.locator('fieldset[aria-labelledby=\'chooseTimeSlot\'] label');
|
this.timeSlots = this.page.locator('fieldset[aria-labelledby=\'chooseTimeSlot\'] label');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async selectLocation(testData: Partial<ITestData>) {
|
||||||
|
const { appointmentDetails, customerDetails } = testData;
|
||||||
|
|
||||||
|
switch(appointmentDetails?.serviceLocation) {
|
||||||
|
case ServiceLocation.Mobile:
|
||||||
|
await this.scheduleMobile(testData);
|
||||||
|
break;
|
||||||
|
case ServiceLocation.InShop:
|
||||||
|
case ServiceLocation.DropOff:
|
||||||
|
await this.scheduleInShop(appointmentDetails);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async scheduleInShop(appointmentDetails?: IAppointmentDetails) {
|
||||||
|
await this.inShopButton.click();
|
||||||
|
if (appointmentDetails && appointmentDetails.shopAddress) {
|
||||||
|
const zipCodeMatch = appointmentDetails.shopAddress.match(/\b\d{5}$/);
|
||||||
|
if (zipCodeMatch) {
|
||||||
|
const zipCode = zipCodeMatch[0];
|
||||||
|
// Enter the ZIP code into the updateZipTextBox
|
||||||
|
await this.changeZipButton.click();
|
||||||
|
await this.page.waitForTimeout(500);
|
||||||
|
await this.updateZipTextBox.fill(zipCode);
|
||||||
|
await this.saveZipButton.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.inShopButton.click();
|
||||||
|
// await this.selectAShopOptions.locator(`[buttonbodycopy="${appointmentDetails.shopAddress}"]`).scrollIntoViewIfNeeded().then(() => this.selectAShopOptions.locator(`[buttonbodycopy="${appointmentDetails.shopAddress}"]`).click());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (appointmentDetails && (appointmentDetails.shopAddress === "" || appointmentDetails.shopAddress === undefined )) {
|
||||||
|
appointmentDetails.shopAddress = await this.yourSafeliteShop.getAttribute("buttonbodycopy") || "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async scheduleMobile(testData: Partial<ITestData>) {
|
||||||
|
const { appointmentDetails, customerDetails } = testData;
|
||||||
|
if (appointmentDetails?.serviceAddress) {
|
||||||
|
await this.mobileButton.click();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async scheduleAppointment(appointmentDetails: IAppointmentDetails) {
|
async scheduleAppointment(appointmentDetails: IAppointmentDetails) {
|
||||||
const formattedDate = formatDate(appointmentDetails.appointmentDate!);
|
const formattedDate = formatDate(appointmentDetails.appointmentDate!);
|
||||||
const formattedTime = formatTime(appointmentDetails.appointmentDate!);
|
const formattedTime = formatTime(appointmentDetails.appointmentDate!);
|
||||||
|
|
@ -49,26 +121,60 @@ export class SchedulePage extends BasePage {
|
||||||
|
|
||||||
async scheduleFirstAppointment(testData: Partial<ITestData>) {
|
async scheduleFirstAppointment(testData: Partial<ITestData>) {
|
||||||
const { appointmentDetails, customerDetails } = testData;
|
const { appointmentDetails, customerDetails } = testData;
|
||||||
|
// await this.page.waitForTimeout(1000);
|
||||||
|
await this.page.waitForSelector('.date-picker', { state: 'visible' });
|
||||||
|
await this.page.locator('#date-picker-fieldset .loader').filter({visible: true}).waitFor({ state: 'hidden' });
|
||||||
while (!(await this.firstAvailableDate.isVisible())) {
|
while (!(await this.firstAvailableDate.isVisible())) {
|
||||||
|
|
||||||
if (appointmentDetails?.appointmentTimeSlot == AppointmentTimeslot.EarlyBird) {
|
if (appointmentDetails?.appointmentTimeSlot === AppointmentTimeslot.EarlyBird) {
|
||||||
await this.mockScheduleResponseForEarlyBird(customerDetails!);
|
await this.mockScheduleResponseForEarlyBird(customerDetails!);
|
||||||
}
|
}
|
||||||
await this.viewMoreDatesLink.click();
|
await this.viewMoreDatesLink.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch(appointmentDetails?.serviceLocation) {
|
||||||
|
case ServiceLocation.InShop:
|
||||||
|
await this.firstAvailableDate.click().then(async () => {
|
||||||
|
customerDetails!.apptDate = `${await this.firstAvailableDate.getAttribute("id")}`
|
||||||
|
});
|
||||||
|
if (await this.pickATimeButton.isVisible()) {
|
||||||
|
await this.pickATimeButton.click();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ServiceLocation.DropOff:
|
||||||
|
const inshopAvailableDates = this.page.locator('.selectable-day').filter({ visible: true}).all();
|
||||||
|
for (const inshopAvailableDate of await inshopAvailableDates) {
|
||||||
|
await inshopAvailableDate.click();
|
||||||
|
if (await this.allDayDropOffButton.isVisible()) {
|
||||||
|
customerDetails!.apptDate = `${await inshopAvailableDate.getAttribute("id")}`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ServiceLocation.Mobile:
|
||||||
|
if (appointmentDetails?.appointmentTimeSlot === AppointmentTimeslot.EarlyBird) {
|
||||||
|
await this.page.locator(`.selectable-days, [id='${customerDetails?.apptDate}-mobile']`).click();
|
||||||
|
} else {
|
||||||
|
await this.firstAvailableDate.click().then(async () => {
|
||||||
|
customerDetails!.apptDate = `${await this.firstAvailableDate.getAttribute("id")}`.replace("-mobile", "");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (appointmentDetails?.appointmentTimeSlot == AppointmentTimeslot.EarlyBird) {
|
if (appointmentDetails?.appointmentTimeSlot == AppointmentTimeslot.EarlyBird) {
|
||||||
await this.page.locator(`.selectable-days, [id='${customerDetails?.apptDate}']`).click();
|
|
||||||
const earlyBirdTimeSlot = this.timeSlots.filter({ hasText: "Earlybird" }).first();
|
const earlyBirdTimeSlot = this.timeSlots.filter({ hasText: "Earlybird" }).first();
|
||||||
await earlyBirdTimeSlot.click().then(async () => {
|
await earlyBirdTimeSlot.click().then(async () => {
|
||||||
customerDetails!.apptTime = await this.getFormattedTimeSlot(earlyBirdTimeSlot);
|
customerDetails!.apptTime = await this.getFormattedTimeSlot(earlyBirdTimeSlot);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
else if (appointmentDetails?.serviceLocation == ServiceLocation.DropOff)
|
||||||
|
{
|
||||||
|
await this.allDayDropOffButton.click().then(async () => {
|
||||||
|
customerDetails!.apptTime = await this.getFormattedTimeSlot(this.allDayDropOffButton);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
await this.firstAvailableDate.click().then(async () => {
|
|
||||||
customerDetails!.apptDate = `${await this.firstAvailableDate.getAttribute("id")}`
|
|
||||||
});
|
|
||||||
|
|
||||||
// const timeSlots = this.timeSlots;
|
// const timeSlots = this.timeSlots;
|
||||||
const timeSlotCount = await this.timeSlots.count();
|
const timeSlotCount = await this.timeSlots.count();
|
||||||
const randomIndex = Math.floor(Math.random() * timeSlotCount);
|
const randomIndex = Math.floor(Math.random() * timeSlotCount);
|
||||||
|
|
@ -80,7 +186,6 @@ export class SchedulePage extends BasePage {
|
||||||
|
|
||||||
// appointmentmentDetails.serviceLocation === ServiceLocation.DropOff ? await this.dropOffButton.click() : await this.firstAvailableTime.click();
|
// appointmentmentDetails.serviceLocation === ServiceLocation.DropOff ? await this.dropOffButton.click() : await this.firstAvailableTime.click();
|
||||||
customerDetails!.apptDuration = (await this.appointmentDuration.innerText()).replace("Duration: ", "");
|
customerDetails!.apptDuration = (await this.appointmentDuration.innerText()).replace("Duration: ", "");
|
||||||
await this.nextPage();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getFormattedTimeSlot(timeSlot: Locator) {
|
async getFormattedTimeSlot(timeSlot: Locator) {
|
||||||
|
|
@ -103,8 +208,10 @@ export class SchedulePage extends BasePage {
|
||||||
|
|
||||||
@step("SchedulePage >> Schedule appointment: ")
|
@step("SchedulePage >> Schedule appointment: ")
|
||||||
async handleSchedulePage(testData: Partial<ITestData>) {
|
async handleSchedulePage(testData: Partial<ITestData>) {
|
||||||
|
const { appointmentDetails } = testData;
|
||||||
await this.validateProgressBar(ProgressBarPercentages.SchedulePage);
|
await this.validateProgressBar(ProgressBarPercentages.SchedulePage);
|
||||||
|
await this.selectLocation(testData);
|
||||||
await this.scheduleFirstAppointment(testData);
|
await this.scheduleFirstAppointment(testData);
|
||||||
|
await this.nextPage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { expect, type Locator, type Page } from '@playwright/test';
|
import { expect, type Locator, type Page } from '@playwright/test';
|
||||||
import { BasePage } from './BasePage';
|
import { BasePage } from './BasePage';
|
||||||
import { ServicePackage, VehicleDamage } from 'safelite-playwright-core';
|
import { AppointmentTimeslot, ServicePackage, VehicleDamage } from 'safelite-playwright-core';
|
||||||
import { ProgressBarPercentages } from 'framework/localTypes/Enums';
|
import { ProgressBarPercentages } from 'framework/localTypes/Enums';
|
||||||
import { PaymentMethod } from "framework/localTypes/Enums";
|
import { PaymentMethod } from "framework/localTypes/Enums";
|
||||||
import { step } from 'framework/localTypes/Step';
|
import { step } from 'framework/localTypes/Step';
|
||||||
|
|
@ -189,7 +189,7 @@ export class ServicePackagesPage extends BasePage {
|
||||||
|
|
||||||
@step("ServicePackagePage >> Select Payment Method and Service Type: ")
|
@step("ServicePackagePage >> Select Payment Method and Service Type: ")
|
||||||
async handleServicePackagePage(testData: Partial<ITestData>) {
|
async handleServicePackagePage(testData: Partial<ITestData>) {
|
||||||
const { customerDetails, paymentMethod, paymentDetails, servicePackage, isCanNotRecal, isDynamicRecal, hasOemEndorsement, vehicleDamage } = testData;
|
const { customerDetails, paymentMethod, paymentDetails, servicePackage, isCanNotRecal, isDynamicRecal, hasOemEndorsement, vehicleDamage, appointmentDetails } = testData;
|
||||||
|
|
||||||
await this.validateProgressBar(ProgressBarPercentages.ServicePackagePage);
|
await this.validateProgressBar(ProgressBarPercentages.ServicePackagePage);
|
||||||
// Define repair damage types (vs. replacement types)
|
// Define repair damage types (vs. replacement types)
|
||||||
|
|
@ -235,6 +235,10 @@ export class ServicePackagesPage extends BasePage {
|
||||||
if (hasOemEndorsement) {
|
if (hasOemEndorsement) {
|
||||||
await this.verifyOEMPart();
|
await this.verifyOEMPart();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (appointmentDetails?.appointmentTimeSlot === AppointmentTimeslot.EarlyBird) {
|
||||||
|
await this.mockScheduleResponseForEarlyBird(customerDetails!);
|
||||||
|
}
|
||||||
|
|
||||||
await this.nextPage();
|
await this.nextPage();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { Page } from "@playwright/test";
|
import { Page } from "@playwright/test";
|
||||||
import { addSmokeTagToRandomTest, Flow } from 'safelite-playwright-core';
|
import { addSmokeTagToRandomTest, Flow, ServiceLocation } from 'safelite-playwright-core';
|
||||||
import { ValidationOptions } from 'safelite-playwright-core';
|
import { ValidationOptions } from 'safelite-playwright-core';
|
||||||
import heavyTruckTests from "./alert-validation/alert0001_HeavyTruck";
|
import heavyTruckTests from "./alert-validation/alert0001_HeavyTruck";
|
||||||
import repairAndReplaceTests from "./alert-validation/alert0002_RepairAndReplace";
|
import repairAndReplaceTests from "./alert-validation/alert0002_RepairAndReplace";
|
||||||
|
|
@ -288,14 +288,19 @@ async function runWorkflow(page: Page, testCase: TestCase) {
|
||||||
//============================= SERVICE SCHEDULING =============================
|
//============================= SERVICE SCHEDULING =============================
|
||||||
|
|
||||||
// Select service location
|
// Select service location
|
||||||
let serviceLocationPage = testCase.pages.serviceLocationPage;
|
// let serviceLocationPage = testCase.pages.serviceLocationPage;
|
||||||
await serviceLocationPage.handleServiceLocationPage(testCase.testData);
|
// await serviceLocationPage.handleServiceLocationPage(testCase.testData);
|
||||||
|
|
||||||
|
|
||||||
// Schedule appointment
|
// Schedule appointment
|
||||||
let schedulePage = testCase.pages.schedulePage;
|
let schedulePage = testCase.pages.schedulePage;
|
||||||
await schedulePage.handleSchedulePage(testCase.testData);
|
await schedulePage.handleSchedulePage(testCase.testData);
|
||||||
|
|
||||||
|
if (testCase.testData.appointmentDetails?.serviceLocation === ServiceLocation.Mobile) {
|
||||||
|
let mobileDetailsPage = testCase.pages.mobileDetailsPage;
|
||||||
|
await mobileDetailsPage.handleMobileDetailsPage(testCase.testData);
|
||||||
|
}
|
||||||
|
|
||||||
// Enter contact details
|
// Enter contact details
|
||||||
let contactDetailsPage = testCase.pages.contactDetailsPage;
|
let contactDetailsPage = testCase.pages.contactDetailsPage;
|
||||||
await contactDetailsPage.handleContactDetailsPage(testCase.testData);
|
await contactDetailsPage.handleContactDetailsPage(testCase.testData);
|
||||||
|
|
@ -376,12 +381,14 @@ export async function handleInsuranceFlow(testCase: TestCase) {
|
||||||
// Handle scenario where user selected Cash to Insurance and needs to go back through the flow
|
// Handle scenario where user selected Cash to Insurance and needs to go back through the flow
|
||||||
// right now we are choosing pay at appointment as payment method for this scenario
|
// right now we are choosing pay at appointment as payment method for this scenario
|
||||||
if (isCashInsuranceFlow) {
|
if (isCashInsuranceFlow) {
|
||||||
let serviceLocationPage = testCase.pages.serviceLocationPage;
|
// Schedule appointment
|
||||||
await serviceLocationPage.nextPage();
|
|
||||||
|
|
||||||
//schedule
|
|
||||||
let schedulePage = testCase.pages.schedulePage;
|
let schedulePage = testCase.pages.schedulePage;
|
||||||
await schedulePage.nextPage();
|
await schedulePage.handleSchedulePage(testCase.testData);
|
||||||
|
|
||||||
|
if (testCase.testData.appointmentDetails?.serviceLocation === ServiceLocation.Mobile) {
|
||||||
|
let mobileDetailsPage = testCase.pages.mobileDetailsPage;
|
||||||
|
await mobileDetailsPage.handleMobileDetailsPage(testCase.testData);
|
||||||
|
}
|
||||||
//customer dertails
|
//customer dertails
|
||||||
let contactDetailsPage = testCase.pages.contactDetailsPage;
|
let contactDetailsPage = testCase.pages.contactDetailsPage;
|
||||||
await contactDetailsPage.nextPage();
|
await contactDetailsPage.nextPage();
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,6 @@ const cashRepairInShopAfterPayData : Partial<ITestData> = {
|
||||||
// Override appointment details
|
// Override appointment details
|
||||||
appointmentDetails: {
|
appointmentDetails: {
|
||||||
...getDefaultTestData().appointmentDetails!,
|
...getDefaultTestData().appointmentDetails!,
|
||||||
shopAddress: "6826 Sawmill Rd, Columbus, OH 43235"
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Use predefined payment data
|
// Use predefined payment data
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,17 @@ const cashReplaceGlassPromoInshopData: Partial<ITestData> = {
|
||||||
|
|
||||||
// CASH Client
|
// CASH Client
|
||||||
paymentMethod: PaymentMethod.SelfPay,
|
paymentMethod: PaymentMethod.SelfPay,
|
||||||
|
|
||||||
|
customerDetails: {
|
||||||
|
...getDefaultTestData().customerDetails!,
|
||||||
|
address: {
|
||||||
|
street: '4076 Spectacle Dr',
|
||||||
|
city: 'Columbus',
|
||||||
|
state: 'Ohio',
|
||||||
|
postalCode: '43235',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// Flag for recalibration vehicle
|
// Flag for recalibration vehicle
|
||||||
isRecalVehicle: true,
|
isRecalVehicle: true,
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,8 @@ const cashReplaceMultiSlidingGlassDropoffData: Partial<ITestData> = {
|
||||||
// Override for drop-off service
|
// Override for drop-off service
|
||||||
appointmentDetails: {
|
appointmentDetails: {
|
||||||
serviceLocation: ServiceLocation.DropOff,
|
serviceLocation: ServiceLocation.DropOff,
|
||||||
appointmentDate: getDefaultTestData().appointmentDetails?.appointmentDate
|
appointmentDate: getDefaultTestData().appointmentDetails?.appointmentDate,
|
||||||
|
shopAddress: "6826 Sawmill Rd, Columbus, OH 43235"
|
||||||
},
|
},
|
||||||
|
|
||||||
// Override vehicle details
|
// Override vehicle details
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
//Imports here
|
//Imports here
|
||||||
import { ITestData } from 'framework/TestData'
|
import { ITestData } from 'framework/TestData'
|
||||||
import { ServiceLocation } from "safelite-playwright-core";
|
import { Flow, ServiceLocation } from "safelite-playwright-core";
|
||||||
import { DamageType, PartQuestionType, PaymentType } from 'safelite-playwright-core'
|
import { DamageType, PartQuestionType, PaymentType } from 'safelite-playwright-core'
|
||||||
import { ITestCase } from '../framework/Typedefs'
|
import { ITestCase } from '../framework/Typedefs'
|
||||||
import { getDefaultTestData, setFakerSeedFromTestName } from 'safelite-playwright-core';
|
import { getDefaultTestData, setFakerSeedFromTestName } from 'safelite-playwright-core';
|
||||||
|
import { PaymentMethod } from 'framework/localTypes/Enums';
|
||||||
|
|
||||||
// Set the seed for consistent data generation
|
// Set the seed for consistent data generation
|
||||||
setFakerSeedFromTestName("CashReplaceSwitchToInsuranceProgressiveNoComp");
|
setFakerSeedFromTestName("CashReplaceSwitchToInsuranceProgressiveNoComp");
|
||||||
|
|
@ -13,6 +14,10 @@ const cashReplaceSwitchToInsuranceProgressiveNoCompData: Partial<ITestData> = {
|
||||||
...getDefaultTestData(),
|
...getDefaultTestData(),
|
||||||
|
|
||||||
isCashInsuranceFlow: true,
|
isCashInsuranceFlow: true,
|
||||||
|
flow: Flow.Managed,
|
||||||
|
|
||||||
|
// CASH Client
|
||||||
|
paymentMethod: PaymentMethod.SelfPay,
|
||||||
|
|
||||||
// Override customer details based on provided ZIP code
|
// Override customer details based on provided ZIP code
|
||||||
customerDetails: {
|
customerDetails: {
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,8 @@ const cashReplaceWiperDropoffData: Partial<ITestData> = {
|
||||||
// Override for drop-off service
|
// Override for drop-off service
|
||||||
appointmentDetails: {
|
appointmentDetails: {
|
||||||
serviceLocation: ServiceLocation.DropOff,
|
serviceLocation: ServiceLocation.DropOff,
|
||||||
appointmentDate: getDefaultTestData().appointmentDetails?.appointmentDate
|
appointmentDate: getDefaultTestData().appointmentDetails?.appointmentDate,
|
||||||
|
shopAddress: "6826 Sawmill Rd, Columbus, OH 43235"
|
||||||
},
|
},
|
||||||
|
|
||||||
// Payment at service
|
// Payment at service
|
||||||
|
|
|
||||||
|
|
@ -1,35 +1,35 @@
|
||||||
export const pageProgressMapper = {
|
export const pageProgressMapper = {
|
||||||
//Combine progress bar slide percent and button steps
|
//Combine progress bar slide percent and button steps
|
||||||
vehicle: {
|
vehicle: {
|
||||||
percent: 7,
|
|
||||||
step: 1,
|
|
||||||
},
|
|
||||||
"vehicle-damage": {
|
|
||||||
percent: 10,
|
percent: 10,
|
||||||
step: 1,
|
step: 1,
|
||||||
},
|
},
|
||||||
estimate: {
|
"vehicle-damage": {
|
||||||
percent: 14,
|
percent: 14,
|
||||||
step: 1,
|
step: 1,
|
||||||
},
|
},
|
||||||
"service-zip": {
|
estimate: {
|
||||||
percent: 18,
|
percent: 18,
|
||||||
step: 1,
|
step: 1,
|
||||||
},
|
},
|
||||||
|
"service-zip": {
|
||||||
|
percent: 22,
|
||||||
|
step: 1,
|
||||||
|
},
|
||||||
"vin-lookup": {
|
"vin-lookup": {
|
||||||
percent: 18,
|
percent: 22,
|
||||||
step: 1,
|
step: 1,
|
||||||
},
|
},
|
||||||
"license-plate-lookup": {
|
"license-plate-lookup": {
|
||||||
percent: 18,
|
percent: 22,
|
||||||
step: 1,
|
step: 1,
|
||||||
},
|
},
|
||||||
"address-lookup": {
|
"address-lookup": {
|
||||||
percent: 18,
|
percent: 22,
|
||||||
step: 1,
|
step: 1,
|
||||||
},
|
},
|
||||||
"address-vehicles": {
|
"address-vehicles": {
|
||||||
percent: 25,
|
percent: 26,
|
||||||
step: 1,
|
step: 1,
|
||||||
},
|
},
|
||||||
"part-questions": {
|
"part-questions": {
|
||||||
|
|
@ -49,7 +49,7 @@ export const pageProgressMapper = {
|
||||||
step: 1,
|
step: 1,
|
||||||
},
|
},
|
||||||
quote: {
|
quote: {
|
||||||
percent: 39, //Must be 39
|
percent: 39, //The first Step 2 must be 39 so the animated bar lines up with the dot
|
||||||
step: 2,
|
step: 2,
|
||||||
},
|
},
|
||||||
"insurance-company": {
|
"insurance-company": {
|
||||||
|
|
@ -61,7 +61,7 @@ export const pageProgressMapper = {
|
||||||
step: 2,
|
step: 2,
|
||||||
},
|
},
|
||||||
schedule: {
|
schedule: {
|
||||||
percent: 70.5, //Must be 70.5
|
percent: 70.5, //The first Step 3 must be 70.5 so the animated bar lines up with the dot
|
||||||
step: 3,
|
step: 3,
|
||||||
},
|
},
|
||||||
"mobile-details": {
|
"mobile-details": {
|
||||||
|
|
@ -73,7 +73,7 @@ export const pageProgressMapper = {
|
||||||
step: 3,
|
step: 3,
|
||||||
},
|
},
|
||||||
"payment-method": {
|
"payment-method": {
|
||||||
percent: 92, //Must be 92
|
percent: 92, //The first Step 4 must be 92 so the animated bar lines up with the dot
|
||||||
step: 3,
|
step: 3,
|
||||||
},
|
},
|
||||||
payment: {
|
payment: {
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@
|
||||||
<textBlock
|
<textBlock
|
||||||
v-show="durationTextBlockCopy"
|
v-show="durationTextBlockCopy"
|
||||||
:customText="durationTextBlockCopy"
|
:customText="durationTextBlockCopy"
|
||||||
justifyText="center"
|
|
||||||
typeStyle="small"
|
typeStyle="small"
|
||||||
marginTopSizeOverride="0"
|
marginTopSizeOverride="0"
|
||||||
class="duration-text-block" />
|
class="duration-text-block" />
|
||||||
|
|
@ -1027,6 +1026,10 @@ export default {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
|
.duration-text-block {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
.date-picker-header {
|
.date-picker-header {
|
||||||
margin-bottom: 1.5rem;
|
margin-bottom: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
@ -1050,7 +1053,6 @@ export default {
|
||||||
}
|
}
|
||||||
.calendar-grid-container {
|
.calendar-grid-container {
|
||||||
margin: 0 auto 1rem auto;
|
margin: 0 auto 1rem auto;
|
||||||
max-width: 360px;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
transition:
|
transition:
|
||||||
height ease 2s,
|
height ease 2s,
|
||||||
|
|
@ -1188,8 +1190,6 @@ export default {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
min-width: 2.5rem;
|
|
||||||
width: 2.5rem;
|
|
||||||
height: 2.5rem;
|
height: 2.5rem;
|
||||||
border: 2px solid transparent;
|
border: 2px solid transparent;
|
||||||
|
|
||||||
|
|
@ -1227,14 +1227,8 @@ export default {
|
||||||
&.selectable-day {
|
&.selectable-day {
|
||||||
label {
|
label {
|
||||||
background-color: $blue-100;
|
background-color: $blue-100;
|
||||||
min-width: 2.75rem;
|
|
||||||
width: 2.75rem;
|
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@include media-breakpoint-up(md) {
|
|
||||||
min-width: 3rem;
|
|
||||||
width: 3rem;
|
|
||||||
}
|
|
||||||
&:hover {
|
&:hover {
|
||||||
border: 2px solid $blue;
|
border: 2px solid $blue;
|
||||||
}
|
}
|
||||||
|
|
@ -1413,18 +1407,13 @@ export default {
|
||||||
line-height: 1.2;
|
line-height: 1.2;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: 0.25rem;
|
padding: 0.25rem;
|
||||||
|
margin: 0.15rem;
|
||||||
}
|
}
|
||||||
&.selectable-day {
|
&.selectable-day {
|
||||||
label {
|
label {
|
||||||
color: $black;
|
color: $black;
|
||||||
background-color: $blue-100;
|
background-color: $blue-100;
|
||||||
min-width: 2.75rem;
|
|
||||||
width: 2.75rem;
|
|
||||||
border-radius: 0.25rem;
|
border-radius: 0.25rem;
|
||||||
@include media-breakpoint-up(md) {
|
|
||||||
min-width: 3rem;
|
|
||||||
width: 3rem;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&.current-day {
|
&.current-day {
|
||||||
|
|
@ -1432,7 +1421,6 @@ export default {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-family: "UrbanistSemibold";
|
font-family: "UrbanistSemibold";
|
||||||
color: $black;
|
color: $black;
|
||||||
font-size-adjust: 0.5; // needed for Averta fonts baseline alignment
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1443,7 +1431,6 @@ export default {
|
||||||
color: $black;
|
color: $black;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-family: "UrbanistSemibold";
|
font-family: "UrbanistSemibold";
|
||||||
font-size-adjust: 0.5; // needed for Averta fonts baseline alignment
|
|
||||||
|
|
||||||
&.price {
|
&.price {
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
|
|
|
||||||
|
|
@ -317,10 +317,18 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.custom-secondary {
|
.custom-secondary {
|
||||||
&:hover {
|
border-radius: 0.5rem;
|
||||||
|
color: $blue;
|
||||||
|
border: 1px solid $blue;
|
||||||
|
&:hover,
|
||||||
|
&:focus,
|
||||||
|
&:active {
|
||||||
color: $blue;
|
color: $blue;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
border: 1px solid $blue;
|
border: 1px solid $blue;
|
||||||
|
box-shadow:
|
||||||
|
0 0 0 3px #ffffff,
|
||||||
|
0 0 0 5.5px $blue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -218,14 +218,17 @@ export default {
|
||||||
|
|
||||||
&.has-subheader {
|
&.has-subheader {
|
||||||
& > .package-specs {
|
& > .package-specs {
|
||||||
& > div:first-of-type {
|
display: inline-flex;
|
||||||
display: flex;
|
@include media-breakpoint-up(md) {
|
||||||
|
& > div:first-of-type {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.has-package-discount {
|
&.has-package-discount {
|
||||||
min-height: 150px;
|
min-height: 170px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:before {
|
&:before {
|
||||||
|
|
@ -267,10 +270,6 @@ export default {
|
||||||
+ .package-label {
|
+ .package-label {
|
||||||
.package-specs {
|
.package-specs {
|
||||||
max-height: 1000px;
|
max-height: 1000px;
|
||||||
|
|
||||||
.sub-label {
|
|
||||||
background-color: $green-200;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+ .package-label {
|
+ .package-label {
|
||||||
|
|
@ -281,11 +280,10 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+ .package-label {
|
+ .package-label {
|
||||||
background-color: $blue-100;
|
|
||||||
border: 1px solid $blue;
|
border: 1px solid $blue;
|
||||||
max-height: 500px;
|
max-height: 500px;
|
||||||
.special-save-box {
|
.special-save-box {
|
||||||
background-color: $green-200;
|
background-color: $green-100;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+ .package-label {
|
+ .package-label {
|
||||||
|
|
@ -336,12 +334,16 @@ export default {
|
||||||
|
|
||||||
&.sub-label {
|
&.sub-label {
|
||||||
color: $green;
|
color: $green;
|
||||||
background-color: $green-100;
|
|
||||||
padding: 0.25rem 0.5rem;
|
padding: 0.25rem 0;
|
||||||
border-radius: 0.75rem;
|
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
|
@include media-breakpoint-up(md) {
|
||||||
|
background-color: $green-100;
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
border-radius: 0.75rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
span {
|
span {
|
||||||
|
|
@ -429,14 +431,17 @@ export default {
|
||||||
background-color: $green-100;
|
background-color: $green-100;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
margin: 0.5rem auto 1rem -1.25rem;
|
margin: 0.25rem auto 0 -1.25rem;
|
||||||
padding: 0.5rem 1rem;
|
padding: 0.25rem 1rem;
|
||||||
border-radius: 0.25rem;
|
border-radius: 0.25rem;
|
||||||
span {
|
span {
|
||||||
color: $green;
|
color: $green;
|
||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
@include media-breakpoint-up(md) {
|
||||||
|
margin: 1rem auto 0 -1.25rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.pricing-info {
|
.pricing-info {
|
||||||
color: $green;
|
color: $green;
|
||||||
|
|
@ -447,6 +452,7 @@ export default {
|
||||||
margin-left: 1.25rem;
|
margin-left: 1.25rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
|
||||||
span.strikethrough-price {
|
span.strikethrough-price {
|
||||||
text-decoration: line-through;
|
text-decoration: line-through;
|
||||||
|
|
|
||||||
|
|
@ -318,7 +318,6 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+ .package-label {
|
+ .package-label {
|
||||||
background-color: $blue-100;
|
|
||||||
border: 1px solid $blue;
|
border: 1px solid $blue;
|
||||||
max-height: 500px;
|
max-height: 500px;
|
||||||
.special-save-box {
|
.special-save-box {
|
||||||
|
|
|
||||||
|
|
@ -19,10 +19,10 @@
|
||||||
v-if="showThisQuestionChain(questionsDatum, i)"
|
v-if="showThisQuestionChain(questionsDatum, i)"
|
||||||
:answerKey="questionsDatum.answerKey"
|
:answerKey="questionsDatum.answerKey"
|
||||||
:validationRules="validationRules" />
|
:validationRules="validationRules" />
|
||||||
|
<!-- Save your progress slot -->
|
||||||
|
<slot></slot>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<slot></slot>
|
|
||||||
|
|
||||||
<navbar
|
<navbar
|
||||||
ref="navbar"
|
ref="navbar"
|
||||||
cmsWidgetName="FunnelFooterWidget"
|
cmsWidgetName="FunnelFooterWidget"
|
||||||
|
|
@ -88,6 +88,12 @@ export default {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
.nav-bar {
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
.save-progress-modal-question {
|
||||||
|
margin: 2rem 0;
|
||||||
|
}
|
||||||
.questions-page {
|
.questions-page {
|
||||||
.question-text {
|
.question-text {
|
||||||
margin-bottom: 1.5rem;
|
margin-bottom: 1.5rem;
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
<buttonMain
|
<buttonMain
|
||||||
ref="buttonMain"
|
ref="buttonMain"
|
||||||
isPrimary
|
isPrimary
|
||||||
:buttonText="buttonText"
|
:buttonText="overrideButtonText || buttonText"
|
||||||
loaderColor="white"
|
loaderColor="white"
|
||||||
:class="isForwardActionDisabled && 'form-test-invalid'"
|
:class="isForwardActionDisabled && 'form-test-invalid'"
|
||||||
:aria-disabled="isForwardActionDisabled"
|
:aria-disabled="isForwardActionDisabled"
|
||||||
|
|
@ -28,7 +28,7 @@
|
||||||
<div
|
<div
|
||||||
v-if="!isBackButtonHidden"
|
v-if="!isBackButtonHidden"
|
||||||
class="col-auto link-col py-1 text-break"
|
class="col-auto link-col py-1 text-break"
|
||||||
:class="[buttonSize ? 'back-centered-below mt-5' : '']">
|
:class="[buttonSize ? 'mt-5' : '']">
|
||||||
<textLink
|
<textLink
|
||||||
linkType="navigation"
|
linkType="navigation"
|
||||||
:text="backLink"
|
:text="backLink"
|
||||||
|
|
@ -56,6 +56,7 @@ export default {
|
||||||
cmsWidgetName: String,
|
cmsWidgetName: String,
|
||||||
buttonSize: { type: Boolean, default: false },
|
buttonSize: { type: Boolean, default: false },
|
||||||
isSubmitHidden: { type: Boolean, default: false },
|
isSubmitHidden: { type: Boolean, default: false },
|
||||||
|
overrideButtonText: String,
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
textLink,
|
textLink,
|
||||||
|
|
@ -134,11 +135,6 @@ export default {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.back-centered-below {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (min-width: 340px) {
|
@media only screen and (min-width: 340px) {
|
||||||
.col,
|
.col,
|
||||||
.col-auto,
|
.col-auto,
|
||||||
|
|
@ -147,11 +143,6 @@ export default {
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
}
|
}
|
||||||
|
|
||||||
.back-centered-below {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-primary {
|
.btn-primary {
|
||||||
width: auto;
|
width: auto;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,13 @@
|
||||||
<template>
|
<template>
|
||||||
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm">
|
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm">
|
||||||
<div class="page-container-grouped-styles position-relative">
|
<loadingModal notFullScreen ref="loadingModal" />
|
||||||
<div>
|
<funnelHeader cmsWidgetName="FunnelHeaderWidget" ref="funnelHeader" />
|
||||||
<div class="container-fluid pb-2">
|
<div class="container page-container-grouped-styles">
|
||||||
<div class="row justify-content-center">
|
<div class="row">
|
||||||
<div class="col-md-6">
|
<div class="col-12 col-md-10 col-lg-8 col-xl-7">
|
||||||
<funnelHeader
|
<div class="header-container">
|
||||||
cmsWidgetName="FunnelHeaderWidget"
|
<img :src="ScheduleConfirmationImage" />
|
||||||
ref="funnelHeader"
|
<span v-html="ScheduleConfirmationText"></span>
|
||||||
hideSalesforceWebchatLaunchButton />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="row justify-content-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-md-6 col-xl-4">
|
<div class="col-md-6 col-xl-4">
|
||||||
|
|
@ -85,7 +83,63 @@
|
||||||
|
|
||||||
<hr class="mb-5" />
|
<hr class="mb-5" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<addToCalendar
|
||||||
|
mobileWidgetName="AddToCalendar_Mobile"
|
||||||
|
inShopWidgetName="AddToCalendar_InShop"
|
||||||
|
dropOffWidgetName="AddToCalendar_DropOff"
|
||||||
|
overnightDropOffWidgetName="AddToCalendar_OvernightDropOff"
|
||||||
|
allDayDropOffWidgetName="AddToCalendar_AllDayDropOff"
|
||||||
|
sameDayDropOffWidgetName="AddToCalendar_SameDayDropOff"
|
||||||
|
:serviceLocationFullAddress="ServiceLocationFullAddress"
|
||||||
|
:providerFullAddress="ProviderFullAddress"
|
||||||
|
:appointmentType="AppointmentType"
|
||||||
|
:scheduleDate="ScheduleDate"
|
||||||
|
:scheduleStartTime="ScheduleStartTime"
|
||||||
|
:scheduleEndTime="ScheduleEndTime" />
|
||||||
|
|
||||||
|
<div class="appointment-text" v-html="AppointmentWordingText"></div>
|
||||||
|
|
||||||
|
<textBlock
|
||||||
|
:customText="AppointmentDuration"
|
||||||
|
justifyText="center"
|
||||||
|
typeStyle="medium"
|
||||||
|
class="duration-text-block" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-3" v-if="shouldDisplayFosterLove">
|
||||||
|
<donationBlock
|
||||||
|
cmsWidgetName="DonationWidget"
|
||||||
|
v-model="donationAmount"
|
||||||
|
:donationValues="donationValues"
|
||||||
|
:showDonationSuccess="showDonationSuccess"
|
||||||
|
:showDonationError="showDonationError"
|
||||||
|
@DonationAddedEvent="addDonationToOrder"
|
||||||
|
ref="donationBlock" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr class="mt-5 mb-0" />
|
||||||
|
|
||||||
|
<cart
|
||||||
|
v-if="ShowCart"
|
||||||
|
:damage="damageInfo"
|
||||||
|
:availableVaps="vaps"
|
||||||
|
:allowItemRemoval="false"
|
||||||
|
v-model="lineItemsWithoutDonation"
|
||||||
|
:donationCartItem="donationLineItem"
|
||||||
|
:showAsPaid="isPia"
|
||||||
|
servicePackageOptionsCmsName="ServicePackageTitle"
|
||||||
|
:isInsurance="isInsurance"
|
||||||
|
:insuranceDeductible="currentDeductible"
|
||||||
|
:insuranceCompanyName="insuranceCompanyName"
|
||||||
|
:showInsuranceCoverageAs="showInsuranceCoverageAs"
|
||||||
|
:shouldHideRecalibration="shouldHideRecalibration"
|
||||||
|
:isExpandedOnLoad="false"
|
||||||
|
:isItac="isItac"
|
||||||
|
:isNoComp="isNoComp"
|
||||||
|
:isMSRFeeApplicable="isMSRFeeApplicable" />
|
||||||
|
|
||||||
|
<hr class="mt-4 mb-5" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -94,7 +148,6 @@
|
||||||
<script>
|
<script>
|
||||||
// Components
|
// Components
|
||||||
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 addToCalendar from "@/layouts/confirmation/add-to-calendar/add-to-calendar";
|
import addToCalendar from "@/layouts/confirmation/add-to-calendar/add-to-calendar";
|
||||||
import cart from "@/fmg-components/cart/cart";
|
import cart from "@/fmg-components/cart/cart";
|
||||||
import textBlock from "@/digital-components/text-block/text-block";
|
import textBlock from "@/digital-components/text-block/text-block";
|
||||||
|
|
@ -620,7 +673,8 @@ export default {
|
||||||
.header-container {
|
.header-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 1rem 0;
|
justify-content: flex-start;
|
||||||
|
padding: 1.5rem 0 0.5rem 0;
|
||||||
|
|
||||||
p {
|
p {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@
|
||||||
@ForwardClicked="forwardButtonAction" />
|
@ForwardClicked="forwardButtonAction" />
|
||||||
|
|
||||||
<textBlock
|
<textBlock
|
||||||
class="mb-5 disclaimer-block"
|
class="mb-5"
|
||||||
cmsWidgetName="DisclaimerCopyWidget"
|
cmsWidgetName="DisclaimerCopyWidget"
|
||||||
typeStyle="caption" />
|
typeStyle="caption" />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -215,10 +215,3 @@ export default {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.disclaimer-block {
|
|
||||||
position: relative;
|
|
||||||
z-index: 2;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@
|
||||||
</transition>
|
</transition>
|
||||||
<transition name="fade" mode="out-in">
|
<transition name="fade" mode="out-in">
|
||||||
<div class="row mb-4" aria-live="polite">
|
<div class="row mb-4" aria-live="polite">
|
||||||
<div class="col">
|
<div class="col-8">
|
||||||
<dropdownQuestion
|
<dropdownQuestion
|
||||||
customDropdownId="state"
|
customDropdownId="state"
|
||||||
cmsWidgetName="StateQuestionWidget"
|
cmsWidgetName="StateQuestionWidget"
|
||||||
|
|
@ -56,7 +56,7 @@
|
||||||
:labelBold="labelBold"
|
:labelBold="labelBold"
|
||||||
:isDisabled="this.isStateDisabled" />
|
:isDisabled="this.isStateDisabled" />
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="col-4">
|
||||||
<textboxQuestion
|
<textboxQuestion
|
||||||
customInputId="zipCode"
|
customInputId="zipCode"
|
||||||
cmsWidgetName="ZipQuestionWidget"
|
cmsWidgetName="ZipQuestionWidget"
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
<template>
|
<template>
|
||||||
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }">
|
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }">
|
||||||
<loadingModal ref="loadingModal" />
|
<loadingModal ref="loadingModal" />
|
||||||
<div class="container-fluid page-container-grouped-styles">
|
<funnelHeader cmsWidgetName="FunnelHeaderWidget" ref="funnelHeader" />
|
||||||
<div class="row justify-content-center">
|
<div class="container position-relative">
|
||||||
<div class="col-md-6">
|
<div class="row">
|
||||||
<funnelHeader cmsWidgetName="FunnelHeaderWidget" ref="funnelHeader" />
|
<div class="col-12 col-md-10 col-lg-8 col-xl-7">
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row justify-content-center">
|
|
||||||
<div class="col-md-6 col-xl-4 mt-4">
|
|
||||||
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" />
|
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" />
|
||||||
|
<hr class="my-3" />
|
||||||
|
<textBlock cmsWidgetName="AppointmentTimeOfService" class="time-header" />
|
||||||
|
<textBlock cmsWidgetName="AppointmentDateAndTime" />
|
||||||
|
<hr class="my-3" />
|
||||||
<div class="mobile-location-questions">
|
<div class="mobile-location-questions">
|
||||||
<div class="address-questions-container">
|
<div class="address-questions-container">
|
||||||
<mobileAddressQuestions
|
<mobileAddressQuestions
|
||||||
|
|
@ -150,8 +150,11 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
headerText() {
|
AppointmentDateAndTime() {
|
||||||
return this.getCmsContent("MobileLocationModalWidget", "HeaderText");
|
return this.getCmsContent("AppointmentDateAndTime", "Text");
|
||||||
|
},
|
||||||
|
AppointmentTimeOfService() {
|
||||||
|
return this.getCmsContent("AppointmentDateAndTime", "Text");
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
|
|
@ -166,3 +169,11 @@ export default {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.time-header {
|
||||||
|
font-family: UrbanistSemibold, Arial, Helvetica, sans-serif;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $black;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -963,5 +963,6 @@ function setupMocks({ customMountOptions }) {
|
||||||
|
|
||||||
const wrapper = shallowMount(quote, mountOptions);
|
const wrapper = shallowMount(quote, mountOptions);
|
||||||
wrapper.vm.setCmsContent = jest.fn();
|
wrapper.vm.setCmsContent = jest.fn();
|
||||||
|
wrapper.vm.getCmsContent = jest.fn();
|
||||||
return { wrapper };
|
return { wrapper };
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
<funnelHeader cmsWidgetName="FunnelHeaderWidget" ref="funnelHeader" />
|
<funnelHeader cmsWidgetName="FunnelHeaderWidget" ref="funnelHeader" />
|
||||||
<div class="container quote position-relative">
|
<div class="container quote position-relative">
|
||||||
<div class="row justify-content-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-12 col-md-10 col-lg-8 col-xl-7">
|
<div class="col-12">
|
||||||
<funnelSubHeader class="mb-5" cmsWidgetName="FunnelSubHeaderWidget" />
|
<funnelSubHeader class="mb-5" cmsWidgetName="FunnelSubHeaderWidget" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -78,7 +78,10 @@
|
||||||
:isForwardActionDisabled="!meta.valid"
|
:isForwardActionDisabled="!meta.valid"
|
||||||
:isBackButtonHidden="shouldHideBackButton"
|
:isBackButtonHidden="shouldHideBackButton"
|
||||||
@back-clicked="backButtonAction"
|
@back-clicked="backButtonAction"
|
||||||
@ForwardClicked="forwardButtonAction" />
|
@ForwardClicked="forwardButtonAction"
|
||||||
|
:overrideButtonText="
|
||||||
|
isInsuranceSelected ? isInsuranceContinueButtonText : ''
|
||||||
|
" />
|
||||||
|
|
||||||
<saveProgressModalQuestion
|
<saveProgressModalQuestion
|
||||||
modalWidgetName="SaveProgressModalWidget"
|
modalWidgetName="SaveProgressModalWidget"
|
||||||
|
|
@ -567,6 +570,9 @@ export default {
|
||||||
this.$refs?.servicePackage?.savePackageInfoToPageData?.();
|
this.$refs?.servicePackage?.savePackageInfoToPageData?.();
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
isInsuranceContinueButtonText() {
|
||||||
|
return this.getCmsContent("isInsuranceContinueButtonText", "Text");
|
||||||
|
},
|
||||||
lineItemsCloneForWatcher() {
|
lineItemsCloneForWatcher() {
|
||||||
return Object.assign({}, this.lineItems);
|
return Object.assign({}, this.lineItems);
|
||||||
},
|
},
|
||||||
|
|
@ -616,7 +622,7 @@ export default {
|
||||||
methods: {
|
methods: {
|
||||||
getColCount() {
|
getColCount() {
|
||||||
if (this.packageNumber > 2) {
|
if (this.packageNumber > 2) {
|
||||||
return "col-xl-10";
|
return "col-xl-12";
|
||||||
} else {
|
} else {
|
||||||
return "col-xl-6";
|
return "col-xl-6";
|
||||||
}
|
}
|
||||||
|
|
@ -927,6 +933,9 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
.funnel-sub-header {
|
.funnel-sub-header {
|
||||||
|
:deep(h5) {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
p {
|
p {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -246,7 +246,6 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+ .package-label {
|
+ .package-label {
|
||||||
background-color: $blue-100;
|
|
||||||
border: 1px solid $blue;
|
border: 1px solid $blue;
|
||||||
max-height: 500px;
|
max-height: 500px;
|
||||||
.special-save-box {
|
.special-save-box {
|
||||||
|
|
|
||||||
|
|
@ -117,4 +117,10 @@ export default {
|
||||||
margin-top: 14rem;
|
margin-top: 14rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
:deep(.menu-modal-container) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
:deep(.progress-bar-outer) {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -60,8 +60,8 @@
|
||||||
alertClass="alert-success" />
|
alertClass="alert-success" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row justify-content-center appointment-type">
|
<div class="row appointment-type">
|
||||||
<div class="col-md-6">
|
<div class="col-12 col-md-10 col-lg-8 col-xl-7">
|
||||||
<appointmentTypeQuestion
|
<appointmentTypeQuestion
|
||||||
v-model="appointmentTypeFromAppointmentTypeQuestion"
|
v-model="appointmentTypeFromAppointmentTypeQuestion"
|
||||||
v-show="isAppointmentTypeDisplayed"
|
v-show="isAppointmentTypeDisplayed"
|
||||||
|
|
@ -78,9 +78,9 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="row justify-content-center your-shop-location"
|
class="row your-shop-location"
|
||||||
v-if="appointmentType && appointmentType !== appointmentTypeStrings.MOBILE">
|
v-if="appointmentType && appointmentType !== appointmentTypeStrings.MOBILE">
|
||||||
<div class="col-md-6 col-xl-4">
|
<div class="col-12 col-md-10 col-lg-8 col-xl-7">
|
||||||
<button-question
|
<button-question
|
||||||
class="shop-question-button"
|
class="shop-question-button"
|
||||||
ref="buttonQuestion"
|
ref="buttonQuestion"
|
||||||
|
|
@ -91,9 +91,8 @@
|
||||||
:questionText="questionText"
|
:questionText="questionText"
|
||||||
buttonTypeString="shopListButton"
|
buttonTypeString="shopListButton"
|
||||||
:buttonTypeObject="shopListButton"
|
:buttonTypeObject="shopListButton"
|
||||||
groupName="chooseShop"
|
groupName="chooseShop" />
|
||||||
textPosition="text-start" />
|
<div>
|
||||||
<div class="text-center">
|
|
||||||
<textBlock
|
<textBlock
|
||||||
v-if="mobileFeeApplies && isMobileSelected"
|
v-if="mobileFeeApplies && isMobileSelected"
|
||||||
:customText="mobileFeeText"
|
:customText="mobileFeeText"
|
||||||
|
|
@ -113,8 +112,8 @@
|
||||||
<contentGroupModal ref="RecalModal" cmsWidgetName="RecalModal" />
|
<contentGroupModal ref="RecalModal" cmsWidgetName="RecalModal" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row justify-content-center date-picker-wrapper">
|
<div class="row date-picker-wrapper">
|
||||||
<div class="col-md-6 col-xl-4">
|
<div class="col-12 col-md-10 col-lg-8 col-xl-7">
|
||||||
<locationAlerts cmsWidgetPrefix="LocationAlert-" ref="locationAlerts" />
|
<locationAlerts cmsWidgetPrefix="LocationAlert-" ref="locationAlerts" />
|
||||||
<datePicker
|
<datePicker
|
||||||
:currentZip="zipCode"
|
:currentZip="zipCode"
|
||||||
|
|
@ -1169,7 +1168,7 @@ export default {
|
||||||
|
|
||||||
if (this.preSelectedDate) this.selectedDate = this.preSelectedDate;
|
if (this.preSelectedDate) this.selectedDate = this.preSelectedDate;
|
||||||
|
|
||||||
if (!this.selectedDate) {
|
if (!this.preSelectedDate) {
|
||||||
// if no date is preselected on load, then select the first available
|
// if no date is preselected on load, then select the first available
|
||||||
let selectedDateMobile = this.getSelectedDateForMobile();
|
let selectedDateMobile = this.getSelectedDateForMobile();
|
||||||
let selectedDateInshop = this.getSelectedDateForInshop();
|
let selectedDateInshop = this.getSelectedDateForInshop();
|
||||||
|
|
@ -1656,6 +1655,17 @@ export default {
|
||||||
AppointmentTypeStrings.IN_SHOP_OR_DROP_OFF) &&
|
AppointmentTypeStrings.IN_SHOP_OR_DROP_OFF) &&
|
||||||
this.selectedProvider
|
this.selectedProvider
|
||||||
) {
|
) {
|
||||||
|
this.selectedTimeSlotInfo = {
|
||||||
|
timeSlot: {
|
||||||
|
date: null,
|
||||||
|
routeCode: null,
|
||||||
|
startTime: null,
|
||||||
|
endTime: null,
|
||||||
|
jobMaxMinutes: null,
|
||||||
|
jobMinMinutes: null,
|
||||||
|
},
|
||||||
|
isPremiumAppointment: null,
|
||||||
|
};
|
||||||
this.lastSelectedInshopOrDropoffProvider = this.selectedProvider;
|
this.lastSelectedInshopOrDropoffProvider = this.selectedProvider;
|
||||||
}
|
}
|
||||||
this.appointmentType = AppointmentTypeStrings.MOBILE;
|
this.appointmentType = AppointmentTypeStrings.MOBILE;
|
||||||
|
|
@ -1737,7 +1747,7 @@ export default {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.container-fluid {
|
.container {
|
||||||
&.page-schedule {
|
&.page-schedule {
|
||||||
padding: 0 1rem;
|
padding: 0 1rem;
|
||||||
.text-link-small {
|
.text-link-small {
|
||||||
|
|
@ -1774,6 +1784,10 @@ export default {
|
||||||
padding: 0 0.75rem;
|
padding: 0 0.75rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.question-text > span {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.shop-question-button .list-button-content {
|
.shop-question-button .list-button-content {
|
||||||
background: $blue-100;
|
background: $blue-100;
|
||||||
|
|
|
||||||
|
|
@ -187,10 +187,14 @@ export default {
|
||||||
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
|
||||||
) {
|
) {
|
||||||
|
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
|
||||||
|
|
@ -205,6 +209,10 @@ export default {
|
||||||
// );
|
// );
|
||||||
// }
|
// }
|
||||||
} else {
|
} else {
|
||||||
|
if (!this.selectedAnswerForTimeSlots && this.selectedRouteCode) {
|
||||||
|
// Clearing selectedRouteCode if no time slot is selected
|
||||||
|
this.resetSelectedTimeSlot();
|
||||||
|
}
|
||||||
appointmentTypeCmsWidgetName = this.selectedRouteCode?.includes(
|
appointmentTypeCmsWidgetName = this.selectedRouteCode?.includes(
|
||||||
PREMIUM_TIME_SLOT_ID_FLAG
|
PREMIUM_TIME_SLOT_ID_FLAG
|
||||||
)
|
)
|
||||||
|
|
@ -394,6 +402,8 @@ export default {
|
||||||
this.setSelectedRouteCodeFromParent();
|
this.setSelectedRouteCodeFromParent();
|
||||||
},
|
},
|
||||||
async setSelectedTimeSlot() {
|
async setSelectedTimeSlot() {
|
||||||
|
this.selectedRouteCode =
|
||||||
|
this.selectedAnswerForTimeSlots || this.selectedAnswerForDropOffOrInshop;
|
||||||
this.$emit(
|
this.$emit(
|
||||||
"update:modelValue",
|
"update:modelValue",
|
||||||
this.getSelectedTimeSlotInfoObject(this.selectedRouteCode)
|
this.getSelectedTimeSlotInfoObject(this.selectedRouteCode)
|
||||||
|
|
@ -483,12 +493,13 @@ export default {
|
||||||
},
|
},
|
||||||
autoSelectTimeSlotIfOnlyOneIsAvailable() {
|
autoSelectTimeSlotIfOnlyOneIsAvailable() {
|
||||||
const numberOfOptions = this.timeSlotsForSelectedDate?.timeSlots?.length;
|
const numberOfOptions = this.timeSlotsForSelectedDate?.timeSlots?.length;
|
||||||
if (numberOfOptions === 1) {
|
if (this.appointmentType && numberOfOptions === 1) {
|
||||||
if (this.availableTimeSlots.length > 0) {
|
if (this.availableTimeSlots.length > 0) {
|
||||||
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) {
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,9 @@ export default {
|
||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
}
|
}
|
||||||
.vin-toggle {
|
.vin-toggle {
|
||||||
|
display: inline-flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
&:after {
|
&:after {
|
||||||
content: "";
|
content: "";
|
||||||
transition: all 0.5s ease;
|
transition: all 0.5s ease;
|
||||||
|
|
|
||||||
|
|
@ -135,7 +135,7 @@ export default {
|
||||||
outline: none;
|
outline: none;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
color: $white;
|
color: $white;
|
||||||
background: linear-gradient(84.45deg, #125b7e 0%, #3b8fb8 100%);
|
background: $blue;
|
||||||
border-radius: 0.5rem;
|
border-radius: 0.5rem;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue