feat: Autofill feature (#15437)
* feat: Autofill feature * Revert "feat: Autofill feature" This reverts commit ac94257cbb516e140ed550ae804e725ca5d036ca. * feat: store prev response in localStorage * review fixes * add tests * remove unused import --------- Co-authored-by: Hariom Balhara <hariombalhara@gmail.com> Co-authored-by: unknown <adhabal2002@gmail.com>
This commit is contained in:
co-authored by
Hariom Balhara
unknown
parent
7b94822c8d
commit
8194ffcd07
@@ -22,6 +22,7 @@ export interface IUseBookingForm {
|
||||
guests: string[];
|
||||
name: string | null;
|
||||
};
|
||||
lastBookingResponse?: Record<string, string>;
|
||||
}
|
||||
|
||||
export type UseBookingFormReturnType = ReturnType<typeof useBookingForm>;
|
||||
@@ -34,6 +35,7 @@ export const useBookingForm = ({
|
||||
hasSession,
|
||||
extraOptions,
|
||||
prefillFormParams,
|
||||
lastBookingResponse,
|
||||
}: IUseBookingForm) => {
|
||||
const rescheduleUid = useBookerStore((state) => state.rescheduleUid);
|
||||
const bookingData = useBookerStore((state) => state.bookingData);
|
||||
@@ -71,6 +73,7 @@ export const useBookingForm = ({
|
||||
hasSession,
|
||||
extraOptions,
|
||||
prefillFormParams,
|
||||
lastBookingResponse,
|
||||
});
|
||||
|
||||
const bookingForm = useForm<BookingFormValues>({
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import type { z } from "zod";
|
||||
import { z } from "zod";
|
||||
|
||||
import { useBookerStore } from "@calcom/features/bookings/Booker/store";
|
||||
import type getBookingResponsesSchema from "@calcom/features/bookings/lib/getBookingResponsesSchema";
|
||||
@@ -21,6 +21,7 @@ type UseInitialFormValuesProps = {
|
||||
guests: string[];
|
||||
name: string | null;
|
||||
};
|
||||
lastBookingResponse?: Record<string, string>;
|
||||
};
|
||||
|
||||
export function useInitialFormValues({
|
||||
@@ -33,6 +34,7 @@ export function useInitialFormValues({
|
||||
hasSession,
|
||||
extraOptions,
|
||||
prefillFormParams,
|
||||
lastBookingResponse,
|
||||
}: UseInitialFormValuesProps) {
|
||||
const [initialValues, setDefaultValues] = useState<{
|
||||
responses?: Partial<z.infer<ReturnType<typeof getBookingResponsesSchema>>>;
|
||||
@@ -62,6 +64,7 @@ export function useInitialFormValues({
|
||||
// `guests` because the `name` of the corresponding bookingField is `guests`
|
||||
guests: prefillFormParams.guests,
|
||||
});
|
||||
const parsedLastBookingResponse = z.record(z.any()).nullish().parse(lastBookingResponse);
|
||||
|
||||
const defaultUserValues = {
|
||||
email:
|
||||
@@ -69,13 +72,13 @@ export function useInitialFormValues({
|
||||
? bookingData?.attendees[0].email
|
||||
: !!parsedQuery["email"]
|
||||
? parsedQuery["email"]
|
||||
: email ?? "",
|
||||
: email ?? parsedLastBookingResponse?.email ?? "",
|
||||
name:
|
||||
rescheduleUid && bookingData && bookingData.attendees.length > 0
|
||||
? bookingData?.attendees[0].name
|
||||
: !!parsedQuery["name"]
|
||||
? parsedQuery["name"]
|
||||
: name ?? username ?? "",
|
||||
: name ?? username ?? parsedLastBookingResponse?.name ?? "",
|
||||
};
|
||||
|
||||
if (!isRescheduling) {
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
import { describe, it, expect, beforeEach, vi } from "vitest";
|
||||
|
||||
import { localStorage } from "@calcom/lib/webstorage";
|
||||
|
||||
import { setLastBookingResponse, getLastBookingResponse } from "./lastBookingResponse";
|
||||
|
||||
vi.mock("@calcom/lib/webstorage", () => ({
|
||||
localStorage: {
|
||||
getItem: vi.fn(),
|
||||
setItem: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
describe("Last Booking Response", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
describe("setLastBookingResponse", () => {
|
||||
it("should not store anything if responses is null", () => {
|
||||
setLastBookingResponse(null);
|
||||
expect(localStorage.setItem).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should store only allowed responses in localStorage", () => {
|
||||
const responses = {
|
||||
email: "test@example.com",
|
||||
name: "John Doe",
|
||||
age: 30,
|
||||
};
|
||||
|
||||
setLastBookingResponse(responses);
|
||||
|
||||
expect(localStorage.setItem).toHaveBeenCalledWith(
|
||||
"lastBookingResponse",
|
||||
JSON.stringify({ email: "test@example.com", name: "John Doe" })
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getLastBookingResponse", () => {
|
||||
it("should return an empty object if there is no last booking response", () => {
|
||||
(localStorage.getItem as jest.Mock).mockReturnValue(null);
|
||||
|
||||
const result = getLastBookingResponse();
|
||||
expect(result).toEqual({});
|
||||
});
|
||||
|
||||
it("should return parsed response if valid JSON is present", () => {
|
||||
const mockResponse = JSON.stringify({ email: "test@example.com", name: "John Doe" });
|
||||
(localStorage.getItem as jest.Mock).mockReturnValue(mockResponse);
|
||||
|
||||
const result = getLastBookingResponse();
|
||||
expect(result).toEqual({ email: "test@example.com", name: "John Doe" });
|
||||
});
|
||||
|
||||
it("should return an empty object if JSON is invalid", () => {
|
||||
const invalidJson = "{ email: 'test@example.com' ";
|
||||
(localStorage.getItem as jest.Mock).mockReturnValue(invalidJson);
|
||||
|
||||
const result = getLastBookingResponse();
|
||||
expect(result).toEqual({});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,22 @@
|
||||
import logger from "@calcom/lib/logger";
|
||||
import { localStorage } from "@calcom/lib/webstorage";
|
||||
|
||||
const responsesToStore = ["email", "name"];
|
||||
|
||||
export const setLastBookingResponse = (responses: Record<string, unknown> | null) => {
|
||||
if (!responses) return;
|
||||
const prevResponse = Object.fromEntries(
|
||||
Object.entries(responses).filter(([key]) => responsesToStore.includes(key))
|
||||
);
|
||||
localStorage.setItem("lastBookingResponse", JSON.stringify(prevResponse));
|
||||
};
|
||||
|
||||
export const getLastBookingResponse = () => {
|
||||
const lastBookingResponse = localStorage.getItem("lastBookingResponse");
|
||||
try {
|
||||
return JSON.parse(lastBookingResponse ?? "{}");
|
||||
} catch (err) {
|
||||
logger.error("Error parsing lastBookingResponse: ", err);
|
||||
return {};
|
||||
}
|
||||
};
|
||||
@@ -19,6 +19,7 @@ import { useVerifyCode } from "@calcom/features/bookings/Booker/components/hooks
|
||||
import { useVerifyEmail } from "@calcom/features/bookings/Booker/components/hooks/useVerifyEmail";
|
||||
import { useBookerStore, useInitializeBookerStore } from "@calcom/features/bookings/Booker/store";
|
||||
import { useEvent, useScheduleForEvent } from "@calcom/features/bookings/Booker/utils/event";
|
||||
import { getLastBookingResponse } from "@calcom/features/bookings/Booker/utils/lastBookingResponse";
|
||||
import { useBrandColors } from "@calcom/features/bookings/Booker/utils/use-brand-colors";
|
||||
import { DEFAULT_LIGHT_BRAND_COLOR, DEFAULT_DARK_BRAND_COLOR, WEBAPP_URL } from "@calcom/lib/constants";
|
||||
import { useRouterQuery } from "@calcom/lib/hooks/useRouterQuery";
|
||||
@@ -85,6 +86,8 @@ export const BookerWebWrapper = (props: BookerWebWrapperAtomProps) => {
|
||||
};
|
||||
}, [searchParams, firstNameQueryParam, lastNameQueryParam]);
|
||||
|
||||
const lastBookingResponse = getLastBookingResponse();
|
||||
|
||||
const bookerForm = useBookingForm({
|
||||
event: event.data,
|
||||
sessionEmail: session?.user.email,
|
||||
@@ -93,6 +96,7 @@ export const BookerWebWrapper = (props: BookerWebWrapperAtomProps) => {
|
||||
hasSession,
|
||||
extraOptions: routerQuery,
|
||||
prefillFormParams,
|
||||
lastBookingResponse,
|
||||
});
|
||||
const calendars = useCalendars({ hasSession });
|
||||
const verifyEmail = useVerifyEmail({
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { UseBookingFormReturnType } from "@calcom/features/bookings/Booker/components/hooks/useBookingForm";
|
||||
import { useBookerStore } from "@calcom/features/bookings/Booker/store";
|
||||
import { setLastBookingResponse } from "@calcom/features/bookings/Booker/utils/lastBookingResponse";
|
||||
import {
|
||||
useTimePreferences,
|
||||
mapBookingToMutationInput,
|
||||
@@ -74,6 +75,8 @@ export const useHandleBookEvent = ({
|
||||
? duration
|
||||
: event.data.length;
|
||||
|
||||
setLastBookingResponse(values.responses);
|
||||
|
||||
const bookingInput = {
|
||||
values,
|
||||
duration: validDuration,
|
||||
|
||||
Reference in New Issue
Block a user