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: benny@cal.com <sldisek783@gmail.com>

* fix

* fix

* fix

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

Co-Authored-By: benny@cal.com <sldisek783@gmail.com>

* 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>
This commit is contained in:
Benny Joo
2026-01-29 16:41:44 +02:00
committed by GitHub
co-authored by Devin AI
parent 18049bc1d6
commit c7ee3984ef
36 changed files with 134 additions and 53 deletions
@@ -1,6 +1,6 @@
import { _generateMetadata, getTranslate } from "app/_utils";
import { FlagListingView } from "@calcom/features/flags/pages/flag-listing-view";
import { FlagListingView } from "@calcom/web/modules/feature-flags/views/flag-listing-view";
import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader";
export const generateMetadata = async () =>
@@ -1,4 +1,4 @@
import DestinationCalendarSelector from "@calcom/features/calendars/DestinationCalendarSelector";
import DestinationCalendarSelector from "@calcom/features/calendars/components/DestinationCalendarSelector";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import type { RouterInputs } from "@calcom/trpc/react";
import { trpc } from "@calcom/trpc/react";
@@ -1,6 +1,6 @@
import { useForm } from "react-hook-form";
import Schedule from "@calcom/features/schedules/components/Schedule";
import Schedule from "@calcom/web/modules/schedules/components/Schedule";
import { DEFAULT_SCHEDULE } from "@calcom/lib/availability";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc/react";
+1 -1
View File
@@ -11,7 +11,7 @@ import DynamicPostHogProvider from "~/ee/posthog/providerDynamic";
import DynamicPostHogPageView from "~/ee/posthog/pageViewDynamic";
import { OrgBrandingProvider } from "@calcom/features/ee/organizations/context/provider";
import { FeatureProvider } from "@calcom/features/flags/context/provider";
import { useFlags } from "@calcom/features/flags/hooks";
import { useFlags } from "@calcom/web/modules/feature-flags/hooks/useFlags";
import DynamicHelpscoutProvider from "@calcom/web/modules/ee/support/lib/helpscout/providerDynamic";
import DynamicIntercomProvider from "@calcom/web/modules/ee/support/lib/intercom/providerDynamic";
+1 -1
View File
@@ -21,7 +21,7 @@ import { useEffect } from "react";
import { OrgBrandingProvider } from "@calcom/features/ee/organizations/context/provider";
import { FeatureProvider } from "@calcom/features/flags/context/provider";
import { useFlags } from "@calcom/features/flags/hooks";
import { useFlags } from "@calcom/web/modules/feature-flags/hooks/useFlags";
import DynamicHelpscoutProvider from "@calcom/web/modules/ee/support/lib/helpscout/providerDynamic";
import DynamicIntercomProvider from "@calcom/web/modules/ee/support/lib/intercom/providerDynamic";
@@ -9,7 +9,7 @@ import posthog from "posthog-js";
import { BulkEditDefaultForEventsModal } from "@calcom/web/modules/event-types/components/BulkEditDefaultForEventsModal";
import type { BulkUpdatParams } from "@calcom/web/modules/event-types/components/BulkEditDefaultForEventsModal";
import { NewScheduleButton } from "@calcom/features/schedules/components/NewScheduleButton";
import { NewScheduleButton } from "@calcom/web/modules/schedules/components/NewScheduleButton";
import { ScheduleListItem } from "@calcom/features/schedules/components/ScheduleListItem";
import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams";
import { useLocale } from "@calcom/lib/hooks/useLocale";
@@ -81,7 +81,7 @@ const mockEvent = {
isPending: false,
};
vi.mock("@calcom/features/calendars/NoAvailabilityDialog", () => ({
vi.mock("@calcom/features/calendars/components/NoAvailabilityDialog", () => ({
default: () => {
return null;
},
@@ -2,12 +2,12 @@ import React from "react";
import { vi, afterEach } from "vitest";
import dayjs from "@calcom/dayjs";
import { DatePicker as DatePickerComponent } from "@calcom/features/calendars/DatePicker";
import { DatePicker as DatePickerComponent } from "@calcom/features/calendars/components/DatePicker";
import { render } from "@calcom/features/bookings/Booker/__tests__/test-utils";
import { DatePicker } from "./DatePicker";
vi.mock("@calcom/features/calendars/DatePicker", () => {
vi.mock("@calcom/features/calendars/components/DatePicker", () => {
return {
DatePicker: vi.fn(() => <div data-testid="mock-date-picker" />),
};
@@ -4,7 +4,7 @@ import type { Dayjs } from "@calcom/dayjs";
import dayjs from "@calcom/dayjs";
import { useBookerStoreContext } from "@calcom/features/bookings/Booker/BookerStoreProvider";
import type { DatePickerClassNames } from "@calcom/features/bookings/Booker/types";
import { DatePicker as DatePickerComponent } from "@calcom/features/calendars/DatePicker";
import { DatePicker as DatePickerComponent } from "@calcom/features/calendars/components/DatePicker";
import { useNonEmptyScheduleDays } from "@calcom/features/schedules/lib/use-schedule/useNonEmptyScheduleDays";
import { weekdayToWeekIndex } from "@calcom/lib/dayjs";
import { useLocale } from "@calcom/lib/hooks/useLocale";
+1 -1
View File
@@ -22,7 +22,7 @@ import {
useEvent,
useScheduleForEvent,
} from "@calcom/features/bookings/Booker/utils/event";
import DatePicker from "@calcom/features/calendars/DatePicker";
import DatePicker from "@calcom/features/calendars/components/DatePicker";
import { Dialog } from "@calcom/features/components/controlled-dialog";
import { TimezoneSelect } from "@calcom/web/modules/timezone/components/TimezoneSelect";
import type { Slot } from "@calcom/features/schedules/lib/use-schedule/types";
@@ -13,7 +13,7 @@ import {
} from "@calcom/web/modules/calendars/components/SelectedCalendarsSettingsWebWrapper";
import { Timezone as PlatformTimzoneSelect } from "@calcom/atoms/timezone";
import getLocationsOptionsForSelect from "@calcom/features/bookings/lib/getLocationOptionsForSelect";
import DestinationCalendarSelector from "@calcom/features/calendars/DestinationCalendarSelector";
import DestinationCalendarSelector from "@calcom/features/calendars/components/DestinationCalendarSelector";
import { TimezoneSelect as WebTimezoneSelect } from "@calcom/web/modules/timezone/components/TimezoneSelect";
import useLockedFieldsManager from "@calcom/features/ee/managed-event-types/hooks/useLockedFieldsManager";
import {
@@ -1,6 +1,6 @@
"use client";
import { useFlags } from "@calcom/features/flags/hooks";
import { useFlags } from "@calcom/web/modules/feature-flags/hooks/useFlags";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { Button } from "@calcom/ui/components/button";
import { Icon } from "@calcom/ui/components/icon";
@@ -6,7 +6,7 @@ import React from "react";
import { useForm, useFieldArray } from "react-hook-form";
import { z } from "zod";
import { useFlags } from "@calcom/features/flags/hooks";
import { useFlags } from "@calcom/web/modules/feature-flags/hooks/useFlags";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { Button } from "@calcom/ui/components/button";
import { Form } from "@calcom/ui/components/form";
@@ -4,7 +4,7 @@ import { useRouter } from "next/navigation";
import posthog from "posthog-js";
import React from "react";
import { useFlags } from "@calcom/features/flags/hooks";
import { useFlags } from "@calcom/web/modules/feature-flags/hooks/useFlags";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { Button } from "@calcom/ui/components/button";
@@ -4,7 +4,7 @@ import { useRouter, useSearchParams } from "next/navigation";
import posthog from "posthog-js";
import React, { useEffect } from "react";
import { useFlags } from "@calcom/features/flags/hooks";
import { useFlags } from "@calcom/web/modules/feature-flags/hooks/useFlags";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { Button } from "@calcom/ui/components/button";
@@ -0,0 +1,29 @@
import React from "react";
import type { Control, FieldValues } from "react-hook-form";
import {
ScheduleComponent,
type FieldPathByValue,
type ScheduleLabelsType,
} from "@calcom/features/schedules/components/ScheduleComponent";
import useMeQuery from "@calcom/trpc/react/hooks/useMeQuery";
import type { TimeRange } from "@calcom/types/schedule";
const Schedule = <
TFieldValues extends FieldValues,
TPath extends FieldPathByValue<TFieldValues, TimeRange[][]>
>(props: {
name: TPath;
control: Control<TFieldValues>;
weekStart?: number;
disabled?: boolean;
labels?: ScheduleLabelsType;
userTimeFormat?: number | null;
}) => {
const query = useMeQuery();
const { timeFormat } = query.data || { timeFormat: null };
return <ScheduleComponent userTimeFormat={timeFormat} {...props} />;
};
export default Schedule;
@@ -4,7 +4,7 @@ import dayjs from "@calcom/dayjs";
import { TimezoneSelect } from "@calcom/web/modules/timezone/components/TimezoneSelect";
import DateOverrideInputDialog from "@calcom/features/schedules/components/DateOverrideInputDialog";
import DateOverrideList from "@calcom/features/schedules/components/DateOverrideList";
import Schedule from "@calcom/features/schedules/components/Schedule";
import Schedule from "@calcom/web/modules/schedules/components/Schedule";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { HttpError } from "@calcom/lib/http-error";
import type { RouterOutputs } from "@calcom/trpc/react";
@@ -7,7 +7,7 @@ import dayjs from "@calcom/dayjs";
import { BookerStoreProvider } from "@calcom/features/bookings/Booker/BookerStoreProvider";
import { PeriodType } from "@calcom/prisma/enums";
import { DatePicker } from "../DatePicker";
import { DatePicker } from "../components/DatePicker";
const noop = () => {
/* noop */
@@ -4,7 +4,7 @@ import { vi } from "vitest";
import dayjs from "@calcom/dayjs";
import { PeriodType } from "@calcom/prisma/enums";
import NoAvailabilityDialog from "../NoAvailabilityDialog";
import NoAvailabilityDialog from "../components/NoAvailabilityDialog";
vi.mock("@calcom/lib/hooks/useLocale", () => ({
useLocale: () => ({
@@ -1,6 +1,7 @@
import type { SelectClassNames } from "@calcom/features/eventtypes/lib/types";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import type { RouterOutputs } from "@calcom/trpc/react";
import type { App } from "@calcom/types/App";
import type { IntegrationCalendar } from "@calcom/types/Calendar";
import { Badge } from "@calcom/ui/components/badge";
import { Select } from "@calcom/ui/components/form";
import { Icon } from "@calcom/ui/components/icon";
@@ -9,6 +10,51 @@ import { useEffect, useState } from "react";
import type { OptionProps, SingleValueProps } from "react-select";
import { components } from "react-select";
type CalendarWithPrimary = Omit<IntegrationCalendar, "primary"> & {
primary?: boolean | null;
isSelected?: boolean;
credentialId?: number | null;
delegationCredentialId?: string | null;
};
interface ConnectedCalendar {
credentialId: number;
integration: App & { title?: string; slug?: string };
primary?: CalendarWithPrimary | null;
calendars?: CalendarWithPrimary[] | null;
cacheUpdatedAt?: Date | string | null;
delegationCredentialId?: string | null;
error?: {
message: string;
};
}
interface DestinationCalendar {
integration: string;
externalId: string;
primaryEmail?: string | null;
name?: string | null;
integrationTitle?: string | null;
credentialId?: number | null;
email?: string | null;
readOnly?: boolean | null;
primary?: boolean | null;
id?: string | number | null;
userId?: number | null;
eventTypeId?: number | null;
customCalendarReminder?: number | null;
delegationCredentialId?: string | null;
domainWideDelegationCredentialId?: string | null;
createdAt?: Date | string | null;
updatedAt?: Date | string | null;
}
interface CalendarsQueryData {
connectedCalendars: ConnectedCalendar[];
destinationCalendar: DestinationCalendar | null;
}
interface Props {
onChange: (value: { externalId: string; integration: string }) => void;
isPending?: boolean;
@@ -17,7 +63,7 @@ interface Props {
value: string | undefined;
maxWidth?: number;
hideAdvancedText?: boolean;
calendarsQueryData?: RouterOutputs["viewer"]["calendars"]["connectedCalendars"];
calendarsQueryData?: CalendarsQueryData;
customClassNames?: SelectClassNames;
}
@@ -15,9 +15,9 @@ import { Switch } from "@calcom/ui/components/form";
import { Form } from "@calcom/ui/components/form";
import { showToast } from "@calcom/ui/components/toast";
import DatePicker from "../../calendars/DatePicker";
import type { TimeRange } from "./Schedule";
import { DayRanges } from "./Schedule";
import DatePicker from "@calcom/features/calendars/components/DatePicker";
import type { TimeRange } from "./ScheduleComponent";
import { DayRanges } from "./ScheduleComponent";
const DateOverrideForm = ({
value,
@@ -4,7 +4,6 @@ import { formatInTimeZone } from "date-fns-tz";
import { useIsPlatform } from "@calcom/atoms/hooks/useIsPlatform";
import dayjs from "@calcom/dayjs";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import type { RouterOutputs } from "@calcom/trpc/react";
import type { TimeRange, WorkingHours } from "@calcom/types/schedule";
import { Button } from "@calcom/ui/components/button";
import { DialogTrigger } from "@calcom/ui/components/dialog";
@@ -16,6 +15,13 @@ const sortByDate = (a: { ranges: TimeRange[]; id: string }, b: { ranges: TimeRan
return a.ranges[0].start > b.ranges[0].start ? 1 : -1;
};
interface TravelSchedule {
id: number;
startDate: Date;
endDate: Date | null;
timeZone: string;
}
// I would like this to be decoupled, but RHF really doesn't support this.
const DateOverrideList = ({
workingHours,
@@ -36,7 +42,7 @@ const DateOverrideList = ({
excludedDates?: string[];
userTimeFormat: number | null;
hour12: boolean;
travelSchedules?: RouterOutputs["viewer"]["travelSchedules"]["get"];
travelSchedules?: TravelSchedule[];
weekStart?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
handleAvailabilityUpdate?: VoidFunction;
isDryRun?: boolean;
@@ -187,23 +187,6 @@ const CopyButton = ({
);
};
const Schedule = <
TFieldValues extends FieldValues,
TPath extends FieldPathByValue<TFieldValues, TimeRange[][]>
>(props: {
name: TPath;
control: Control<TFieldValues>;
weekStart?: number;
disabled?: boolean;
labels?: ScheduleLabelsType;
userTimeFormat?: number | null;
}) => {
const query = useMeQuery();
const { timeFormat } = query.data || { timeFormat: null };
return <ScheduleComponent userTimeFormat={timeFormat} {...props} />;
};
export const ScheduleComponent = <
TFieldValues extends FieldValues,
TPath extends FieldPathByValue<TFieldValues, TimeRange[][]>
@@ -820,5 +803,3 @@ const CopyTimes = ({
</div>
);
};
export default Schedule;
@@ -6,7 +6,6 @@ import { Fragment, useState } from "react";
import { availabilityAsString } from "@calcom/lib/availability";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { sortAvailabilityStrings } from "@calcom/lib/weekstart";
import type { RouterOutputs } from "@calcom/trpc/react";
import { Dialog } from "@calcom/features/components/controlled-dialog";
import { Badge } from "@calcom/ui/components/badge";
import { Button } from "@calcom/ui/components/button";
@@ -21,6 +20,23 @@ import { ConfirmationDialogContent } from "@calcom/ui/components/dialog";
import { Icon } from "@calcom/ui/components/icon";
import { showToast } from "@calcom/ui/components/toast";
interface Schedule {
id: number;
name: string;
isDefault: boolean;
timeZone?: string | null;
availability: {
id: number;
userId: number | null;
eventTypeId: number | null;
days: number[];
startTime: Date;
endTime: Date;
date: Date | null;
scheduleId: number | null;
}[];
}
export function ScheduleListItem({
schedule,
deleteFunction,
@@ -30,7 +46,7 @@ export function ScheduleListItem({
duplicateFunction,
redirectUrl,
}: {
schedule: RouterOutputs["viewer"]["availability"]["list"]["schedules"][number];
schedule: Schedule;
deleteFunction: ({ scheduleId }: { scheduleId: number }) => void;
displayOptions?: {
timeZone?: string;
@@ -1,6 +1,6 @@
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
import { parseTimeString } from "./Schedule";
import { parseTimeString } from "./ScheduleComponent";
describe("parseTimeString", () => {
beforeEach(() => {
@@ -24,9 +24,10 @@ import type {
import { BulkEditDefaultForEventsModal } from "@calcom/web/modules/event-types/components/BulkEditDefaultForEventsModal";
import DateOverrideInputDialog from "@calcom/features/schedules/components/DateOverrideInputDialog";
import DateOverrideList from "@calcom/features/schedules/components/DateOverrideList";
import WebSchedule, {
import {
ScheduleComponent as PlatformSchedule,
} from "@calcom/features/schedules/components/Schedule";
} from "@calcom/features/schedules/components/ScheduleComponent";
import WebSchedule from "@calcom/web/modules/schedules/components/Schedule";
import { availabilityAsString } from "@calcom/lib/availability";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { sortAvailabilityStrings } from "@calcom/lib/weekstart";
@@ -1,7 +1,7 @@
import type { ReactNode } from "react";
import { forwardRef, useRef } from "react";
import type { ScheduleLabelsType } from "@calcom/features/schedules/components/Schedule";
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";
@@ -1,15 +1,17 @@
"use client";
import { useState, useEffect, useMemo } from "react";
import {
OptionComponent,
SingleValueComponent,
} from "@calcom/features/calendars/DestinationCalendarSelector";
} from "@calcom/features/calendars/components/DestinationCalendarSelector";
import type { ConnectedDestinationCalendars } from "@calcom/features/calendars/lib/getConnectedDestinationCalendars";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import classNames from "@calcom/ui/classNames";
import { Badge } from "@calcom/ui/components/badge";
import { Select } from "@calcom/ui/components/form";
import { useEffect, useMemo, useState } from "react";
import { getPlaceholderContent } from "../lib/getPlaceholderContent";
export type DestinationCalendarProps = {