Files
81e4241f85 refactor: apply biome formatting to apps/web (#27692)
* refactor: apply biome formatting to apps/web (batch 1)

Formats apps/web non-modules directories:
- apps/web/app
- apps/web/lib
- apps/web/pages
- apps/web/styles
- apps/web/server
- apps/web/test
- Root-level .ts and .mjs files

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* refactor: apply biome formatting to apps/web/modules (batch 2)

Formats smaller apps/web/modules directories:
- onboarding, data-table, users, apps, auth
- integration-attribute-sync, shell, availability
- feature-flags, team, webhooks, getting-started
- feature-opt-in, troubleshooter, schedules, notifications
- signup-view.tsx

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* refactor: apply biome formatting to apps/web/modules (batch 3)

Formats medium apps/web/modules directories:
- bookings
- event-types
- insights
- embed

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* refactor: apply biome formatting to apps/web/modules/ee (batch 4)

Formats apps/web/modules/ee directory.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* refactor: apply biome formatting to apps/web (batch 5)

Formats remaining apps/web directories:
- apps/web/modules/settings
- apps/web/modules/booking-audit
- apps/web/playwright

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* refactor: apply biome formatting to apps/web/components (batch 6)

Formats remaining apps/web/components files.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* refactor: apply biome formatting to new apps/web files from main

Formats newly added/modified files from main merge:
- WorkflowStepContainer.tsx (resolved merge conflicts)
- Newly moved files (blocklist, form-builder, schedules, etc.)
- Other files modified in main

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* refactor: apply biome formatting to new apps/web files from main

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-02-10 23:25:24 +05:30

594 lines
17 KiB
TypeScript

import type { GetServerSidePropsContext, GetServerSidePropsResult } from "next";
import { z } from "zod";
import { createDefaultInstallation } from "@calcom/app-store/_utils/installation";
import { filterEventTypesWhereLocationUpdateIsAllowed } from "@calcom/app-store/_utils/getBulkEventTypes";
import { appStoreMetadata } from "@calcom/app-store/appStoreMetaData";
import type { LocationObject } from "@calcom/app-store/locations";
import { isConferencing as isConferencingApp } from "@calcom/app-store/utils";
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
import { UserRepository } from "@calcom/features/users/repositories/UserRepository";
import { AppOnboardingSteps } from "@calcom/lib/apps/appOnboardingSteps";
import { CAL_URL } from "@calcom/lib/constants";
import { getPlaceholderAvatar } from "@calcom/lib/defaultAvatarImage";
import prisma from "@calcom/prisma";
import { Prisma } from "@calcom/prisma/client";
import { eventTypeBookingFields } from "@calcom/prisma/zod-utils";
import { STEPS } from "../../../../modules/apps/installation/[[...step]]/constants";
import type {
OnboardingPageProps,
TEventTypeGroup,
TEventType,
} from "../../../../modules/apps/installation/[[...step]]/step-view";
const eventTypeSelect: Prisma.EventTypeSelect = {
id: true,
description: true,
durationLimits: true,
metadata: true,
length: true,
title: true,
position: true,
recurringEvent: true,
requiresConfirmation: true,
canSendCalVideoTranscriptionEmails: true,
team: { select: { slug: true } },
schedulingType: true,
teamId: true,
users: { select: { username: true } },
seatsPerTimeSlot: true,
slug: true,
locations: true,
userId: true,
destinationCalendar: true,
bookingFields: true,
calVideoSettings: true,
parentId: true,
};
type EventTypeFromDb = Prisma.EventTypeGetPayload<{
select: typeof eventTypeSelect;
}>;
type MinimalUser = {
id: number;
username: string | null;
name: string | null;
avatarUrl: string | null;
};
type MinimalTeam = {
id: number;
name: string;
logoUrl: string | null;
isOrganization: boolean;
};
type RedirectResult = { redirect: { permanent: boolean; destination: string } };
const mapEventType = (item: EventTypeFromDb): TEventType => {
let teamSlug = "";
if (item.team) {
teamSlug = `team/${item.team.slug}`;
}
const userSlug = item?.users?.[0]?.username;
let urlPart = userSlug;
if (teamSlug) {
urlPart = teamSlug;
}
return {
...item,
URL: `${CAL_URL}/${urlPart}/${item.slug}`,
selected: false,
locations: item.locations as unknown as LocationObject[],
bookingFields: eventTypeBookingFields.parse(item.bookingFields || []),
};
};
const getUser = async (
userId: number
): Promise<
| (MinimalUser & {
teams: {
id: number;
name: string;
logoUrl: string | null;
isOrganization: boolean;
parent: { id: number; name: string; logoUrl: string | null } | null;
}[];
})
| null
> => {
const userRepo = new UserRepository(prisma);
const userAdminTeams = await userRepo.getUserAdminTeams({ userId });
if (!userAdminTeams?.id) {
return null;
}
const teams = userAdminTeams.teams.map(({ team }) => {
const parentLogoUrl = team.parent?.logoUrl;
const parentName = team.parent?.name;
let logoUrl = "";
if (team.parent) {
logoUrl = getPlaceholderAvatar(parentLogoUrl, parentName);
} else {
logoUrl = getPlaceholderAvatar(team.logoUrl, team.name);
}
return {
...team,
logoUrl,
};
});
return {
...userAdminTeams,
teams,
};
};
const getOrgSubTeams = async (
parentId: number
): Promise<
{
id: number;
name: string;
logoUrl: string | null;
isOrganization: boolean;
parent: { id: number; name: string; logoUrl: string | null } | null;
}[]
> => {
const teams = await prisma.team.findMany({
where: {
parentId,
},
select: {
id: true,
name: true,
logoUrl: true,
isOrganization: true,
parent: {
select: {
logoUrl: true,
name: true,
id: true,
},
},
},
});
return teams.map((team) => {
const parentLogoUrl = team.parent?.logoUrl;
const parentName = team.parent?.name;
let logoUrl = "";
if (team.parent) {
logoUrl = getPlaceholderAvatar(parentLogoUrl, parentName);
} else {
logoUrl = getPlaceholderAvatar(team.logoUrl, team.name);
}
return {
...team,
logoUrl,
};
});
};
const getAppBySlug = async (
appSlug: string
): Promise<{ slug: string; keys: Prisma.JsonValue; enabled: boolean; dirName: string } | null> => {
const app = await prisma.app.findUnique({
where: { slug: appSlug, enabled: true },
select: { slug: true, keys: true, enabled: true, dirName: true },
});
return app;
};
const getTeamEventTypes = async (
teamIds: number[],
isConferencing: boolean,
eventTypeSelect: Prisma.EventTypeSelect
): Promise<TEventTypeGroup[]> => {
const teams = await prisma.team.findMany({
where: { id: { in: teamIds }, isOrganization: false },
select: {
id: true,
name: true,
logoUrl: true,
slug: true,
isOrganization: true,
eventTypes: { select: eventTypeSelect },
},
});
return teams.map((team) => {
let filteredEventTypes = team.eventTypes;
if (isConferencing) {
filteredEventTypes = filterEventTypesWhereLocationUpdateIsAllowed(team.eventTypes);
}
return {
teamId: team.id,
slug: team.slug,
name: team.name,
isOrganisation: team.isOrganization,
image: getPlaceholderAvatar(team.logoUrl, team.name),
eventTypes: (filteredEventTypes as unknown as EventTypeFromDb[])
.map(mapEventType)
.sort((a, b) => (b.position || 0) - (a.position || 0)),
};
});
};
const getUserEventTypes = async (
userId: number,
isConferencing: boolean,
eventTypeSelect: Prisma.EventTypeSelect
): Promise<TEventTypeGroup[]> => {
const user = await prisma.user.findUnique({
where: { id: userId },
select: {
id: true,
username: true,
name: true,
avatarUrl: true,
eventTypes: { where: { teamId: null }, select: eventTypeSelect },
},
});
if (!user) return [];
let filteredEventTypes = user.eventTypes;
if (isConferencing) {
filteredEventTypes = filterEventTypesWhereLocationUpdateIsAllowed(user.eventTypes);
}
return [
{
userId: user.id,
slug: user.username,
name: user.name,
image: getPlaceholderAvatar(user.avatarUrl, user.name),
eventTypes: (filteredEventTypes as unknown as EventTypeFromDb[])
.map(mapEventType)
.sort((a, b) => (b.position || 0) - (a.position || 0)),
},
];
};
const getEventTypes = async ({
userId,
teamIds,
isConferencing = false,
}: {
userId: number;
teamIds?: number[];
isConferencing?: boolean;
}): Promise<TEventTypeGroup[] | null> => {
if (teamIds && teamIds.length > 0) {
return getTeamEventTypes(teamIds, isConferencing, eventTypeSelect);
}
return getUserEventTypes(userId, isConferencing, eventTypeSelect);
};
const getAppInstallsBySlug = async (
appSlug: string,
userId: number,
teamIds?: number[]
): Promise<Prisma.CredentialGetPayload<Record<string, never>>[]> => {
if (teamIds?.length) {
return prisma.credential.findMany({
where: {
OR: [
{ appId: appSlug, userId: userId },
{ appId: appSlug, teamId: { in: teamIds } },
],
},
});
}
return prisma.credential.findMany({
where: { appId: appSlug, userId: userId },
});
};
const handleAutoInstall = async (
user: MinimalUser,
appMetadata: import("@calcom/types/App").AppMeta,
parsedAppSlug: string
): Promise<number | null> => {
try {
const newCredential = await createDefaultInstallation({
appType: appMetadata.type,
user: { id: user.id },
slug: parsedAppSlug,
key: {},
teamId: undefined,
});
return newCredential.id;
} catch (error) {
if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === "P2002") {
const existing = await prisma.credential.findFirst({
where: { appId: parsedAppSlug, userId: user.id, teamId: null },
select: { id: true },
});
return existing?.id ?? null;
}
return null;
}
};
const getEventTypeGroupsForStep = async (
user: MinimalUser,
userTeams: MinimalTeam[],
parsedTeamIdParam: number | undefined,
isOrg: boolean,
isConferencing: boolean
): Promise<TEventTypeGroup[] | null> => {
let groups: TEventTypeGroup[] | null = null;
if (isOrg) {
groups = await getEventTypes({
userId: user.id,
teamIds: userTeams.map((item) => item.id),
isConferencing,
});
} else if (parsedTeamIdParam) {
groups = await getEventTypes({ userId: user.id, teamIds: [parsedTeamIdParam], isConferencing });
} else {
groups = await getEventTypes({ userId: user.id, isConferencing });
}
if (isConferencing && groups) {
const destinationCalendar = await prisma.destinationCalendar.findFirst({
where: { userId: user.id, eventTypeId: null },
});
groups.forEach((group) => {
group.eventTypes = group.eventTypes.map((eventType) => {
if (!eventType.destinationCalendar) {
return { ...eventType, destinationCalendar };
}
return eventType;
});
});
}
return groups;
};
const getInitialStep = (
parsedStepParam: string | undefined,
hasTeams: boolean,
showEventTypesStep: boolean,
parsedAppSlug: string,
isOAuth: boolean
): { step: string | undefined; redirect?: RedirectResult } => {
if (!hasTeams && parsedStepParam === AppOnboardingSteps.ACCOUNTS_STEP && showEventTypesStep && !isOAuth) {
return {
step: parsedStepParam,
redirect: {
redirect: { permanent: false, destination: `/apps/installation/event-types?slug=${parsedAppSlug}` },
},
};
}
let step = parsedStepParam;
if (!parsedStepParam) {
if (hasTeams) {
step = AppOnboardingSteps.ACCOUNTS_STEP;
} else {
step = isOAuth ? AppOnboardingSteps.ACCOUNTS_STEP : AppOnboardingSteps.EVENT_TYPES_STEP;
}
}
return { step };
};
const getAppAndMetadata = async (
parsedAppSlug: string
): Promise<{
app: { slug: string; keys: Prisma.JsonValue; enabled: boolean; dirName: string } | null;
appMetadata: import("@calcom/types/App").AppMeta | null;
redirect?: RedirectResult;
}> => {
const app = await getAppBySlug(parsedAppSlug);
if (!app)
return {
app: null,
appMetadata: null,
redirect: { redirect: { permanent: false, destination: "/apps" } },
};
const appMetadata = appStoreMetadata[app.dirName as keyof typeof appStoreMetadata];
if (!appMetadata)
return {
app: null,
appMetadata: null,
redirect: { redirect: { permanent: false, destination: "/apps" } },
};
return { app, appMetadata };
};
const getCredentialId = (
parsedTeamIdParam: number | undefined,
appInstalls: Prisma.CredentialGetPayload<Record<string, never>>[],
userId: number
): number | null => {
if (parsedTeamIdParam) {
return appInstalls.find((item) => item.teamId === parsedTeamIdParam)?.id ?? null;
}
return appInstalls.find((item) => item.userId === userId)?.id ?? null;
};
const prepareUserTeams = async (
user: MinimalUser & { teams: MinimalTeam[] },
parsedTeamIdParam: number | undefined
): Promise<{ userTeams: MinimalTeam[]; isOrg: boolean; redirect?: RedirectResult }> => {
let userTeams = user.teams;
let isOrg = false;
if (parsedTeamIdParam) {
const currentTeam = userTeams.find((team: MinimalTeam) => team.id === parsedTeamIdParam);
if (!currentTeam?.id)
return {
userTeams: [],
isOrg: false,
redirect: { redirect: { permanent: false, destination: "/apps" } },
};
if (currentTeam.isOrganization) {
userTeams = [...userTeams, ...(await getOrgSubTeams(parsedTeamIdParam))];
isOrg = true;
}
}
return { userTeams, isOrg };
};
const getCredential = async (
parsedTeamIdParam: number | undefined,
appInstalls: Prisma.CredentialGetPayload<Record<string, never>>[],
user: MinimalUser & { teams: MinimalTeam[] },
initialStep: string,
appMetadata: import("@calcom/types/App").AppMeta,
parsedAppSlug: string
): Promise<{ credentialId: number | null; redirect?: RedirectResult }> => {
let credentialId = getCredentialId(parsedTeamIdParam, appInstalls, user.id);
if (
!credentialId &&
!user.teams.length &&
initialStep === AppOnboardingSteps.EVENT_TYPES_STEP &&
!appMetadata.isOAuth
) {
credentialId = await handleAutoInstall(user, appMetadata, parsedAppSlug);
if (!credentialId)
return { credentialId: null, redirect: { redirect: { permanent: false, destination: "/apps" } } };
}
return { credentialId };
};
const getTeamsInstalled = (
hasTeams: boolean,
userTeams: MinimalTeam[],
appInstalls: Prisma.CredentialGetPayload<Record<string, never>>[]
): import("../../../../modules/apps/installation/[[...step]]/step-view").TTeams => {
if (!hasTeams) return [];
return userTeams.map((team: MinimalTeam) => ({
...team,
alreadyInstalled: appInstalls.some((install) => !!install.teamId && install.teamId === team.id),
}));
};
const getParsedStep = (paramsStep: string | string[] | undefined): string | undefined => {
if (Array.isArray(paramsStep)) return paramsStep[0];
return paramsStep;
};
const getInstallationParams = async (
context: GetServerSidePropsContext
): Promise<{
parsedAppSlug: string;
parsedStepParam: string | undefined;
parsedTeamIdParam: number | undefined;
sessionId: number | null;
}> => {
const { query, params, req } = context;
const parsedAppSlug = z.coerce.string().parse(query?.slug);
const parsedStepParam = z.string().optional().parse(getParsedStep(params?.step));
const parsedTeamIdParam = z.coerce.number().optional().parse(query?.teamId);
const session = await getServerSession({ req });
const sessionId = (session?.user as { id?: number })?.id ?? null;
return { parsedAppSlug, parsedStepParam, parsedTeamIdParam, sessionId };
};
export const getServerSideProps = async (
context: GetServerSidePropsContext
): Promise<GetServerSidePropsResult<OnboardingPageProps>> => {
const { parsedAppSlug, parsedStepParam, parsedTeamIdParam, sessionId } =
await getInstallationParams(context);
if (!sessionId) return { redirect: { permanent: false, destination: "/auth/login" } };
const { app, appMetadata, redirect: appRedirect } = await getAppAndMetadata(parsedAppSlug);
if (appRedirect) return appRedirect;
const user = await getUser(sessionId);
if (!user?.username || !appMetadata) return { redirect: { permanent: false, destination: "/apps" } };
const appMetadataFinal = appMetadata;
const showEventTypesStep =
appMetadataFinal.extendsFeature === "EventType" || isConferencingApp(appMetadataFinal.categories);
const { userTeams, isOrg, redirect: teamRedirect } = await prepareUserTeams(user, parsedTeamIdParam);
if (teamRedirect) return teamRedirect;
const { step: initialStep, redirect: stepRedirect } = getInitialStep(
parsedStepParam,
!!userTeams.length,
showEventTypesStep,
parsedAppSlug,
!!appMetadataFinal.isOAuth
);
if (stepRedirect) return stepRedirect;
if (!initialStep) {
return { redirect: { permanent: false, destination: "/apps" } };
}
let initialStepValidated: (typeof STEPS)[number];
try {
initialStepValidated = z.enum(STEPS).parse(initialStep);
} catch {
return { redirect: { permanent: false, destination: "/apps" } };
}
const appInstalls = await getAppInstallsBySlug(
parsedAppSlug,
user.id,
userTeams.map(({ id }: MinimalTeam) => id)
);
const { credentialId, redirect: credRedirect } = await getCredential(
parsedTeamIdParam,
appInstalls,
user,
initialStepValidated,
appMetadataFinal,
parsedAppSlug
);
if (credRedirect) return credRedirect;
let eventTypeGroups = null;
if (initialStepValidated === AppOnboardingSteps.EVENT_TYPES_STEP) {
if (!showEventTypesStep) {
return {
redirect: {
permanent: false,
destination: `/apps/installed/${appMetadataFinal.categories[0]}?hl=${appMetadataFinal.slug}`,
},
};
}
eventTypeGroups = await getEventTypeGroupsForStep(
user,
userTeams,
parsedTeamIdParam,
isOrg,
isConferencingApp(appMetadataFinal.categories)
);
if (!credentialId) return { redirect: { permanent: false, destination: "/apps" } };
}
const personalAccount = {
id: user.id,
name: user.name,
avatarUrl: user.avatarUrl,
alreadyInstalled: appInstalls.some((install) => !install.teamId && install.userId === user.id),
};
const teams = getTeamsInstalled(!!user.teams.length, userTeams, appInstalls);
return {
props: {
app,
appMetadata: appMetadataFinal,
showEventTypesStep,
step: initialStepValidated,
personalAccount,
teams,
eventTypeGroups,
userName: user.username,
credentialId: credentialId || undefined,
isConferencing: isConferencingApp(appMetadataFinal.categories),
isOrg,
installableOnTeams:
!!appMetadataFinal?.concurrentMeetings || !isConferencingApp(appMetadataFinal.categories),
} as OnboardingPageProps,
};
};