* fix * move event types components to web * update import paths * mv apps components * migrate form builder * fix * mv sso * fix * mv * update import paths * update import paths * mv * mv * mv * fix * update Booker * fix * fix * fix * fix * mv video * mv embed components to web * update import paths * mv calendar weekly view components * update import paths * fix * fixp * fix * fix * fix * fix: update FormBuilder imports to use @calcom/features/form-builder Co-Authored-By: benny@cal.com <sldisek783@gmail.com> * fix: update broken import paths after file migrations Co-Authored-By: benny@cal.com <sldisek783@gmail.com> * fix: correct import paths for platform atoms and moved components Co-Authored-By: benny@cal.com <sldisek783@gmail.com> * fix: apply CSS type fixes and add missing atoms exports Co-Authored-By: benny@cal.com <sldisek783@gmail.com> * fix: resolve type errors in test files after component migrations Co-Authored-By: benny@cal.com <sldisek783@gmail.com> * fix: resolve remaining type errors in test files Co-Authored-By: benny@cal.com <sldisek783@gmail.com> * fix * migrate * fix: resolve type errors in test and mock files - Add missing bookingForm, bookerFormErrorRef, instantConnectCooldownMs to Booker.test.tsx bookings prop - Add all required BookerEvent properties to event.mock.ts - Add vi import from vitest to all mock files - Fix date parameter types in packages/dayjs/__mocks__/index.ts - Add verificationCode and setVerificationCode to test-utils.tsx mock store - Remove children.type access in Section.tsx mock to fix type error - Fix lint issues: remove unused React imports, use import type where needed, add return types - Add biome-ignore comments for pre-existing lint warnings in test files Co-Authored-By: benny@cal.com <sldisek783@gmail.com> * migrate * migrate * migrate * update import paths * update import paths * update import paths * fix * migrate data table * migrate data table * fix * fix * fix * migrate insights components * migrate insights components * fix * mv * update import paths * fix * fix * fix * fix * fix * fix: resolve type errors in test mocks - Booker.test.tsx: Add all required UseFormReturn methods to bookingForm mock - event.mock.ts: Fix entity, subsetOfHosts, instantMeetingParameters, fieldTranslations, image types - dayjs/__mocks__/index.ts: Use Object.assign for proper typing of mock properties - Section.tsx: Change 'class' to 'className' in JSX with biome-ignore comment Co-Authored-By: benny@cal.com <sldisek783@gmail.com> * fix: add missing hasDataErrors and dataErrors to bookings.errors mock Co-Authored-By: benny@cal.com <sldisek783@gmail.com> * fix: add missing loadingStates properties to bookings mock Co-Authored-By: benny@cal.com <sldisek783@gmail.com> * fix: add missing slots properties (setTentativeSelectedTimeslots, tentativeSelectedTimeslots, slotReservationId) Co-Authored-By: benny@cal.com <sldisek783@gmail.com> * fix: update quickAvailabilityChecks to include utcEndIso and use valid status type Co-Authored-By: benny@cal.com <sldisek783@gmail.com> * fix: add missing bookerForm properties (formName, beforeVerifyEmail, formErrors) Co-Authored-By: benny@cal.com <sldisek783@gmail.com> * fix: add missing UseFormReturn properties to bookerForm.bookingForm mock Co-Authored-By: benny@cal.com <sldisek783@gmail.com> * fix: add hasFormErrors and formErrors to bookerForm.formErrors mock Co-Authored-By: benny@cal.com <sldisek783@gmail.com> * fix: add hasFormErrors and formErrors to bookerForm.errors mock Co-Authored-By: benny@cal.com <sldisek783@gmail.com> * fix: add missing isError property to mockEvent Co-Authored-By: benny@cal.com <sldisek783@gmail.com> * fix: use complete BookerEvent mock in Booker.test.tsx Co-Authored-By: benny@cal.com <sldisek783@gmail.com> * fix: use branded bookingFields type in event mock Co-Authored-By: benny@cal.com <sldisek783@gmail.com> * fix: add missing schedule mock properties (isError, isSuccess, isLoading, dataUpdatedAt) Co-Authored-By: benny@cal.com <sldisek783@gmail.com> * revert * fix * fix * fix * fix build * fix * fix * fix * fix: correct AddMembersWithSwitch test wrapper to use initial assignAllTeamMembers value - Fixed test wrapper to initialize useState with componentProps.assignAllTeamMembers instead of hardcoded false, allowing tests to properly test different states - Updated test expectations for ALL_TEAM_MEMBERS_ENABLED_AND_SEGMENT_NOT_APPLICABLE state to match actual component behavior (toggle should be present and checked) - Fixed 'should show Segment when toggled on' test to start with assignAllTeamMembers: false to properly test the flow of enabling it - Added explicit types to satisfy biome lint requirements Co-Authored-By: benny@cal.com <sldisek783@gmail.com> * fix: use JSX.Element instead of React.JSX.Element for type compatibility Co-Authored-By: benny@cal.com <sldisek783@gmail.com> --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
183 lines
5.8 KiB
TypeScript
183 lines
5.8 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, Suspense } from "react";
|
|
|
|
import { InstallAppButton } from "@calcom/app-store/InstallAppButton";
|
|
import { DestinationCalendarSettingsWebWrapper } from "@calcom/atoms/destination-calendar/wrappers/DestinationCalendarSettingsWebWrapper";
|
|
import { SelectedCalendarsSettingsWebWrapper } from "@calcom/atoms/selected-calendars/wrappers/SelectedCalendarsSettingsWebWrapper";
|
|
import AppListCard from "@calcom/web/modules/apps/components/AppListCard";
|
|
import { SkeletonLoader } from "@calcom/web/modules/apps/components/SkeletonLoader";
|
|
import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader";
|
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
|
import { trpc } from "@calcom/trpc/react";
|
|
import type { RouterOutputs } from "@calcom/trpc/react";
|
|
import { Button } from "@calcom/ui/components/button";
|
|
import { EmptyScreen } from "@calcom/ui/components/empty-screen";
|
|
import { ShellSubHeading } from "@calcom/ui/components/layout";
|
|
import { List } from "@calcom/ui/components/list";
|
|
import { showToast } from "@calcom/ui/components/toast";
|
|
import { revalidateSettingsCalendars } from "@calcom/web/app/cache/path/settings/my-account";
|
|
|
|
import { QueryCell } from "@lib/QueryCell";
|
|
import useRouterQuery from "@lib/hooks/useRouterQuery";
|
|
|
|
import SubHeadingTitleWithConnections from "@components/integrations/SubHeadingTitleWithConnections";
|
|
|
|
type Props = {
|
|
onChanged: () => unknown | Promise<unknown>;
|
|
fromOnboarding?: boolean;
|
|
destinationCalendarId?: string;
|
|
isPending?: boolean;
|
|
};
|
|
|
|
function CalendarList(props: Props) {
|
|
const { t } = useLocale();
|
|
const query = trpc.viewer.apps.integrations.useQuery({ variant: "calendar", onlyInstalled: false });
|
|
|
|
return (
|
|
<QueryCell
|
|
query={query}
|
|
success={({ data }) => (
|
|
<List>
|
|
{data.items.map((item) => (
|
|
<AppListCard
|
|
title={item.name}
|
|
key={item.name}
|
|
logo={item.logo}
|
|
description={item.description}
|
|
shouldHighlight
|
|
slug={item.slug}
|
|
actions={
|
|
<InstallAppButton
|
|
type={item.type}
|
|
render={(buttonProps) => (
|
|
<Button color="secondary" {...buttonProps}>
|
|
{t("connect")}
|
|
</Button>
|
|
)}
|
|
onChanged={() => props.onChanged()}
|
|
/>
|
|
}
|
|
/>
|
|
))}
|
|
</List>
|
|
)}
|
|
/>
|
|
);
|
|
}
|
|
|
|
const AddCalendarButton = () => {
|
|
const { t } = useLocale();
|
|
return (
|
|
<>
|
|
<Button color="secondary" StartIcon="plus" href="/apps/categories/calendar">
|
|
{t("add_calendar")}
|
|
</Button>
|
|
</>
|
|
);
|
|
};
|
|
|
|
export const CalendarListContainerSkeletonLoader = () => {
|
|
const { t } = useLocale();
|
|
return (
|
|
<SettingsHeader
|
|
title={t("calendars")}
|
|
description={t("calendars_description")}
|
|
CTA={<AddCalendarButton />}>
|
|
<SkeletonLoader />
|
|
</SettingsHeader>
|
|
);
|
|
};
|
|
|
|
type CalendarListContainerProps = {
|
|
connectedCalendars: RouterOutputs["viewer"]["calendars"]["connectedCalendars"];
|
|
installedCalendars: RouterOutputs["viewer"]["apps"]["integrations"];
|
|
heading?: boolean;
|
|
fromOnboarding?: boolean;
|
|
};
|
|
|
|
export function CalendarListContainer({
|
|
connectedCalendars: data,
|
|
installedCalendars,
|
|
heading = true,
|
|
fromOnboarding,
|
|
}: CalendarListContainerProps) {
|
|
const { t } = useLocale();
|
|
const { error, setQuery: setError } = useRouterQuery("error");
|
|
|
|
useEffect(() => {
|
|
if (error === "account_already_linked" || error === "no_default_calendar") {
|
|
showToast(t(error), "error", { id: error });
|
|
setError(undefined);
|
|
}
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, []);
|
|
const utils = trpc.useUtils();
|
|
const onChanged = () =>
|
|
Promise.allSettled([
|
|
utils.viewer.apps.integrations.invalidate(
|
|
{ variant: "calendar", onlyInstalled: true },
|
|
{
|
|
exact: true,
|
|
}
|
|
),
|
|
utils.viewer.calendars.connectedCalendars.invalidate(),
|
|
revalidateSettingsCalendars(),
|
|
]);
|
|
|
|
const mutation = trpc.viewer.calendars.setDestinationCalendar.useMutation({
|
|
onSuccess: () => {
|
|
utils.viewer.calendars.connectedCalendars.invalidate();
|
|
revalidateSettingsCalendars();
|
|
},
|
|
});
|
|
|
|
return (
|
|
<SettingsHeader
|
|
title={t("calendars")}
|
|
description={t("calendars_description")}
|
|
CTA={<AddCalendarButton />}>
|
|
{!!data.connectedCalendars.length || !!installedCalendars?.items.length ? (
|
|
<>
|
|
{heading && (
|
|
<>
|
|
<DestinationCalendarSettingsWebWrapper />
|
|
<Suspense fallback={<SkeletonLoader />}>
|
|
<SelectedCalendarsSettingsWebWrapper
|
|
onChanged={onChanged}
|
|
fromOnboarding={fromOnboarding}
|
|
destinationCalendarId={data.destinationCalendar?.externalId}
|
|
isPending={mutation.isPending}
|
|
/>
|
|
</Suspense>
|
|
</>
|
|
)}
|
|
</>
|
|
) : fromOnboarding ? (
|
|
<>
|
|
{!!data?.connectedCalendars.length && (
|
|
<ShellSubHeading
|
|
className="mt-4"
|
|
title={<SubHeadingTitleWithConnections title={t("connect_additional_calendar")} />}
|
|
/>
|
|
)}
|
|
<CalendarList onChanged={onChanged} />
|
|
</>
|
|
) : (
|
|
<EmptyScreen
|
|
Icon="calendar"
|
|
headline={t("no_category_apps", {
|
|
category: t("calendar").toLowerCase(),
|
|
})}
|
|
description={t(`no_category_apps_description_calendar`)}
|
|
buttonRaw={
|
|
<Button color="secondary" data-testid="connect-calendar-apps" href="/apps/categories/calendar">
|
|
{t(`connect_calendar_apps`)}
|
|
</Button>
|
|
}
|
|
/>
|
|
)}
|
|
</SettingsHeader>
|
|
);
|
|
}
|