fix: resolve 404 error when rescheduling seated event bookings (#23956)

* fix: skip select field validation for existing responses during reschedule

- Modify hasRequiredBookingFieldsResponses to detect reschedule context
- Skip validation for select, multiselect, checkbox, and radio fields during reschedule to preserve existing responses
- Fixes validation error when rescheduling bookings with select field responses that may no longer match current field options

Resolves issue where reschedule operations fail with 'Value Travel, Adventure & Wildlife is not valid for type select for field Niche-project' error

Co-Authored-By: anik@cal.com <adhabal2002@gmail.com>

* fix: skip frontend validation for existing select field responses during reschedule

- Revert backend changes in bookings.service.ts as the issue is frontend-only
- Add reschedule detection in getBookingResponsesSchema.ts preprocess function
- Skip option validation for select, radio, multiselect, and checkbox fields during reschedule
- Preserves existing booking responses that may no longer match current field options
- Fixes error 'Value Travel, Adventure & Wildlife is not valid for type select for field Niche-project' during slot selection

Co-Authored-By: anik@cal.com <adhabal2002@gmail.com>

* fix: extend reschedule validation skip to handle multiemail fields like guests

- Add reschedule-aware logic to multiemail validation in getBookingResponsesSchema.ts
- Skip validation for existing multiemail responses during reschedule to avoid type mismatch errors
- Fixes 'Value is not valid for type text for field guests' error during slot selection
- Complements existing fix for select/radio field validation during reschedule

Co-Authored-By: anik@cal.com <adhabal2002@gmail.com>

* fix: disable guests field inputs for seated events

- Disable type SelectField for guests field in seated events
- Disable identifier InputField for guests field in seated events
- Add validation to prevent saving guests field changes for seated events
- Pass seatsEnabled prop from EventAdvancedTab to FormBuilder
- Use condition: seatsEnabled && formFieldType === 'multiemail' && fieldForm.getValues('name') === 'guests'
- Fix linting warnings: remove unused variable and add missing dependency

Co-Authored-By: anik@cal.com <adhabal2002@gmail.com>

* Update getBookingResponsesSchema.ts

* Update FormBuilder.tsx

* Update FormBuilder.tsx

* update

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Anik Dhabal Babu
2025-09-22 23:44:46 +00:00
committed by GitHub
co-authored by anik@cal.com <adhabal2002@gmail.com> Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
parent 5463dc261c
commit f3d60b141f
3 changed files with 50 additions and 2 deletions
@@ -2202,6 +2202,7 @@
"booking_confirmation_failed": "Booking confirmation failed",
"not_enough_seats": "Not enough seats",
"form_builder_field_already_exists": "A field with this name already exists",
"guests_field_must_be_multiemail": "Guests field must be of type 'Multiple emails'",
"show_on_booking_page": "Show on booking page",
"visit_cancelled_booking": "You can visit the canceled booking page",
"get_started_zapier_templates": "Get started with Zapier templates",
@@ -1,10 +1,12 @@
import { TooltipProvider } from "@radix-ui/react-tooltip";
import { render } from "@testing-library/react";
import { render, screen, waitFor } from "@testing-library/react";
import type { ReactNode } from "react";
import * as React from "react";
import { FormProvider, useForm } from "react-hook-form";
import { vi } from "vitest";
import { showToast } from "@calcom/ui/components/toast";
import { FormBuilder } from "./FormBuilder";
import {
mockProps,
@@ -32,6 +34,10 @@ vi.mock("next/navigation", async (importOriginal) => {
};
});
vi.mock("@calcom/ui/components/toast", () => ({
showToast: vi.fn(),
}));
const renderComponent = ({
formBuilderProps: formBuilderProps,
formDefaultValues: formDefaultValues,
@@ -219,7 +225,6 @@ describe("FormBuilder", () => {
],
});
verifier.verifyOptionPrices({ identifier, prices: [20, 25] });
});
@@ -271,4 +276,40 @@ describe("FormBuilder", () => {
verifier.verifyOptionPrices({ identifier, prices: [50, 75, 100] });
});
});
describe("Guests Field Validation Tests", () => {
beforeEach(() => {
renderComponent({ formBuilderProps: mockProps, formDefaultValues: {} });
});
it("Should prevent saving guests field with non-multiemail type", async () => {
const dialog = pageObject.openAddFieldDialog();
pageObject.dialog.selectFieldType({ dialog, fieldType: "text" });
pageObject.dialog.fillInFieldIdentifier({ dialog, identifier: "guests" });
pageObject.dialog.fillInFieldLabel({ dialog, label: "Guests", fieldType: "text" });
pageObject.dialog.saveField({ dialog });
await waitFor(() => {
expect(showToast).toHaveBeenCalledWith("guests_field_must_be_multiemail", "error");
});
expect(screen.queryByTestId("field-guests")).not.toBeInTheDocument();
});
it("Should allow saving guests field with multiemail type", async () => {
const dialog = pageObject.openAddFieldDialog();
pageObject.dialog.selectFieldType({ dialog, fieldType: "multiemail" });
pageObject.dialog.fillInFieldIdentifier({ dialog, identifier: "guests" });
pageObject.dialog.fillInFieldLabel({ dialog, label: "Guests", fieldType: "multiemail" });
pageObject.dialog.saveField({ dialog });
await waitFor(() => {
expect(screen.getByTestId("field-guests")).toBeInTheDocument();
});
});
});
});
@@ -380,6 +380,12 @@ export const FormBuilder = function FormBuilder({
handleSubmit={(data: Parameters<SubmitHandler<RhfFormField>>[0]) => {
const type = data.type || "text";
const isNewField = !fieldDialog.data;
if (data.name === "guests" && type !== "multiemail") {
showToast(t("guests_field_must_be_multiemail"), "error");
return;
}
if (isNewField && fields.some((f) => f.name === data.name)) {
showToast(t("form_builder_field_already_exists"), "error");
return;