diff --git a/apps/web/app/(use-page-wrapper)/(main-nav)/bookings/[status]/page.tsx b/apps/web/app/(use-page-wrapper)/(main-nav)/bookings/[status]/page.tsx index aad91983df..d8a8ac2fe0 100644 --- a/apps/web/app/(use-page-wrapper)/(main-nav)/bookings/[status]/page.tsx +++ b/apps/web/app/(use-page-wrapper)/(main-nav)/bookings/[status]/page.tsx @@ -1,18 +1,15 @@ -import { ShellMainAppDir } from "app/(use-page-wrapper)/(main-nav)/ShellMainAppDir"; +import { getServerSession } from "@calcom/features/auth/lib/getServerSession"; +import { getFeatureOptInService } from "@calcom/features/di/containers/FeatureOptInService"; +import { getFeaturesRepository } from "@calcom/features/di/containers/FeaturesRepository"; +import { PermissionCheckService } from "@calcom/features/pbac/services/permission-check.service"; +import { MembershipRole } from "@calcom/prisma/enums"; +import { buildLegacyRequest } from "@lib/buildLegacyCtx"; import type { PageProps } from "app/_types"; import { _generateMetadata, getTranslate } from "app/_utils"; +import { ShellMainAppDir } from "app/(use-page-wrapper)/(main-nav)/ShellMainAppDir"; import { cookies, headers } from "next/headers"; import { redirect } from "next/navigation"; import { z } from "zod"; - -import { getServerSession } from "@calcom/features/auth/lib/getServerSession"; -import { FeaturesRepository } from "@calcom/features/flags/features.repository"; -import { PermissionCheckService } from "@calcom/features/pbac/services/permission-check.service"; -import { prisma } from "@calcom/prisma"; -import { MembershipRole } from "@calcom/prisma/enums"; - -import { buildLegacyRequest } from "@lib/buildLegacyCtx"; - import { validStatuses } from "~/bookings/lib/validStatuses"; import BookingsList from "~/bookings/views/bookings-view"; @@ -37,29 +34,36 @@ const Page = async ({ params }: PageProps) => { const t = await getTranslate(); const session = await getServerSession({ req: buildLegacyRequest(await headers(), await cookies()) }); - let canReadOthersBookings = false; - if (session?.user?.id) { - const permissionService = new PermissionCheckService(); - const userId = session.user.id; - - const teamIdsWithPermission = await permissionService.getTeamIdsWithPermission({ - userId, - permission: "booking.read", - fallbackRoles: [MembershipRole.OWNER, MembershipRole.ADMIN], - }); - // We check if teamIdsWithPermission.length > 0. - // While this may not be entirely accurate, it's acceptable - // because we perform a thorough validation on the server side for the actual filter values. - // This variable is primarily for UI purposes. - canReadOthersBookings = teamIdsWithPermission.length > 0; + if (!session?.user?.id) { + return redirect("/auth/login"); } - const featuresRepository = new FeaturesRepository(prisma); - const featureFlags = session?.user?.id - ? await featuresRepository.getUserFeaturesStatus(session.user.id, ["bookings-v3", "booking-audit"]) - : { "bookings-v3": false, "booking-audit": false }; + const userId = session.user.id; + const permissionService = new PermissionCheckService(); - const bookingsV3Enabled = featureFlags["bookings-v3"] ?? false; + const teamIdsWithPermission = await permissionService.getTeamIdsWithPermission({ + userId, + permission: "booking.read", + fallbackRoles: [MembershipRole.OWNER, MembershipRole.ADMIN], + }); + // We check if teamIdsWithPermission.length > 0. + // While this may not be entirely accurate, it's acceptable + // because we perform a thorough validation on the server side for the actual filter values. + // This variable is primarily for UI purposes. + const canReadOthersBookings = teamIdsWithPermission.length > 0; + + const featuresRepository = getFeaturesRepository(); + const featureOptInService = getFeatureOptInService(); + + const [featureFlags, featureStates] = await Promise.all([ + featuresRepository.getUserFeaturesStatus(userId, ["booking-audit"]), + featureOptInService.resolveFeatureStates({ + userId, + featureIds: ["bookings-v3"], + }), + ]); + + const bookingsV3Enabled = featureStates["bookings-v3"]?.effectiveEnabled ?? false; const bookingAuditEnabled = featureFlags["booking-audit"] ?? false; return ( @@ -69,7 +73,7 @@ const Page = async ({ params }: PageProps) => { headerClassName="bookings-shell-heading"> > { + const { userId, featureIds } = input; + const { orgId, teamIds } = await this.getUserOrgAndTeamIds(userId); + + return this.resolveFeatureStatesAcrossTeams({ + userId, + orgId, + teamIds, + featureIds, + }); + } + /** * List all opt-in features with their states for a user across teams. * Only returns features that are in the allowlist, globally enabled, and scoped to "user". */ - async listFeaturesForUser(input: { - userId: number; - orgId: number | null; - teamIds: number[]; - }): Promise { - const { userId, orgId, teamIds } = input; + async listFeaturesForUser(input: { userId: number }): Promise { + const { userId } = input; const userScopedFeatures = getOptInFeaturesForScope("user"); const featureIds = userScopedFeatures.map((config) => config.slug); - const resolvedStates = await this.resolveFeatureStatesAcrossTeams({ + const resolvedStates = await this.resolveFeatureStates({ userId, - orgId, - teamIds, featureIds, }); diff --git a/packages/features/feature-opt-in/services/IFeatureOptInService.ts b/packages/features/feature-opt-in/services/IFeatureOptInService.ts index 97e419006f..e8e61d55b6 100644 --- a/packages/features/feature-opt-in/services/IFeatureOptInService.ts +++ b/packages/features/feature-opt-in/services/IFeatureOptInService.ts @@ -64,11 +64,11 @@ export interface IFeatureOptInService { teamIds: number[]; featureIds: FeatureId[]; }): Promise>; - listFeaturesForUser(input: { + resolveFeatureStates(input: { userId: number; - orgId: number | null; - teamIds: number[]; - }): Promise; + featureIds: FeatureId[]; + }): Promise>; + listFeaturesForUser(input: { userId: number }): Promise; listFeaturesForTeam(input: { teamId: number; parentOrgId?: number | null; diff --git a/packages/trpc/server/routers/viewer/featureOptIn/_router.ts b/packages/trpc/server/routers/viewer/featureOptIn/_router.ts index 5b685c8af4..bbe32fdfa7 100644 --- a/packages/trpc/server/routers/viewer/featureOptIn/_router.ts +++ b/packages/trpc/server/routers/viewer/featureOptIn/_router.ts @@ -3,7 +3,6 @@ import { getTeamFeatureRepository } from "@calcom/features/di/containers/TeamFea import { getUserFeatureRepository } from "@calcom/features/di/containers/UserFeatureRepository"; import { TeamRepository } from "@calcom/features/ee/teams/repositories/TeamRepository"; import { isOptInFeature } from "@calcom/features/feature-opt-in/config"; -import { MembershipRepository } from "@calcom/features/membership/repositories/MembershipRepository"; import { prisma } from "@calcom/prisma"; import { TRPCError } from "@trpc/server"; import type { ZodEnum } from "zod"; @@ -22,27 +21,6 @@ const featureOptInService: ReturnType = getFeatur const teamFeatureRepository: ReturnType = getTeamFeatureRepository(); const userFeatureRepository: ReturnType = getUserFeatureRepository(); const teamRepository: TeamRepository = new TeamRepository(prisma); -const membershipRepository: MembershipRepository = new MembershipRepository(prisma); - -async function getUserOrgAndTeamIds(userId: number): Promise<{ orgId: number | null; teamIds: number[] }> { - const memberships = await membershipRepository.findAllByUserId({ - userId, - filters: { accepted: true }, - }); - - let orgId: number | null = null; - const teamIds: number[] = []; - - for (const membership of memberships) { - if (membership.team.isOrganization) { - orgId = membership.teamId; - } else { - teamIds.push(membership.teamId); - } - } - - return { orgId, teamIds }; -} export const featureOptInRouter = router({ /** @@ -50,12 +28,8 @@ export const featureOptInRouter = router({ * This considers all teams/orgs the user belongs to. */ listForUser: authedProcedure.query(async ({ ctx }) => { - const { orgId, teamIds } = await getUserOrgAndTeamIds(ctx.user.id); - return featureOptInService.listFeaturesForUser({ userId: ctx.user.id, - orgId, - teamIds, }); }),