Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { createRouterCaller } from "app/_trpc/context";
import type { PageProps } from "app/_types";
import { _generateMetadata } from "app/_utils";
import { redirect } from "next/navigation";
import { z } from "zod";

import { AppCategories } from "@calcom/prisma/enums";
import { appsRouter } from "@calcom/trpc/server/routers/viewer/apps/_router";
import { calendarsRouter } from "@calcom/trpc/server/routers/viewer/calendars/_router";

import InstalledApps from "~/apps/installed/[category]/installed-category-view";

Expand All @@ -28,7 +31,24 @@ const InstalledAppsWrapper = async ({ params }: PageProps) => {
redirect("/apps/installed/calendar");
}

return <InstalledApps category={parsedParams.data.category} />;
const [calendarsCaller, appsCaller] = await Promise.all([
createRouterCaller(calendarsRouter),
createRouterCaller(appsRouter),
]);

const connectedCalendars = await calendarsCaller.connectedCalendars();
const installedCalendars = await appsCaller.integrations({
variant: "calendar",
onlyInstalled: true,
});

return (
<InstalledApps
connectedCalendars={connectedCalendars}
installedCalendars={installedCalendars}
category={parsedParams.data.category}
/>
);
};

export default InstalledAppsWrapper;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
"use server";

import { revalidatePath } from "next/cache";

