feat: add callback for handleFormSubmit in EventType and AvailabilitySettings atoms (#22911)

* feat: Implement callback pattern in handleFormSubmit of EventTypePlatformWrapper

* feat: Implement callback pattern in handleFormSubmit of AvailabilitySettingsPlatformWrapper

* docs: add handleFormSubmit callback documentation for EventType and AvailabilitySettings atoms

* Update availability.tsx

* chore:added changelog

* update docs

---------

Co-authored-by: Rajiv Sahal <sahalrajiv-extc@atharvacoe.ac.in>
This commit is contained in:
Somay Chauhan
2025-08-07 10:26:09 +02:00
committed by GitHub
co-authored by Rajiv Sahal
parent cfda4757e4
commit d3fbc73a39
10 changed files with 129 additions and 17 deletions
+9
View File
@@ -0,0 +1,9 @@
---
"@calcom/atoms": minor
---
Added new callback functions to the handleFormSubmit method in the EventTypeSettings and AvailabilitySettings atoms. The handleFormSubmit method now accepts an optional callbacks object with the following properties:
- **onSuccess**: Called when the form submission is successful, allowing additional logic to be executed after the update.
- **onError**: Called when an error occurs during form submission, providing details about the error to handle specific cases or display custom messages.
+24 -1
View File
@@ -156,7 +156,16 @@ export function AvailabilityWithValidation() {
};
const handleSubmit = () => {
availabilityRef.current?.handleFormSubmit();
availabilityRef.current?.handleFormSubmit({
onSuccess: () => {
// Additional success handling logic here
console.log('Availability updated successfully');
},
onError: (error) => {
// Additional error handling logic here
console.error('Error updating availability:', error);
}
});
};
return (
@@ -184,6 +193,20 @@ export function AvailabilityWithValidation() {
| validateForm | Validates the current availability form state and returns a promise with validation results. |
| handleFormSubmit | Programmatically submits the availability form, triggering the same validation and submission flow as clicking the save button. Unlike `validateForm`, this method will check required fields and prevent submission unless all required fields are set |
### Callbacks
The `handleFormSubmit` method accepts an optional callbacks object with the following properties:
```typescript
type AvailabilitySettingsFormCallbacks = {
onSuccess?: () => void;
onError?: (error: Error) => void;
};
```
- **onSuccess**: Called when the form submission is successful. This allows you to execute additional logic after a successful update.
- **onError**: Called when an error occurs during form submission. The error parameter contains details about what went wrong, allowing you to handle specific error cases or display custom error messages.
The `validateForm` method returns an `AvailabilityFormValidationResult` object with:
- `isValid`: Boolean indicating if the form passed validation
- `errors`: Object containing any validation errors found
+24 -1
View File
@@ -218,7 +218,16 @@ export function EventTypeWithValidation(eventTypeId: number) {
};
const handleSubmit = () => {
eventTypeRef.current?.handleFormSubmit();
eventTypeRef.current?.handleFormSubmit({
onSuccess: () => {
// Additional success handling logic here
console.log('Event type updated successfully');
},
onError: (error) => {
// Additional error handling logic here
console.error('Error updating event type:', error);
}
});
};
return (
@@ -244,6 +253,20 @@ export function EventTypeWithValidation(eventTypeId: number) {
| validateForm | Validates the current event type form state and returns a promise with validation results. |
| handleFormSubmit | Programmatically submits the event type form, triggering the same validation and submission flow as clicking the save button. Unlike `validateForm`, this method will check required fields and prevent submission unless all required fields are set |
### Callbacks
The `handleFormSubmit` method accepts an optional callbacks object with the following properties:
```typescript
type EventTypeFormCallbacks = {
onSuccess?: () => void;
onError?: (error: Error) => void;
};
```
- **onSuccess**: Called when the form submission is successful. This allows you to execute additional logic after a successful update.
- **onError**: Called when an error occurs during form submission. The error parameter contains details about what went wrong, allowing you to handle specific error cases or display custom error messages.
The `validateForm` method returns an `EventTypeFormValidationResult` object with:
- `isValid`: Boolean indicating if the form passed validation
- `errors`: Object containing any validation errors found
+1 -1
View File
@@ -236,5 +236,5 @@ export type FormValidationResult = {
export interface EventTypePlatformWrapperRef {
validateForm: () => Promise<FormValidationResult>;
handleFormSubmit: () => void;
handleFormSubmit: (callbacks?: { onSuccess?: () => void; onError?: (error: Error) => void }) => void;
}
@@ -316,11 +316,24 @@ export const AvailabilitySettings = forwardRef<AvailabilitySettingsFormRef, Avai
const saveButtonRef = useRef<HTMLButtonElement>(null);
const handleFormSubmit = useCallback(() => {
const callbacksRef = useRef<{ onSuccess?: () => void; onError?: (error: Error) => void }>({});
const handleFormSubmit = useCallback((customCallbacks?: { onSuccess?: () => void; onError?: (error: Error) => void }) => {
if (customCallbacks) {
callbacksRef.current = customCallbacks;
}
if (saveButtonRef.current) {
saveButtonRef.current.click();
} else {
form.handleSubmit(handleSubmit)();
form.handleSubmit(async (data) => {
try {
await handleSubmit(data);
callbacksRef.current?.onSuccess?.();
} catch (error) {
callbacksRef.current?.onError?.(error as Error);
}
})();
}
}, [form, handleSubmit]);
@@ -35,7 +35,12 @@ export type AvailabilityFormValidationResult = {
errors: Record<string, unknown>;
};
export interface AvailabilitySettingsFormCallbacks {
onSuccess?: () => void;
onError?: (error: Error) => void;
}
export interface AvailabilitySettingsFormRef {
validateForm: () => Promise<AvailabilityFormValidationResult>;
handleFormSubmit: () => void;
handleFormSubmit: (callbacks?: AvailabilitySettingsFormCallbacks) => void;
}
@@ -1,5 +1,5 @@
import type { ReactNode } from "react";
import { forwardRef } from "react";
import { forwardRef, useRef } from "react";
import type { ScheduleLabelsType } from "@calcom/features/schedules/components/Schedule";
import type { UpdateScheduleResponse } from "@calcom/lib/schedules/updateSchedule";
@@ -12,9 +12,9 @@ import { useMe } from "../../hooks/useMe";
import { AtomsWrapper } from "../../src/components/atoms-wrapper";
import { useToast } from "../../src/components/ui/use-toast";
import type { Availability } from "../AvailabilitySettings";
import type { CustomClassNames, AvailabilitySettingsFormRef } from "../AvailabilitySettings";
import type { CustomClassNames } from "../AvailabilitySettings";
import { AvailabilitySettings } from "../AvailabilitySettings";
import type { AvailabilityFormValues } from "../types";
import type { AvailabilityFormValues, AvailabilitySettingsFormRef } from "../types";
export type AvailabilitySettingsPlatformWrapperProps = {
id?: string;
@@ -39,7 +39,7 @@ export type AvailabilitySettingsPlatformWrapperProps = {
};
export const AvailabilitySettingsPlatformWrapper = forwardRef<
AvailabilitySettingsFormRef,
AvailabilitySettingsFormRef,
AvailabilitySettingsPlatformWrapperProps
>(function AvailabilitySettingsPlatformWrapper(props, ref) {
const {
@@ -84,6 +84,8 @@ export const AvailabilitySettingsPlatformWrapper = forwardRef<
},
});
const callbacksRef = useRef<{ onSuccess?: () => void; onError?: (error: Error) => void }>({});
const { mutate: updateSchedule, isPending: isSavingInProgress } = useAtomUpdateSchedule({
onSuccess: (res) => {
onUpdateSuccess?.(res);
@@ -92,6 +94,7 @@ export const AvailabilitySettingsPlatformWrapper = forwardRef<
description: "Schedule updated successfully",
});
}
callbacksRef.current?.onSuccess?.();
},
onError: (err) => {
onUpdateError?.(err);
@@ -100,6 +103,7 @@ export const AvailabilitySettingsPlatformWrapper = forwardRef<
description: "Could not update schedule",
});
}
callbacksRef.current?.onError?.(err);
},
});
@@ -139,6 +139,7 @@ const EventType = forwardRef<
form.reset(currentValues);
toast({ description: t("event_type_updated_successfully", { eventTypeTitle: eventType.title }) });
onSuccess?.(currentValues);
callbacksRef.current?.onSuccess?.();
},
async onSettled() {
return;
@@ -146,8 +147,12 @@ const EventType = forwardRef<
onError: (err: Error) => {
const currentValues = form.getValues();
const message = err?.message;
toast({ description: message ? t(message) : t(err.message) });
const description = message ? t(message) : t(err.message);
toast({ description });
onError?.(currentValues, err);
const errorObj = new Error(description);
callbacksRef.current?.onError?.(errorObj);
},
teamId: team?.id,
});
@@ -159,6 +164,7 @@ const EventType = forwardRef<
updateMutation.mutate(data);
} else {
toast({ description: t("event_type_updated_successfully", { eventTypeTitle: eventType.title }) });
callbacksRef.current?.onSuccess?.();
}
},
onFormStateChange: onFormStateChange,
@@ -167,11 +173,24 @@ const EventType = forwardRef<
// Create a ref for the save button to trigger its click
const saveButtonRef = useRef<HTMLButtonElement>(null);
const handleFormSubmit = useCallback(() => {
const callbacksRef = useRef<{ onSuccess?: () => void; onError?: (error: Error) => void }>({});
const handleFormSubmit = useCallback((customCallbacks?: { onSuccess?: () => void; onError?: (error: Error) => void }) => {
if (customCallbacks) {
callbacksRef.current = customCallbacks;
}
if (saveButtonRef.current) {
saveButtonRef.current.click();
} else {
form.handleSubmit(handleSubmit)();
form.handleSubmit((data) => {
try {
handleSubmit(data);
customCallbacks?.onSuccess?.();
} catch (error) {
customCallbacks?.onError?.(error as Error);
}
})();
}
}, [handleSubmit, form]);
@@ -1,6 +1,6 @@
import { Navbar } from "@/components/Navbar";
import { Inter } from "next/font/google";
import { useRef, useCallback } from "react";
import { useRef, useCallback, useState } from "react";
import type { AvailabilitySettingsFormRef } from "@calcom/atoms";
import { AvailabilitySettings } from "@calcom/atoms";
@@ -20,7 +20,14 @@ export default function Availability(props: { calUsername: string; calEmail: str
};
const handleSubmit = () => {
availabilityRef.current?.handleFormSubmit();
availabilityRef.current?.handleFormSubmit({
onSuccess: () => {
console.log("Form submitted successfully");
},
onError: (error) => {
console.error("Form submission failed:", error);
},
});
};
return (
@@ -29,7 +36,7 @@ export default function Availability(props: { calUsername: string; calEmail: str
<div>
<h1 className="mx-10 my-4 text-2xl font-semibold">Availability Settings</h1>
<div className="mx-10 mb-4 flex gap-4">
<div className="mx-10 mb-4 flex flex-col gap-4">
<button
onClick={handleValidate}
className="rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50">
@@ -31,7 +31,16 @@ export default function Bookings(props: { calUsername: string; calEmail: string
};
const handleSubmit = () => {
eventTypeRef.current?.handleFormSubmit();
eventTypeRef.current?.handleFormSubmit({
onSuccess: () => {
console.log('Event type updated successfully');
// Additional success handling logic here
},
onError: (error) => {
console.error('Error updating event type:', error);
// Additional error handling logic here
}
});
};
const { isLoading: isLoadingEvents, data: eventTypes, refetch } = useEventTypes(props.calUsername);
const { data: teams } = useTeams();