perf: migrate listHandler to AttributeRepository + cache attributes fetching in RSCs (#21552)

* refactor: migrate listHandler to AttributeRepository

Co-Authored-By: benny@cal.com <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 <benny@cal.com>
Co-authored-by: hbjORbj <sldisek783@gmail.com>
This commit is contained in:
devin-ai-integration[bot]
2025-05-29 15:01:44 -04:00
committed by GitHub
co-authored by benny@cal.com hbjORbj
parent c3add4bae8
commit e115095198
7 changed files with 39 additions and 41 deletions
@@ -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 (
<MembersView org={org} teams={teams} facetedTeamValues={facetedTeamValues} attributes={attributes} />
);
};
export default ServerPageWrapper;
export { default } from "../../members/page";
@@ -0,0 +1,7 @@
"use server";
import { revalidateTag } from "next/cache";
export async function revalidateAttributesList() {
revalidateTag("viewer.attributes.list");
}
@@ -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 (
<MembersView org={org} teams={teams} facetedTeamValues={facetedTeamValues} attributes={attributes} />
);
@@ -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");
@@ -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");
@@ -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,
},
});
}
}
@@ -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;