More dry run fixes (#18865)

This commit is contained in:
Hariom Balhara
2025-02-20 15:51:28 +05:30
committed by GitHub
parent ec55454e43
commit b06a94ab57
8 changed files with 34 additions and 4 deletions
@@ -42,7 +42,9 @@ export default function DryRunSuccessful() {
<div className="bg-success mx-auto mb-4 flex h-12 w-12 items-center justify-center rounded-full">
<Icon name="check" className="h-6 w-6 text-green-600 dark:text-green-400" />
</div>
<h1 className="text-emphasis mb-4 text-2xl font-medium">{t("booking_dry_run_successful")}</h1>
<h1 className="text-emphasis mb-4 text-2xl font-medium" data-testid="dry-run-success-msg">
{t("booking_dry_run_successful")}
</h1>
<p className="text-default mb-8 max-w-2xl text-sm">{t("booking_dry_run_successful_description")}</p>
{/* <div className="border-subtle text-default mt-8 grid grid-cols-3 border-t pt-8 text-left">
+4
View File
@@ -4,6 +4,8 @@ import Head from "next/head";
import type { inferSSRProps } from "@calcom/types/inferSSRProps";
import PageWrapper from "@components/PageWrapper";
import { getServerSideProps } from "../../server/lib/router/getServerSideProps";
export default function Router({ form, message }: inferSSRProps<typeof getServerSideProps>) {
@@ -23,4 +25,6 @@ export default function Router({ form, message }: inferSSRProps<typeof getServer
);
}
Router.PageWrapper = PageWrapper;
export { getServerSideProps };
+16
View File
@@ -0,0 +1,16 @@
1. Navigate to Headless Router - acme.cal.com/router?formId={FORM_ID}&field1=value1&field2=value2
1. Validate the fields types and ensure required fields are present
2. Based on the fields values, choose a route.
3. Records a response for the form.
4. If the route is an eventTypeRedirect,
- Identify the teamMembers matching that route's attribute routing rules.
- Redirect to the booking page of the eventType, along with the `routedTeamMemberIds` query param that has the matching teamMembers' ids.
5. Notify through emails if they are members to be notified and fire Routing form related webhooks.
2. User reaches the booking page.
3. Sees the available slots for the matching teamMembers only
4. Selects a slot.
- Selecting a slot temporarily blocks the slot for other bookers.
5. Confirm the booking
1. Ensures that the members are available for the slot depending on various rules like type of event(RR, Collective) and other configurations in the eventType and selected calendars
2. Send Emails and fire webhooks
@@ -94,6 +94,7 @@ export function getFieldResponse({
/**
* Not called in preview mode or dry run mode
* It takes care of sending webhooks and emails for form submissions
*/
export async function onFormSubmission(
form: Ensure<
@@ -19,7 +19,9 @@ export const DryRunMessage = ({ isEmbed }: { isEmbed?: boolean }) => {
<div className="relative">
<Icon name="info" className="h-5 w-5 text-orange-500" />
</div>
<div className="text-emphasis font-medium">{t("dry_run_mode_active")}</div>
<div className="text-emphasis font-medium" data-testid="dry-run-msg">
{t("dry_run_mode_active")}
</div>
</div>
</div>
);
@@ -4,14 +4,17 @@ import { shallow } from "zustand/shallow";
import dayjs from "@calcom/dayjs";
import { useBookerStore } from "@calcom/features/bookings/Booker/store";
import { useSlotReservationId } from "@calcom/features/bookings/Booker/useSlotReservationId";
import { isBookingDryRun } from "@calcom/features/bookings/Booker/utils/isBookingDryRun";
import type { BookerEvent } from "@calcom/features/bookings/types";
import { MINUTES_TO_BOOK } from "@calcom/lib/constants";
import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams";
import { trpc } from "@calcom/trpc";
export type UseSlotsReturnType = ReturnType<typeof useSlots>;
export const useSlots = (event: { data?: Pick<BookerEvent, "id" | "length"> | null }) => {
const selectedDuration = useBookerStore((state) => state.selectedDuration);
const searchParams = useCompatSearchParams();
const [selectedTimeslot, setSelectedTimeslot] = useBookerStore(
(state) => [state.selectedTimeslot, state.setSelectedTimeslot],
shallow
@@ -45,6 +48,7 @@ export const useSlots = (event: { data?: Pick<BookerEvent, "id" | "length"> | nu
.utc()
.add(selectedDuration || event.data.length, "minutes")
.format(),
_isDryRun: isBookingDryRun(searchParams),
});
}
};
@@ -21,7 +21,7 @@ interface ReserveSlotOptions {
export const reserveSlotHandler = async ({ ctx, input }: ReserveSlotOptions) => {
const { prisma, req, res } = ctx;
const uid = req?.cookies?.uid || uuid();
const { slotUtcStartDate, slotUtcEndDate, eventTypeId, bookingUid } = input;
const { slotUtcStartDate, slotUtcEndDate, eventTypeId, bookingUid, _isDryRun } = input;
const releaseAt = dayjs.utc().add(parseInt(MINUTES_TO_BOOK), "minutes").format();
const eventType = await prisma.eventType.findUnique({
where: { id: eventTypeId },
@@ -54,7 +54,7 @@ export const reserveSlotHandler = async ({ ctx, input }: ReserveSlotOptions) =>
}
}
if (eventType && shouldReserveSlot) {
if (eventType && shouldReserveSlot && !_isDryRun) {
try {
await Promise.all(
eventType.users.map((user) =>
@@ -56,6 +56,7 @@ export const reserveSlotSchema = z
// endTime ISOString
slotUtcEndDate: z.string(),
bookingUid: z.string().optional(),
_isDryRun: z.boolean().optional(),
})
.refine(
(data) => !!data.eventTypeId || !!data.slotUtcStartDate || !!data.slotUtcEndDate,