From e115095198bc9f0b95ccd901ab8597eb771da9ff Mon Sep 17 00:00:00 2001 From: "devin-ai-integration[bot]" <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 29 May 2025 15:01:44 -0400 Subject: [PATCH] perf: migrate listHandler to AttributeRepository + cache attributes fetching in RSCs (#21552) * refactor: migrate listHandler to AttributeRepository Co-Authored-By: benny@cal.com * cache attributes list --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: benny@cal.com Co-authored-by: hbjORbj --- .../organizations/[id]/members/page.tsx | 24 +------------------ .../settings/organizations/members/actions.ts | 7 ++++++ .../settings/organizations/members/page.tsx | 21 +++++++++------- .../attributes/DeleteAttributeModal.tsx | 2 ++ .../attributes/attributes-edit-view.tsx | 4 +++- packages/lib/server/repository/attribute.ts | 11 +++++++++ .../routers/viewer/attributes/list.handler.ts | 11 ++------- 7 files changed, 39 insertions(+), 41 deletions(-) create mode 100644 apps/web/app/(use-page-wrapper)/settings/organizations/members/actions.ts diff --git a/apps/web/app/(use-page-wrapper)/settings/organizations/[id]/members/page.tsx b/apps/web/app/(use-page-wrapper)/settings/organizations/[id]/members/page.tsx index 5e15a03a6a..3d269da816 100644 --- a/apps/web/app/(use-page-wrapper)/settings/organizations/[id]/members/page.tsx +++ b/apps/web/app/(use-page-wrapper)/settings/organizations/[id]/members/page.tsx @@ -1,11 +1,5 @@ -import { createRouterCaller } from "app/_trpc/context"; import { _generateMetadata } from "app/_utils"; -import { attributesRouter } from "@calcom/trpc/server/routers/viewer/attributes/_router"; -import { viewerOrganizationsRouter } from "@calcom/trpc/server/routers/viewer/organizations/_router"; - -import { MembersView } from "~/members/members-view"; - export const generateMetadata = async ({ params }: { params: Promise<{ id: string }> }) => await _generateMetadata( (t) => t("organization_members"), @@ -15,20 +9,4 @@ export const generateMetadata = async ({ params }: { params: Promise<{ id: strin `/settings/organizations/${(await params).id}/members` ); -const ServerPageWrapper = async () => { - const [orgCaller, attributesCaller] = await Promise.all([ - createRouterCaller(viewerOrganizationsRouter), - createRouterCaller(attributesRouter), - ]); - const [org, teams, facetedTeamValues, attributes] = await Promise.all([ - orgCaller.listCurrent(), - orgCaller.getTeams(), - orgCaller.getFacetedValues(), - attributesCaller.list(), - ]); - return ( - - ); -}; - -export default ServerPageWrapper; +export { default } from "../../members/page"; diff --git a/apps/web/app/(use-page-wrapper)/settings/organizations/members/actions.ts b/apps/web/app/(use-page-wrapper)/settings/organizations/members/actions.ts new file mode 100644 index 0000000000..061f4a88a8 --- /dev/null +++ b/apps/web/app/(use-page-wrapper)/settings/organizations/members/actions.ts @@ -0,0 +1,7 @@ +"use server"; + +import { revalidateTag } from "next/cache"; + +export async function revalidateAttributesList() { + revalidateTag("viewer.attributes.list"); +} diff --git a/apps/web/app/(use-page-wrapper)/settings/organizations/members/page.tsx b/apps/web/app/(use-page-wrapper)/settings/organizations/members/page.tsx index 6a499d66af..82cdb79354 100644 --- a/apps/web/app/(use-page-wrapper)/settings/organizations/members/page.tsx +++ b/apps/web/app/(use-page-wrapper)/settings/organizations/members/page.tsx @@ -1,7 +1,8 @@ import { createRouterCaller } from "app/_trpc/context"; import { _generateMetadata } from "app/_utils"; +import { unstable_cache } from "next/cache"; -import { attributesRouter } from "@calcom/trpc/server/routers/viewer/attributes/_router"; +import { AttributeRepository } from "@calcom/lib/server/repository/attribute"; import { viewerOrganizationsRouter } from "@calcom/trpc/server/routers/viewer/organizations/_router"; import { MembersView } from "~/members/members-view"; @@ -15,18 +16,22 @@ export const generateMetadata = async () => "/settings/organizations/members" ); +const getCachedAttributes = unstable_cache( + async (orgId: number) => { + return await AttributeRepository.findAllByOrgIdWithOptions({ orgId }); + }, + ["viewer.attributes.list"], + { revalidate: 3600 } // Cache for 1 hour +); + const Page = async () => { - const [orgCaller, attributesCaller] = await Promise.all([ - createRouterCaller(viewerOrganizationsRouter), - createRouterCaller(attributesRouter), - ]); - const [org, teams, facetedTeamValues, attributes] = await Promise.all([ + const orgCaller = await createRouterCaller(viewerOrganizationsRouter); + const [org, teams, facetedTeamValues] = await Promise.all([ orgCaller.listCurrent(), orgCaller.getTeams(), orgCaller.getFacetedValues(), - attributesCaller.list(), ]); - + const attributes = await getCachedAttributes(org.id); return ( ); diff --git a/packages/features/ee/organizations/pages/settings/attributes/DeleteAttributeModal.tsx b/packages/features/ee/organizations/pages/settings/attributes/DeleteAttributeModal.tsx index e0db01c860..5dc4a80d43 100644 --- a/packages/features/ee/organizations/pages/settings/attributes/DeleteAttributeModal.tsx +++ b/packages/features/ee/organizations/pages/settings/attributes/DeleteAttributeModal.tsx @@ -6,6 +6,7 @@ import type { RouterOutputs } from "@calcom/trpc/react"; import { trpc } from "@calcom/trpc/react"; import { ConfirmationDialogContent } from "@calcom/ui/components/dialog"; import { showToast } from "@calcom/ui/components/toast"; +import { revalidateAttributesList } from "@calcom/web/app/(use-page-wrapper)/settings/organizations/members/actions"; type AttributeItemProps = RouterOutputs["viewer"]["attributes"]["list"][number]; @@ -23,6 +24,7 @@ export function DeleteAttributeModal({ onSuccess: () => { showToast(t("attribute_deleted_successfully"), "success"); utils.viewer.attributes.list.invalidate(); + revalidateAttributesList(); }, onError: (err) => { showToast(err.message, "error"); diff --git a/packages/features/ee/organizations/pages/settings/attributes/attributes-edit-view.tsx b/packages/features/ee/organizations/pages/settings/attributes/attributes-edit-view.tsx index 40caad1b0f..52cce1dcc1 100644 --- a/packages/features/ee/organizations/pages/settings/attributes/attributes-edit-view.tsx +++ b/packages/features/ee/organizations/pages/settings/attributes/attributes-edit-view.tsx @@ -7,8 +7,9 @@ import { z } from "zod"; import LicenseRequired from "@calcom/features/ee/common/components/LicenseRequired"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { trpc } from "@calcom/trpc/react"; -import { showToast } from "@calcom/ui/components/toast"; import { Button } from "@calcom/ui/components/button"; +import { showToast } from "@calcom/ui/components/toast"; +import { revalidateAttributesList } from "@calcom/web/app/(use-page-wrapper)/settings/organizations/members/actions"; import { AttributeForm } from "./AttributesForm"; @@ -37,6 +38,7 @@ function CreateAttributesPage() { id, }); utils.viewer.attributes.list.invalidate(); + revalidateAttributesList(); }, onError: (err) => { showToast(err.message, "error"); diff --git a/packages/lib/server/repository/attribute.ts b/packages/lib/server/repository/attribute.ts index ac3942b9f1..5c0ab24ee6 100644 --- a/packages/lib/server/repository/attribute.ts +++ b/packages/lib/server/repository/attribute.ts @@ -42,4 +42,15 @@ export class AttributeRepository { return result; } + + static async findAllByOrgIdWithOptions({ orgId }: { orgId: number }) { + return await prisma.attribute.findMany({ + where: { + teamId: orgId, + }, + include: { + options: true, + }, + }); + } } diff --git a/packages/trpc/server/routers/viewer/attributes/list.handler.ts b/packages/trpc/server/routers/viewer/attributes/list.handler.ts index dbb1be54fa..4bcb590b35 100644 --- a/packages/trpc/server/routers/viewer/attributes/list.handler.ts +++ b/packages/trpc/server/routers/viewer/attributes/list.handler.ts @@ -1,4 +1,4 @@ -import prisma from "@calcom/prisma"; +import { AttributeRepository } from "@calcom/lib/server/repository/attribute"; import { TRPCError } from "@trpc/server"; @@ -20,14 +20,7 @@ const listHandler = async (opts: GetOptions) => { }); } - return await prisma.attribute.findMany({ - where: { - teamId: org.id, - }, - include: { - options: true, - }, - }); + return await AttributeRepository.findAllByOrgIdWithOptions({ orgId: org.id }); }; export default listHandler;