diff --git a/.changeset/forty-clubs-mate.md b/.changeset/forty-clubs-mate.md new file mode 100644 index 0000000000..fc0d7c5884 --- /dev/null +++ b/.changeset/forty-clubs-mate.md @@ -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. diff --git a/docs/platform/atoms/availability-settings.mdx b/docs/platform/atoms/availability-settings.mdx index 79a7590e8e..fbbe8b75f3 100644 --- a/docs/platform/atoms/availability-settings.mdx +++ b/docs/platform/atoms/availability-settings.mdx @@ -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 diff --git a/docs/platform/atoms/event-type.mdx b/docs/platform/atoms/event-type.mdx index aa8ab19540..487080bde6 100644 --- a/docs/platform/atoms/event-type.mdx +++ b/docs/platform/atoms/event-type.mdx @@ -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 diff --git a/packages/features/eventtypes/lib/types.ts b/packages/features/eventtypes/lib/types.ts index 2092c1a256..2596f7756d 100644 --- a/packages/features/eventtypes/lib/types.ts +++ b/packages/features/eventtypes/lib/types.ts @@ -236,5 +236,5 @@ export type FormValidationResult = { export interface EventTypePlatformWrapperRef { validateForm: () => Promise; - handleFormSubmit: () => void; + handleFormSubmit: (callbacks?: { onSuccess?: () => void; onError?: (error: Error) => void }) => void; } diff --git a/packages/platform/atoms/availability/AvailabilitySettings.tsx b/packages/platform/atoms/availability/AvailabilitySettings.tsx index 40c471c7da..2192a48b00 100644 --- a/packages/platform/atoms/availability/AvailabilitySettings.tsx +++ b/packages/platform/atoms/availability/AvailabilitySettings.tsx @@ -316,11 +316,24 @@ export const AvailabilitySettings = forwardRef(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]); diff --git a/packages/platform/atoms/availability/types.ts b/packages/platform/atoms/availability/types.ts index 41fa969e80..8cdba22a34 100644 --- a/packages/platform/atoms/availability/types.ts +++ b/packages/platform/atoms/availability/types.ts @@ -35,7 +35,12 @@ export type AvailabilityFormValidationResult = { errors: Record; }; +export interface AvailabilitySettingsFormCallbacks { + onSuccess?: () => void; + onError?: (error: Error) => void; +} + export interface AvailabilitySettingsFormRef { validateForm: () => Promise; - handleFormSubmit: () => void; + handleFormSubmit: (callbacks?: AvailabilitySettingsFormCallbacks) => void; } diff --git a/packages/platform/atoms/availability/wrappers/AvailabilitySettingsPlatformWrapper.tsx b/packages/platform/atoms/availability/wrappers/AvailabilitySettingsPlatformWrapper.tsx index 9ebc101fd9..c4098ad482 100644 --- a/packages/platform/atoms/availability/wrappers/AvailabilitySettingsPlatformWrapper.tsx +++ b/packages/platform/atoms/availability/wrappers/AvailabilitySettingsPlatformWrapper.tsx @@ -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); }, }); diff --git a/packages/platform/atoms/event-types/wrappers/EventTypePlatformWrapper.tsx b/packages/platform/atoms/event-types/wrappers/EventTypePlatformWrapper.tsx index d589e7d731..8a77635e44 100644 --- a/packages/platform/atoms/event-types/wrappers/EventTypePlatformWrapper.tsx +++ b/packages/platform/atoms/event-types/wrappers/EventTypePlatformWrapper.tsx @@ -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(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]); diff --git a/packages/platform/examples/base/src/pages/availability.tsx b/packages/platform/examples/base/src/pages/availability.tsx index 5fdb1d0cfc..3477bd256f 100644 --- a/packages/platform/examples/base/src/pages/availability.tsx +++ b/packages/platform/examples/base/src/pages/availability.tsx @@ -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

Availability Settings

-
+