Files
calendar/packages/platform/atoms/availability/wrappers/AvailabilitySettingsPlatformWrapper.tsx
Benny JooGitHubDevin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
c7ee3984ef refactor: move feature-flags, calendars, and schedules components from packages/features to apps/web/modules (#27222)
* refactor: move feature-flags, calendars, and schedules components from packages/features to apps/web/modules

Co-Authored-By: [email protected] <[email protected]>

* fix

* fix

* fix

* fix: move DatePicker and DateOverrideInputDialog to apps/web/modules and update imports

Co-Authored-By: [email protected] <[email protected]>

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* rename

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* wip

* wip

* wip

* fix unit tests

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-01-29 16:41:44 +02:00

211 lines
6.4 KiB
TypeScript

import type { ReactNode } from "react";
import { forwardRef, useRef } from "react";
import type { ScheduleLabelsType } from "@calcom/features/schedules/components/ScheduleComponent";
import type { UpdateScheduleResponse } from "@calcom/features/schedules/services/ScheduleService";
import type { ApiErrorResponse, ApiResponse, UpdateScheduleInput_2024_06_11 } from "@calcom/platform-types";
import { useAtomSchedule } from "../../hooks/schedules/useAtomSchedule";
import { useAtomUpdateSchedule } from "../../hooks/schedules/useAtomUpdateSchedule";
import useDeleteSchedule from "../../hooks/schedules/useDeleteSchedule";
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 } from "../AvailabilitySettings";
import { AvailabilitySettings } from "../AvailabilitySettings";
import type { AvailabilityFormValues, AvailabilitySettingsFormRef } from "../types";
export type AvailabilitySettingsPlatformWrapperProps = {
id?: string;
labels?: {
tooltips: Partial<ScheduleLabelsType>;
};
customClassNames?: Partial<CustomClassNames>;
onUpdateSuccess?: (res: ApiResponse<UpdateScheduleResponse>) => void;
onUpdateError?: (err: ApiErrorResponse) => void;
onDeleteSuccess?: (res: ApiResponse) => void;
onDeleteError?: (err: ApiErrorResponse) => void;
disableEditableHeading?: boolean;
enableOverrides?: boolean;
onBeforeUpdate?: (updateBody: UpdateScheduleInput_2024_06_11) => boolean | Promise<boolean>;
onFormStateChange?: (formState: AvailabilityFormValues) => void;
allowDelete?: boolean;
allowSetToDefault?: boolean;
disableToasts?: boolean;
isDryRun?: boolean;
noScheduleChildren?: ReactNode;
loadingStateChildren?: ReactNode;
};
export const AvailabilitySettingsPlatformWrapper = forwardRef<
AvailabilitySettingsFormRef,
AvailabilitySettingsPlatformWrapperProps
>(function AvailabilitySettingsPlatformWrapper(props, ref) {
const {
id,
customClassNames,
onDeleteError,
onDeleteSuccess,
onUpdateError,
onUpdateSuccess,
disableEditableHeading = false,
enableOverrides = false,
onBeforeUpdate,
onFormStateChange,
allowDelete,
allowSetToDefault,
disableToasts,
isDryRun = false,
noScheduleChildren,
loadingStateChildren,
} = props;
const { isLoading, data: atomSchedule } = useAtomSchedule(id);
const { data: me } = useMe();
const { timeFormat } = me?.data || { timeFormat: null };
const { toast } = useToast();
const { mutate: deleteSchedule, isPending: isDeletionInProgress } = useDeleteSchedule({
onSuccess: (res) => {
onDeleteSuccess?.(res);
if (!disableToasts) {
toast({
description: "Schedule deleted successfully",
});
}
},
onError: (err) => {
onDeleteError?.(err);
if (!disableToasts) {
toast({
description: "Could not delete schedule",
});
}
},
});
const callbacksRef = useRef<{ onSuccess?: () => void; onError?: (error: Error) => void }>({});
const { mutate: updateSchedule, isPending: isSavingInProgress } = useAtomUpdateSchedule({
onSuccess: (res) => {
onUpdateSuccess?.(res);
if (!disableToasts) {
toast({
description: "Schedule updated successfully",
});
}
callbacksRef.current?.onSuccess?.();
},
onError: (err) => {
onUpdateError?.(err);
if (!disableToasts) {
toast({
description: "Could not update schedule",
});
}
callbacksRef.current?.onError?.(err);
},
});
const handleDelete = async (id: number) => {
await deleteSchedule({ id });
};
const handleUpdate = async (id: number, body: AvailabilityFormValues) => {
let canUpdate = true;
if (onBeforeUpdate) {
canUpdate = await onBeforeUpdate(body);
}
if (canUpdate) {
updateSchedule({
scheduleId: id,
body: {
...body,
dateOverrides: body.dateOverrides.flatMap((override) => override.ranges),
},
});
}
};
if (isLoading) {
return (
<>
{loadingStateChildren ? loadingStateChildren : <div className="px-10 py-4 text-xl">Loading...</div>}
</>
);
}
if (!atomSchedule) {
return noScheduleChildren ? (
<>{noScheduleChildren}</>
) : (
<div className="px-10 py-4 text-xl">No user schedule present</div>
);
}
return (
<AtomsWrapper>
<AvailabilitySettings
ref={ref}
disableEditableHeading={disableEditableHeading}
handleDelete={() => {
if (isDryRun) {
toast({
description: "Schedule deleted successfully",
});
}
if (!isDryRun && atomSchedule.id) {
handleDelete(atomSchedule.id);
}
}}
handleSubmit={async (data) => {
if (isDryRun) {
toast({
description: "Schedule updated successfully",
});
}
if (!isDryRun && atomSchedule.id) {
handleUpdate(atomSchedule.id, data);
}
}}
weekStart={me?.data?.weekStart || "Sunday"}
timeFormat={timeFormat}
enableOverrides={enableOverrides}
isLoading={isLoading}
schedule={{
name: atomSchedule.name,
id: atomSchedule.id,
isLastSchedule: atomSchedule.isLastSchedule,
isDefault: atomSchedule.isDefault,
workingHours: atomSchedule.workingHours,
dateOverrides: atomSchedule.dateOverrides,
timeZone: atomSchedule.timeZone,
availability: atomSchedule.availability,
schedule:
atomSchedule.schedule.reduce(
(acc: Availability[], avail: Availability) => [
...acc,
{ days: avail.days, startTime: new Date(avail.startTime), endTime: new Date(avail.endTime) },
],
[]
) || [],
}}
isDeleting={isDeletionInProgress}
isSaving={isSavingInProgress}
backPath=""
isPlatform={true}
customClassNames={customClassNames}
allowDelete={allowDelete}
allowSetToDefault={allowSetToDefault}
onFormStateChange={onFormStateChange}
callbacksRef={callbacksRef}
isDryRun={isDryRun}
/>
</AtomsWrapper>
);
});