fix: extract shared types to non-React modules to fix circular dependencies (#26083)

- Move InvalidAppCredentialBannerProps to packages/features/users/types/invalidAppCredentials.ts
- Add WorkflowListType to packages/features/ee/workflows/lib/types.ts
- Update server file imports to use new type locations
- Update React component imports to re-export from new locations

This fixes circular dependencies where server files were importing from React
component modules that import from @calcom/trpc, creating:
server -> component -> @calcom/trpc -> react -> server (circular)

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Keith Williams
2025-12-21 01:43:49 +05:30
committed by GitHub
co-authored by Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
parent 9ae2381755
commit 825f55c0a1
6 changed files with 49 additions and 43 deletions
@@ -3,10 +3,8 @@ import Link from "next/link";
import { useRouter } from "next/navigation";
import { useState } from "react";
import type { WorkflowPermissions } from "@calcom/features/workflows/repositories/WorkflowPermissionsRepository";
import { getPlaceholderAvatar } from "@calcom/lib/defaultAvatarImage";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import type { Membership, Workflow } from "@calcom/prisma/client";
import { trpc } from "@calcom/trpc/react";
import classNames from "@calcom/ui/classNames";
import { ArrowButton } from "@calcom/ui/components/arrow-button";
@@ -25,38 +23,12 @@ import { Icon } from "@calcom/ui/components/icon";
import { Tooltip } from "@calcom/ui/components/tooltip";
import { getActionIcon } from "../lib/getActionIcon";
import type { WorkflowStep } from "../lib/types";
import { type WorkflowListType } from "../lib/types";
import { DeleteDialog } from "./DeleteDialog";
export type WorkflowType = Workflow & {
team: {
id: number;
name: string;
members: Membership[];
slug: string | null;
logo?: string | null;
} | null;
steps: WorkflowStep[];
activeOnTeams?: {
team: {
id: number;
name?: string | null;
};
}[];
activeOn?: {
eventType: {
id: number;
title: string;
parentId: number | null;
_count: {
children: number;
};
};
}[];
readOnly?: boolean; // Keep for backward compatibility
permissions?: WorkflowPermissions;
isOrg?: boolean;
};
/** @deprecated Use WorkflowListType from ../lib/types instead */
export type WorkflowType = WorkflowListType;
interface Props {
workflows: WorkflowType[] | undefined;
}
+32 -1
View File
@@ -1,6 +1,7 @@
import type { FORM_SUBMITTED_WEBHOOK_RESPONSES } from "@calcom/app-store/routing-forms/lib/formSubmissionUtils";
import type { WorkflowPermissions } from "@calcom/features/workflows/repositories/WorkflowPermissionsRepository";
import type { TimeFormat } from "@calcom/lib/timeFormat";
import type { Prisma } from "@calcom/prisma/client";
import type { Membership, Prisma, Workflow as PrismaWorkflow } from "@calcom/prisma/client";
import type { TimeUnit, WorkflowTemplates, WorkflowTriggerEvents } from "@calcom/prisma/enums";
import { WorkflowActions } from "@calcom/prisma/enums";
import type { CalEventResponses, RecurringEvent } from "@calcom/types/Calendar";
@@ -93,3 +94,33 @@ export type ScheduleEmailReminderAction = Extract<
WorkflowActions,
"EMAIL_HOST" | "EMAIL_ATTENDEE" | "EMAIL_ADDRESS"
>;
export type WorkflowListType = PrismaWorkflow & {
team: {
id: number;
name: string;
members: Membership[];
slug: string | null;
logo?: string | null;
} | null;
steps: WorkflowStep[];
activeOnTeams?: {
team: {
id: number;
name?: string | null;
};
}[];
activeOn?: {
eventType: {
id: number;
title: string;
parentId: number | null;
_count: {
children: number;
};
};
}[];
readOnly?: boolean;
permissions?: WorkflowPermissions;
isOrg?: boolean;
};
@@ -4,6 +4,8 @@ import { useLocale } from "@calcom/lib/hooks/useLocale";
import { type RouterOutputs } from "@calcom/trpc";
import { TopBanner } from "@calcom/ui/components/top-banner";
import { type InvalidAppCredentialBannerProps } from "../types/invalidAppCredentials";
export type InvalidAppCredentialBannersProps = {
data: RouterOutputs["viewer"]["me"]["getUserTopBanners"]["invalidAppCredentialBanners"];
};
@@ -22,10 +24,7 @@ export function InvalidAppCredentialBanners({ data }: InvalidAppCredentialBanner
);
}
export type InvalidAppCredentialBannerProps = {
name: string;
slug: string;
};
export type { InvalidAppCredentialBannerProps };
export function InvalidAppCredentialBanner({ name, slug }: InvalidAppCredentialBannerProps) {
const { t } = useLocale();
@@ -0,0 +1,4 @@
export type InvalidAppCredentialBannerProps = {
name: string;
slug: string;
};
@@ -1,5 +1,5 @@
import { getAppFromSlug } from "@calcom/app-store/utils";
import { type InvalidAppCredentialBannerProps } from "@calcom/features/users/components/InvalidAppCredentialsBanner";
import { type InvalidAppCredentialBannerProps } from "@calcom/features/users/types/invalidAppCredentials";
import { prisma } from "@calcom/prisma";
import { MembershipRole } from "@calcom/prisma/enums";
import type { TrpcSessionUser } from "@calcom/trpc/server/types";
@@ -1,5 +1,5 @@
import { TeamRepository } from "@calcom/features/ee/teams/repositories/TeamRepository";
import type { WorkflowType } from "@calcom/features/ee/workflows/components/WorkflowListPage";
import type { WorkflowListType } from "@calcom/features/ee/workflows/lib/types";
import { WorkflowRepository } from "@calcom/features/ee/workflows/repositories/WorkflowRepository";
// import dayjs from "@calcom/dayjs";
// import { getErrorFromUnknown } from "@calcom/lib/errors";
@@ -19,7 +19,7 @@ type ListOptions = {
};
export const listHandler = async ({ ctx, input }: ListOptions) => {
const workflows: WorkflowType[] = [];
const workflows: WorkflowListType[] = [];
const teamRepository = new TeamRepository(ctx.prisma);
const org = await teamRepository.findOrganization({
@@ -42,7 +42,7 @@ export const listHandler = async ({ ctx, input }: ListOptions) => {
}
if (input && input.teamId) {
const teamWorkflows: WorkflowType[] = await WorkflowRepository.findTeamWorkflows({
const teamWorkflows: WorkflowListType[] = await WorkflowRepository.findTeamWorkflows({
teamId: input.teamId,
userId: ctx.user.id,
excludeFormTriggers: input.includeOnlyEventTypeWorkflows,
@@ -66,7 +66,7 @@ export const listHandler = async ({ ctx, input }: ListOptions) => {
}
if (input && input.userId) {
const userWorkflows: WorkflowType[] = await WorkflowRepository.findUserWorkflows({
const userWorkflows: WorkflowListType[] = await WorkflowRepository.findUserWorkflows({
userId: ctx.user.id,
excludeFormTriggers: input.includeOnlyEventTypeWorkflows,
});
@@ -87,7 +87,7 @@ export const listHandler = async ({ ctx, input }: ListOptions) => {
excludeFormTriggers: input.includeOnlyEventTypeWorkflows,
});
const workflowsWithReadOnly: WorkflowType[] = allWorkflows.map((workflow) => {
const workflowsWithReadOnly: WorkflowListType[] = allWorkflows.map((workflow) => {
const readOnly = !!workflow.team?.members?.find(
(member) => member.userId === ctx.user.id && member.role === MembershipRole.MEMBER
);