export async function revalidateSettingsAppearance() {
revalidatePath("/settings/my-account/appearance");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { SkeletonLoader } from "~/settings/my-account/appearance-skeleton";

export default function Loading() {
return <SkeletonLoader />;
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
import { createRouterCaller } from "app/_trpc/context";
import { _generateMetadata } from "app/_utils";
import { getTranslate } from "app/_utils";
import { cookies, headers } from "next/headers";
import { redirect } from "next/navigation";

import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader";
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
import { IS_SELF_HOSTED } from "@calcom/lib/constants";
import hasKeyInMetadata from "@calcom/lib/hasKeyInMetadata";
import { meRouter } from "@calcom/trpc/server/routers/viewer/me/_router";
import { getCachedHasTeamPlan } from "@calcom/web/app/cache/membership";

import { buildLegacyRequest } from "@lib/buildLegacyCtx";

import AppearancePage from "~/settings/my-account/appearance-view";

Expand All @@ -15,13 +23,29 @@ export const generateMetadata = async () =>
);

const Page = async () => {
const t = await getTranslate();
const session = await getServerSession({ req: buildLegacyRequest(await headers(), await cookies()) });
const userId = session?.user?.id;
const redirectUrl = "/auth/login?callbackUrl=/settings/my-account/appearance";

return (
<SettingsHeader title={t("appearance")} description={t("appearance_description")}>
<AppearancePage />
</SettingsHeader>
);
if (!userId) {
redirect(redirectUrl);
}

const [meCaller, hasTeamPlan] = await Promise.all([
createRouterCaller(meRouter),
getCachedHasTeamPlan(userId),
]);

const user = await meCaller.get();

if (!user) {
redirect(redirectUrl);
}
const isCurrentUsernamePremium =
user && hasKeyInMetadata(user, "isPremium") ? !!user.metadata.isPremium : false;
const hasPaidPlan = IS_SELF_HOSTED ? true : hasTeamPlan?.hasTeamPlan || isCurrentUsernamePremium;

return <AppearancePage user={user} hasPaidPlan={hasPaidPlan} />;
};
Comment on lines 25 to 49
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider consolidating redirect logic and adding error handling.

The current implementation has room for improvement:

  1. The redirect URL is repeated and could be extracted to a constant
  2. Missing error handling for the async operations
  3. The concurrent fetch could fail independently
 const Page = async () => {
   const session = await getServerSession({ req: buildLegacyRequest(await headers(), await cookies()) });
   const userId = session?.user?.id;
-  const redirectUrl = "/auth/login?callbackUrl=/settings/my-account/appearance";
+  const REDIRECT_URL = "/auth/login?callbackUrl=/settings/my-account/appearance";

   if (!userId) {
-    redirect(redirectUrl);
+    redirect(REDIRECT_URL);
   }

-  const [meCaller, hasTeamPlan] = await Promise.all([
-    createRouterCaller(meRouter),
-    getCachedHasTeamPlan(userId),
-  ]);
+  try {
+    const [meCaller, hasTeamPlan] = await Promise.all([
+      createRouterCaller(meRouter),
+      getCachedHasTeamPlan(userId),
+    ]);

-  const user = await meCaller.get();
+    const user = await meCaller.get();

-  if (!user) {
-    redirect(redirectUrl);
-  }
+    if (!user) {
+      redirect(REDIRECT_URL);
+    }
   const isCurrentUsernamePremium =
     user && hasKeyInMetadata(user, "isPremium") ? !!user.metadata.isPremium : false;
   const hasPaidPlan = IS_SELF_HOSTED ? true : hasTeamPlan?.hasTeamPlan || isCurrentUsernamePremium;

   return <AppearancePage user={user} hasPaidPlan={hasPaidPlan} />;
+  } catch (error) {
+    // Log error for monitoring
+    console.error("Failed to fetch user data:", error);
+    redirect(REDIRECT_URL);
+  }
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const Page = async () => {
const t = await getTranslate();
const session = await getServerSession({ req: buildLegacyRequest(await headers(), await cookies()) });
const userId = session?.user?.id;
const redirectUrl = "/auth/login?callbackUrl=/settings/my-account/appearance";
return (
<SettingsHeader title={t("appearance")} description={t("appearance_description")}>
<AppearancePage />
</SettingsHeader>
);
if (!userId) {
redirect(redirectUrl);
}
const [meCaller, hasTeamPlan] = await Promise.all([
createRouterCaller(meRouter),
getCachedHasTeamPlan(userId),
]);
const user = await meCaller.get();
if (!user) {
redirect(redirectUrl);
}
const isCurrentUsernamePremium =
user && hasKeyInMetadata(user, "isPremium") ? !!user.metadata.isPremium : false;
const hasPaidPlan = IS_SELF_HOSTED ? true : hasTeamPlan?.hasTeamPlan || isCurrentUsernamePremium;
return <AppearancePage user={user} hasPaidPlan={hasPaidPlan} />;
};
const Page = async () => {
const session = await getServerSession({
req: buildLegacyRequest(await headers(), await cookies()),
});
const userId = session?.user?.id;
const REDIRECT_URL = "/auth/login?callbackUrl=/settings/my-account/appearance";
if (!userId) {
redirect(REDIRECT_URL);
}
try {
const [meCaller, hasTeamPlan] = await Promise.all([
createRouterCaller(meRouter),
getCachedHasTeamPlan(userId),
]);
const user = await meCaller.get();
if (!user) {
redirect(REDIRECT_URL);
}
const isCurrentUsernamePremium =
user && hasKeyInMetadata(user, "isPremium")
? !!user.metadata.isPremium
: false;
const hasPaidPlan = IS_SELF_HOSTED
? true
: hasTeamPlan?.hasTeamPlan || isCurrentUsernamePremium;
return <AppearancePage user={user} hasPaidPlan={hasPaidPlan} />;
} catch (error) {
// Log error for monitoring
console.error("Failed to fetch user data:", error);
redirect(REDIRECT_URL);
}
};
🤖 Prompt for AI Agents
In
apps/web/app/(use-page-wrapper)/settings/(settings-layout)/my-account/appearance/page.tsx
lines 25 to 49, consolidate the redirect URL into a single constant to avoid
repetition. Add try-catch blocks around the async operations like
getServerSession, createRouterCaller, getCachedHasTeamPlan, and meCaller.get to
handle potential errors gracefully. Ensure that if any of these async calls
fail, the user is redirected appropriately or an error state is handled,
preventing unhandled promise rejections and improving reliability.


export default Page;
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { CalendarListContainerSkeletonLoader } from "@components/apps/CalendarListContainer";

export default function Loading() {
return <CalendarListContainerSkeletonLoader />;
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { createRouterCaller } from "app/_trpc/context";
import { _generateMetadata } from "app/_utils";
import { getTranslate } from "app/_utils";

import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader";
import { Button } from "@calcom/ui/components/button";
import { appsRouter } from "@calcom/trpc/server/routers/viewer/apps/_router";
import { calendarsRouter } from "@calcom/trpc/server/routers/viewer/calendars/_router";

import { CalendarListContainer } from "@components/apps/CalendarListContainer";

Expand All @@ -16,25 +16,18 @@ export const generateMetadata = async () =>
);

const Page = async () => {
const t = await getTranslate();

const AddCalendarButton = () => {
return (
<>
<Button color="secondary" StartIcon="plus" href="/apps/categories/calendar">
{t("add_calendar")}
</Button>
</>
);
};
const [calendarsCaller, appsCaller] = await Promise.all([
createRouterCaller(calendarsRouter),
createRouterCaller(appsRouter),
]);

const connectedCalendars = await calendarsCaller.connectedCalendars();
const installedCalendars = await appsCaller.integrations({
variant: "calendar",
onlyInstalled: true,
});
return (
<SettingsHeader
title={t("calendars")}
description={t("calendars_description")}
CTA={<AddCalendarButton />}>
<CalendarListContainer />
</SettingsHeader>
<CalendarListContainer connectedCalendars={connectedCalendars} installedCalendars={installedCalendars} />
);
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { _generateMetadata } from "app/_utils";
import { getTranslate } from "app/_utils";

import { ConferencingAppsViewWebWrapper } from "@calcom/atoms/connect/conferencing-apps/ConferencingAppsViewWebWrapper";

Expand All @@ -13,15 +12,7 @@ export const generateMetadata = async () =>
);

const Page = async () => {
const t = await getTranslate();

return (
<ConferencingAppsViewWebWrapper
title={t("conferencing")}
description={t("conferencing_description")}
add={t("add")}
/>
);
return <ConferencingAppsViewWebWrapper />;
};

export default Page;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
"use server";

import { revalidatePath } from "next/cache";

export async function revalidateSettingsGeneral() {
revalidatePath("/settings/my-account/general");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { SkeletonLoader } from "~/settings/my-account/general-skeleton";

export default function Loading() {
return <SkeletonLoader />;
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import { createRouterCaller } from "app/_trpc/context";
import { _generateMetadata } from "app/_utils";
import { getTranslate } from "app/_utils";
import { revalidatePath } from "next/cache";
import { cookies, headers } from "next/headers";
import { redirect } from "next/navigation";

import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader";
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
import { meRouter } from "@calcom/trpc/server/routers/viewer/me/_router";
import { getTravelSchedule } from "@calcom/web/app/cache/travelSchedule";

import GeneralQueryView from "~/settings/my-account/general-view";
import { buildLegacyRequest } from "@lib/buildLegacyCtx";

import GeneralView from "~/settings/my-account/general-view";

export const generateMetadata = async () =>
await _generateMetadata(
Expand All @@ -16,17 +21,20 @@ export const generateMetadata = async () =>
);

const Page = async () => {
const t = await getTranslate();
const revalidatePage = async () => {
"use server";
revalidatePath("settings/my-account/general");
};

return (
<SettingsHeader title={t("general")} description={t("general_description")} borderInShellHeader={true}>
<GeneralQueryView revalidatePage={revalidatePage} />
</SettingsHeader>
);
const session = await getServerSession({ req: buildLegacyRequest(await headers(), await cookies()) });
const userId = session?.user?.id;
const redirectUrl = "/auth/login?callbackUrl=/settings/my-account/general";

if (!userId) {
return redirect(redirectUrl);
}
Comment on lines +28 to +30
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix incorrect return statement with redirect.

The redirect() function from Next.js navigation throws internally and doesn't return a value. Remove the return statement.

   if (!userId) {
-    return redirect(redirectUrl);
+    redirect(redirectUrl);
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!userId) {
return redirect(redirectUrl);
}
if (!userId) {
redirect(redirectUrl);
}
🤖 Prompt for AI Agents
In
apps/web/app/(use-page-wrapper)/settings/(settings-layout)/my-account/general/page.tsx
around lines 28 to 30, the code incorrectly uses 'return redirect(redirectUrl);'
but the redirect() function throws internally and does not return a value.
Remove the 'return' keyword and just call 'redirect(redirectUrl);' to fix the
issue.


const meCaller = await createRouterCaller(meRouter);
const [user, travelSchedules] = await Promise.all([meCaller.get(), getTravelSchedule(userId)]);
if (!user) {
redirect(redirectUrl);
}
return <GeneralView user={user} travelSchedules={travelSchedules ?? []} />;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Component no longer uses SettingsHeader which affects UI consistency across settings pages

};
Comment on lines 23 to 38
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add error handling for concurrent data fetching.

Similar to the appearance page, this implementation lacks error handling for the async operations.

 const Page = async () => {
   const session = await getServerSession({ req: buildLegacyRequest(await headers(), await cookies()) });
   const userId = session?.user?.id;
-  const redirectUrl = "/auth/login?callbackUrl=/settings/my-account/general";
+  const REDIRECT_URL = "/auth/login?callbackUrl=/settings/my-account/general";

   if (!userId) {
-    return redirect(redirectUrl);
+    redirect(REDIRECT_URL);
   }

-  const meCaller = await createRouterCaller(meRouter);
-  const [user, travelSchedules] = await Promise.all([meCaller.get(), getTravelSchedule(userId)]);
-  if (!user) {
-    redirect(redirectUrl);
-  }
-  return <GeneralView user={user} travelSchedules={travelSchedules ?? []} />;
+  try {
+    const meCaller = await createRouterCaller(meRouter);
+    const [user, travelSchedules] = await Promise.all([meCaller.get(), getTravelSchedule(userId)]);
+    if (!user) {
+      redirect(REDIRECT_URL);
+    }
+    return <GeneralView user={user} travelSchedules={travelSchedules ?? []} />;
+  } catch (error) {
+    console.error("Failed to fetch user data:", error);
+    redirect(REDIRECT_URL);
+  }
 };
🤖 Prompt for AI Agents
In
apps/web/app/(use-page-wrapper)/settings/(settings-layout)/my-account/general/page.tsx
lines 23 to 38, the concurrent data fetching with Promise.all lacks error
handling. Wrap the Promise.all call in a try-catch block to catch any errors
from meCaller.get() or getTravelSchedule(userId). In the catch block, handle the
error appropriately, such as logging it and redirecting to the login page or
showing an error message, to prevent unhandled promise rejections and improve
robustness.


export default Page;
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
import { _generateMetadata } from "app/_utils";
import { getTranslate } from "app/_utils";

import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader";
import CreateNewOutOfOfficeEntryButton from "@calcom/features/settings/outOfOffice/CreateNewOutOfOfficeEntryButton";
import OutOfOfficeEntriesList from "@calcom/features/settings/outOfOffice/OutOfOfficeEntriesList";
import { OutOfOfficeToggleGroup } from "@calcom/features/settings/outOfOffice/OutOfOfficeToggleGroup";

export const generateMetadata = async () =>
await _generateMetadata(
Expand All @@ -15,22 +11,8 @@ export const generateMetadata = async () =>
"/settings/my-account/out-of-office"
);

const Page = async () => {
const t = await getTranslate();

return (
<SettingsHeader
title={t("out_of_office")}
description={t("out_of_office_description")}
CTA={
<div className="flex gap-2">
<OutOfOfficeToggleGroup />
<CreateNewOutOfOfficeEntryButton data-testid="add_entry_ooo" />
</div>
}>
<OutOfOfficeEntriesList />
</SettingsHeader>
);
const Page = () => {
return <OutOfOfficeEntriesList />;
};

export default Page;
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { SkeletonLoader } from "~/settings/my-account/profile-skeleton";

export default function Loading() {
return <SkeletonLoader />;
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { createRouterCaller } from "app/_trpc/context";
import { _generateMetadata } from "app/_utils";
import { getTranslate } from "app/_utils";
import { redirect } from "next/navigation";

import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader";
import { APP_NAME } from "@calcom/lib/constants";
import { meRouter } from "@calcom/trpc/server/routers/viewer/me/_router";

import ProfileView from "~/settings/my-account/profile-view";

Expand All @@ -16,16 +16,13 @@ export const generateMetadata = async () =>
);

const Page = async () => {
const t = await getTranslate();
const meCaller = await createRouterCaller(meRouter);
const user = await meCaller.get({ includePasswordAdded: true });
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The get() call uses an object with includePasswordAdded: true, which may result in overfetching if the implementation uses Prisma's include instead of select. Per project guidelines, always use select to fetch only required fields and avoid unnecessary data exposure.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The get() call uses an object with includePasswordAdded: true, which may result in overfetching if the implementation uses Prisma's include instead of select. Per project guidelines, always use select to fetch only required fields and avoid unnecessary data exposure.

if (!user) {
redirect("/auth/login");
}

return (
<SettingsHeader
title={t("profile")}
description={t("profile_description", { appName: APP_NAME })}
borderInShellHeader={true}>
<ProfileView />
</SettingsHeader>
);
return <ProfileView user={user} />;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Directly passing the user object as a prop may result in prop drilling if ProfileView passes it further down. Prefer composition or context for user data to avoid deep prop drilling, per project guidelines.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Directly passing the user object as a prop may result in prop drilling if ProfileView passes it further down. Prefer composition or context for user data to avoid deep prop drilling, per project guidelines.

};

export default Page;
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import { getTranslate } from "app/_utils";
import { _generateMetadata } from "app/_utils";

import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader";

import PushNotificationsView from "~/settings/my-account/push-notifications-view";

export const generateMetadata = async () =>
Expand All @@ -14,17 +11,8 @@ export const generateMetadata = async () =>
"/settings/my-account/push-notifications"
);

const Page = async () => {
const t = await getTranslate();

return (
<SettingsHeader
title={t("push_notifications")}
description={t("push_notifications_description")}
borderInShellHeader={true}>
<PushNotificationsView />
</SettingsHeader>
);
const Page = () => {
return <PushNotificationsView />;
};

export default Page;
27 changes: 27 additions & 0 deletions apps/web/app/cache/membership.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"use server";

import { revalidateTag, unstable_cache } from "next/cache";

import { NEXTJS_CACHE_TTL } from "@calcom/lib/constants";
import { MembershipRepository } from "@calcom/lib/server/repository/membership";

const CACHE_TAGS = {
HAS_TEAM_PLAN: "MembershipRepository.findFirstAcceptedMembershipByUserId",
} as const;

export const getCachedHasTeamPlan = unstable_cache(
async (userId: number) => {
const hasTeamPlan = await MembershipRepository.findFirstAcceptedMembershipByUserId(userId);

return { hasTeamPlan: !!hasTeamPlan };
},
["getCachedHasTeamPlan"],
{
revalidate: NEXTJS_CACHE_TTL,
tags: [CACHE_TAGS.HAS_TEAM_PLAN],
}
);
Comment on lines +12 to +23
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

⚠️ Potential issue

Add error handling and consider cache key uniqueness.

The cache implementation needs improvement:

  1. No error handling if the repository call fails
  2. The cache key should include the userId for uniqueness
  3. Consider the implications of using unstable_cache
 export const getCachedHasTeamPlan = unstable_cache(
   async (userId: number) => {
-    const hasTeamPlan = await MembershipRepository.findFirstAcceptedMembershipByUserId(userId);
-
-    return { hasTeamPlan: !!hasTeamPlan };
+    try {
+      const hasTeamPlan = await MembershipRepository.findFirstAcceptedMembershipByUserId(userId);
+      return { hasTeamPlan: !!hasTeamPlan };
+    } catch (error) {
+      console.error(`Failed to fetch team plan for user ${userId}:`, error);
+      // Return false as a safe default when cache fetch fails
+      return { hasTeamPlan: false };
+    }
   },
-  ["getCachedHasTeamPlan"],
+  ["getCachedHasTeamPlan", (userId) => userId.toString()],
   {
     revalidate: NEXTJS_CACHE_TTL,
     tags: [CACHE_TAGS.HAS_TEAM_PLAN],
   }
 );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const getCachedHasTeamPlan = unstable_cache(
async (userId: number) => {
const hasTeamPlan = await MembershipRepository.findFirstAcceptedMembershipByUserId(userId);
return { hasTeamPlan: !!hasTeamPlan };
},
["getCachedHasTeamPlan"],
{
revalidate: NEXTJS_CACHE_TTL,
tags: [CACHE_TAGS.HAS_TEAM_PLAN],
}
);
export const getCachedHasTeamPlan = unstable_cache(
async (userId: number) => {
try {
const hasTeamPlan = await MembershipRepository.findFirstAcceptedMembershipByUserId(userId);
return { hasTeamPlan: !!hasTeamPlan };
} catch (error) {
console.error(`Failed to fetch team plan for user ${userId}:`, error);
// Return false as a safe default when cache fetch fails
return { hasTeamPlan: false };
}
},
["getCachedHasTeamPlan", (userId) => userId.toString()],
{
revalidate: NEXTJS_CACHE_TTL,
tags: [CACHE_TAGS.HAS_TEAM_PLAN],
}
);
🤖 Prompt for AI Agents
In apps/web/app/cache/membership.ts around lines 12 to 23, add error handling to
catch and handle any errors from
MembershipRepository.findFirstAcceptedMembershipByUserId to prevent unhandled
exceptions. Modify the cache key to include the userId parameter to ensure cache
entries are unique per user. Review the use of unstable_cache for potential
stability or behavior concerns and document or adjust usage accordingly.


export const revalidateHasTeamPlan = async () => {
revalidateTag(CACHE_TAGS.HAS_TEAM_PLAN);
};
11 changes: 11 additions & 0 deletions apps/web/app/cache/path/settings/my-account/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
"use server";

import { revalidatePath } from "next/cache";

export async function revalidateSettingsProfile() {
revalidatePath("/settings/my-account/profile");
}

export async function revalidateSettingsCalendars() {
revalidatePath("/settings/my-account/calendars");
}
Loading
Loading