Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add date overrides to availability settings atom #15459

24 changes: 11 additions & 13 deletions packages/features/schedules/components/DateOverrideInputDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -108,11 +108,18 @@ const DateOverrideForm = ({
<Form
form={form}
handleSubmit={(values) => {
if (selectedDates.length === 0) return;

const datesInRanges: TimeRange[] = [];

if (!datesUnavailable) {
if (selectedDates.length === 0) return;

if (datesUnavailable) {
selectedDates.map((date) => {
datesInRanges.push({
start: date.utc(true).startOf("day").toDate(),
end: date.utc(true).startOf("day").toDate(),
});
});
} else {
selectedDates.map((date) => {
values.range.map((item) => {
datesInRanges.push({
Expand All @@ -127,16 +134,7 @@ const DateOverrideForm = ({
});
}

onChange(
datesUnavailable
? selectedDates.map((date) => {
return {
start: date.utc(true).startOf("day").toDate(),
end: date.utc(true).startOf("day").toDate(),
};
})
: datesInRanges
);
onChange(datesInRanges);
setSelectedDates([]);
}}
className="p-6 sm:flex sm:p-0 xl:flex-row">
Expand Down
31 changes: 13 additions & 18 deletions packages/platform/atoms/availability/AvailabilitySettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ type AvailabilitySettingsProps = {
isPlatform?: boolean;
customClassNames?: CustomClassNames;
disableEditableHeading?: boolean;
enableOverrides?: boolean;
};

const DeleteDialogButton = ({
Expand Down Expand Up @@ -234,6 +235,7 @@ export function AvailabilitySettings({
isPlatform = false,
customClassNames,
disableEditableHeading = false,
enableOverrides = false,
}: AvailabilitySettingsProps) {
const [openSidebar, setOpenSidebar] = useState(false);
const { t, i18n } = useLocale();
Expand All @@ -248,7 +250,6 @@ export function AvailabilitySettings({
useEffect(() => {
const subscription = form.watch(
(value, { name }) => {
console.log(name);
if (!!name && name.split(".")[0] !== "schedule" && name !== "name")
handleSubmit(value as AvailabilityFormValues);
},
Expand Down Expand Up @@ -507,23 +508,17 @@ export function AvailabilitySettings({
)}
</div>
</div>
{!isPlatform ? (
<div className="border-subtle my-6 rounded-md border">
{schedule?.workingHours && (
<DateOverride
workingHours={schedule.workingHours}
userTimeFormat={timeFormat}
travelSchedules={travelSchedules}
weekStart={
["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"].indexOf(
weekStart
) as 0 | 1 | 2 | 3 | 4 | 5 | 6
}
/>
)}
</div>
) : (
<></>
{enableOverrides && (
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

enableOverrides prop allows users to enable or disable date overrides inside an atom

<DateOverride
workingHours={schedule.workingHours}
userTimeFormat={timeFormat}
travelSchedules={travelSchedules}
weekStart={
["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"].indexOf(
weekStart
) as 0 | 1 | 2 | 3 | 4 | 5 | 6
}
/>
)}
</div>
<div className="min-w-40 col-span-3 hidden space-y-2 md:block lg:col-span-1">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,40 @@ export function transformAtomScheduleForApi(body: AvailabilityFormValues): Updat
const overrides =
dateOverrides.flatMap(
(dateOverridesRanges) =>
dateOverridesRanges?.ranges?.map((range) => ({
date: `${range.start.getUTCFullYear}-${range.start.getUTCMonth}-${range.start.getUTCDate}`,
startTime: `${range.start.getUTCHours}-${range.start.getUTCMinutes}`,
endTime: `${range.end.getUTCHours}-${range.end.getUTCMinutes}`,
})) ?? []
dateOverridesRanges?.ranges?.map((range) => transfromAtomOverrideForApi(range)) ?? []
) ?? [];

const availability = formatScheduleTime(schedule);

return { name, timeZone, isDefault, availability, overrides };
}

type AtomDateOverride = {
start: Date;
end: Date;
};

function transfromAtomOverrideForApi(override: AtomDateOverride) {
const date = `${override.start.getUTCFullYear()}-${(override.start.getUTCMonth() + 1)
.toString()
.padStart(2, "0")}-${override.start.getUTCDate().toString().padStart(2, "0")}`;

return {
date,
startTime: padHoursMinutesWithZeros(`${override.start.getUTCHours()}:${override.start.getUTCMinutes()}`),
endTime: padHoursMinutesWithZeros(`${override.end.getUTCHours()}:${override.end.getUTCMinutes()}`),
};
}

function padHoursMinutesWithZeros(hhMM: string) {
const [hours, minutes] = hhMM.split(":");

const formattedHours = hours.padStart(2, "0");
const formattedMinutes = minutes.padStart(2, "0");

return `${formattedHours}:${formattedMinutes}`;
}

function formatScheduleTime(
weekSchedule: AvailabilityFormValues["schedule"]
): UpdateScheduleInput_2024_06_11["availability"] {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ type AvailabilitySettingsPlatformWrapperProps = {
onDeleteSuccess?: (res: ApiResponse) => void;
onDeleteError?: (err: ApiErrorResponse) => void;
disableEditableHeading?: boolean;
enableOverrides?: boolean;
};

export const AvailabilitySettingsPlatformWrapper = ({
Expand All @@ -36,6 +37,7 @@ export const AvailabilitySettingsPlatformWrapper = ({
onUpdateError,
onUpdateSuccess,
disableEditableHeading = false,
enableOverrides = false,
}: AvailabilitySettingsPlatformWrapperProps) => {
const { isLoading, data: schedule } = useSchedule(id);
const { data: schedules } = useSchedules();
Expand Down Expand Up @@ -99,6 +101,7 @@ export const AvailabilitySettingsPlatformWrapper = ({
}}
weekStart="Sunday"
timeFormat={timeFormat}
enableOverrides={enableOverrides}
isLoading={isLoading}
schedule={{
name: atomSchedule.name,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ export const AvailabilitySettingsWebWrapper = () => {
isDeleting={deleteMutation.isPending}
isLoading={isPending}
isSaving={updateMutation.isPending}
enableOverrides={true}
timeFormat={timeFormat}
weekStart={me.data?.weekStart || "Sunday"}
backPath={fromEventType ? true : "/availability"}
Expand Down
32 changes: 18 additions & 14 deletions packages/platform/atoms/src/components/ui/dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,20 +33,24 @@ const DialogContent = React.forwardRef<
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
>(({ className, children, ...props }, ref) => (
<>
<DialogOverlay />
<DialogPrimitive.Content
ref={ref}
className={cn(
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border p-6 shadow-lg duration-200 sm:rounded-lg",
className
)}
{...props}>
{children}
<DialogPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute right-4 top-4 rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:pointer-events-none">
<Icon name="x" className="h-4 w-4" />
<span className="sr-only">Close</span>
</DialogPrimitive.Close>
</DialogPrimitive.Content>
<DialogPortal>
<div className="calcom-atoms">
<DialogOverlay />
<DialogPrimitive.Content
ref={ref}
className={cn(
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border p-6 shadow-lg duration-200 sm:rounded-lg",
className
)}
{...props}>
{children}
<DialogPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute right-4 top-4 rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:pointer-events-none">
<Icon name="x" className="h-4 w-4" />
<span className="sr-only">Close</span>
</DialogPrimitive.Close>
</DialogPrimitive.Content>
</div>
</DialogPortal>
</>
));
DialogContent.displayName = DialogPrimitive.Content.displayName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export default function Availability(props: { calUsername: string; calEmail: str
<Navbar username={props.calUsername} />
<div>
<AvailabilitySettings
enableOverrides={true}
customClassNames={{
subtitlesClassName: "text-red-500",
ctaClassName: "border p-4 rounded-md",
Expand Down
Loading