fix: hide insights submenu for free users (#25754)

* fix: hide insights submenu for free users

* fix: allow navigation access while plan pending

* refactor: update upgrade link to insights in navigation

* fix: addressed all the issues

* fix: prevent flash of premium features for free users during loading

Change from optimistic to pessimistic loading so free users don't
briefly see the Insights submenu while plan status is being fetched.

---------

Co-authored-by: Dhairyashil <dhairyashil10101010@gmail.com>
This commit is contained in:
Kartik Labhshetwar
2025-12-11 21:04:13 +00:00
committed by GitHub
co-authored by Dhairyashil
parent c7bb28d64c
commit acf2f53daf
@@ -3,6 +3,7 @@ import { useMemo } from "react";
import { useIsEmbed } from "@calcom/embed-core/embed-iframe";
import UnconfirmedBookingBadge from "@calcom/features/bookings/UnconfirmedBookingBadge";
import { useHasPaidPlan } from "@calcom/features/billing/hooks/useHasPaidPlan";
import {
useOrgBranding,
type OrganizationBranding,
@@ -16,7 +17,19 @@ import { NavigationItem, MobileNavigationItem, MobileNavigationMoreItem } from "
export const MORE_SEPARATOR_NAME = "more";
const getNavigationItems = (orgBranding: OrganizationBranding): NavigationItemType[] => [
const preserveBookingsQueryParams = ({
prevPathname,
nextPathname,
}: {
prevPathname: string | null;
nextPathname: string;
}) => Boolean(prevPathname?.startsWith("/bookings/")) && nextPathname.startsWith("/bookings/");
const getNavigationItems = (
orgBranding: OrganizationBranding,
hasInsightsAccess: boolean
): NavigationItemType[] => [
{
name: "event_types_page_title",
href: "/event-types",
@@ -106,29 +119,31 @@ const getNavigationItems = (orgBranding: OrganizationBranding): NavigationItemTy
icon: "chart-bar",
isCurrent: ({ pathname: path, item }) => path?.startsWith(item.href) ?? false,
moreOnMobile: true,
child: [
{
name: "bookings",
href: "/insights",
isCurrent: ({ pathname: path }) => path === "/insights",
},
{
name: "routing",
href: "/insights/routing",
isCurrent: ({ pathname: path }) => path?.startsWith("/insights/routing") ?? false,
},
{
name: "router_position",
href: "/insights/router-position",
isCurrent: ({ pathname: path }) => path?.startsWith("/insights/router-position") ?? false,
},
{
name: "call_history",
href: "/insights/call-history",
// icon: "phone",
isCurrent: ({ pathname: path }) => path?.startsWith("/insights/call-history") ?? false,
},
],
child: hasInsightsAccess
? [
{
name: "bookings",
href: "/insights",
isCurrent: ({ pathname: path }) => path === "/insights",
},
{
name: "routing",
href: "/insights/routing",
isCurrent: ({ pathname: path }) => path?.startsWith("/insights/routing") ?? false,
},
{
name: "router_position",
href: "/insights/router-position",
isCurrent: ({ pathname: path }) => path?.startsWith("/insights/router-position") ?? false,
},
{
name: "call_history",
href: "/insights/call-history",
// icon: "phone",
isCurrent: ({ pathname: path }) => path?.startsWith("/insights/call-history") ?? false,
},
]
: undefined,
},
];
@@ -184,8 +199,12 @@ const platformNavigationItems: NavigationItemType[] = [
const useNavigationItems = (isPlatformNavigation = false) => {
const orgBranding = useOrgBranding();
const { hasPaidPlan, isPending } = useHasPaidPlan();
return useMemo(() => {
const items = !isPlatformNavigation ? getNavigationItems(orgBranding) : platformNavigationItems;
const hasInsightsAccess = !isPending && !!hasPaidPlan;
const items = !isPlatformNavigation
? getNavigationItems(orgBranding, hasInsightsAccess)
: platformNavigationItems;
const desktopNavigationItems = items.filter((item) => item.name !== MORE_SEPARATOR_NAME);
const mobileNavigationBottomItems = items.filter(
@@ -196,7 +215,7 @@ const useNavigationItems = (isPlatformNavigation = false) => {
);
return { desktopNavigationItems, mobileNavigationBottomItems, mobileNavigationMoreItems };
}, [isPlatformNavigation, orgBranding]);
}, [hasPaidPlan, isPending, isPlatformNavigation, orgBranding]);
};
export const Navigation = ({ isPlatformNavigation = false }: { isPlatformNavigation?: boolean }) => {