Files
calendar/apps/web/modules/onboarding/hooks/useOnboardingCalendarEvents.ts
T
sean-brydonandGitHub dc3742d322 chore: Add calendar weekly view enhancements and welcome modal feature (#24948)
## What does this PR do?

- Adds a welcome modal for new Cal.com users
- Implements timezone display in the weekly calendar view
- Creates a hook for fetching onboarding calendar events

## Visual Demo (For contributors especially)

#### Image Demo:

![Welcome Modal](https://user-images.githubusercontent.com/1234567/example-welcome-modal.png)

![Timezone Display](https://user-images.githubusercontent.com/1234567/example-timezone-display.png)

## Mandatory Tasks

- [x] I have self-reviewed the code
- [x] I have updated the developer docs in /docs
- [x] I confirm automated tests are in place that prove my fix is effective or that my feature works.

## How should this be tested?

1. **Welcome Modal:**
   - Create a new user account
   - Verify the welcome modal appears with correct content
   - Test the "Continue" button closes the modal
   - Check that the modal can be triggered via URL parameter `?welcomeToCalcomModal=true`

2. **Timezone Display:**
   - Go to the weekly calendar view
   - Verify the timezone is displayed correctly when `showTimezone` is enabled
   - Test with different timezones to ensure proper formatting

3. **Onboarding Calendar Events:**
   - Test the hook by connecting a calendar during onboarding
   - Verify events are fetched and displayed correctly
   - Check that events refresh when new calendars are connected

## Checklist

- I have read the [contributing guide](https://github.com/calcom/cal.com/blob/main/CONTRIBUTING.md)
- My code follows the style guidelines of this project
- I have commented my code, particularly in hard-to-understand areas
- I have checked if my changes generate no new warnings
2025-11-11 10:47:22 +00:00

80 lines
2.4 KiB
TypeScript

import { useSession } from "next-auth/react";
import { useMemo, useEffect } from "react";
import dayjs from "@calcom/dayjs";
import type { GetUserAvailabilityResult } from "@calcom/features/availability/lib/getUserAvailability";
import type { CalendarEvent } from "@calcom/features/calendars/weeklyview/types/events";
import { BookingStatus } from "@calcom/prisma/enums";
import { trpc } from "@calcom/trpc/react";
type UseOnboardingCalendarEventsProps = {
startDate: Date;
endDate: Date;
};
const emptyAvailabilityData = {
busy: [],
timeZone: "",
dateRanges: [],
oooExcludedDateRanges: [],
workingHours: [],
dateOverrides: [],
currentSeats: null,
datesOutOfOffice: {},
} as GetUserAvailabilityResult;
export const useOnboardingCalendarEvents = ({ startDate, endDate }: UseOnboardingCalendarEventsProps) => {
const { data: session } = useSession();
const utils = trpc.useUtils();
// Watch for calendar installations to refetch events
const { data: connectedCalendars } = trpc.viewer.calendars.connectedCalendars.useQuery(undefined, {
refetchInterval: 5000, // Poll every 5 seconds to detect new calendar installations
});
const { data: busyEvents } = trpc.viewer.availability.user.useQuery(
{
username: session?.user?.username || "",
dateFrom: dayjs(startDate).startOf("day").utc().format(),
dateTo: dayjs(endDate).endOf("day").utc().format(),
withSource: true,
},
{
enabled: !!session?.user?.username,
// Don't show loading state - return empty array immediately
placeholderData: emptyAvailabilityData,
}
);
// Refetch availability when calendars change
useEffect(() => {
if (connectedCalendars?.connectedCalendars) {
utils.viewer.availability.user.invalidate();
}
}, [connectedCalendars?.connectedCalendars?.length, utils.viewer.availability.user]);
// Format events similar to Troubleshooter
// Always return an array, never undefined
const events = useMemo((): CalendarEvent[] => {
if (!busyEvents?.busy) return [];
return busyEvents.busy.map((event, idx) => {
return {
id: idx,
title: event.title ?? `Busy`,
start: new Date(event.start),
end: new Date(event.end),
options: {
color: event.source ? undefined : undefined,
status: BookingStatus.ACCEPTED,
},
};
});
}, [busyEvents]);
return {
events,
isLoading: false, // Never show loading state
};
};