diff --git a/apps/main/src/App.tsx b/apps/main/src/App.tsx index 707991c..6fbbe7d 100644 --- a/apps/main/src/App.tsx +++ b/apps/main/src/App.tsx @@ -3,6 +3,9 @@ import { ThemeProvider } from "@xtablo/shared/contexts/ThemeContext"; import { Toaster } from "@xtablo/ui/components/sonner"; import { BrowserRouter as Router, useRoutes } from "react-router-dom"; import { CookieBanner } from "./components/CookieBanner"; +import { TrialUpsellModal } from "./components/TrialUpsellModal"; +import { UpgradePanel } from "./components/UpgradePanel"; +import { UpgradeBlockProvider } from "./contexts/UpgradeBlockContext"; import { useCookieConsent } from "./hooks/useCookieConsent"; import { useDatadogRumViewName } from "./hooks/useDatadogRumViewName"; import { publicRoutes } from "./lib/publicRoutes"; @@ -21,9 +24,17 @@ const Routes = () => { return publicElement; } - // If app route matched, render it inside UserStoreProvider + // If app route matched, render it inside UserStoreProvider and UpgradeBlockProvider if (appElement) { - return {appElement}; + return ( + + + + + {appElement} + + + ); } // Neither matched, show 404 diff --git a/apps/main/src/components/NavigationBar.tsx b/apps/main/src/components/NavigationBar.tsx index ba99c44..925a28c 100644 --- a/apps/main/src/components/NavigationBar.tsx +++ b/apps/main/src/components/NavigationBar.tsx @@ -13,10 +13,12 @@ import { import { TypographyLarge, TypographyMuted } from "@xtablo/ui/components/typography"; import { cva, type VariantProps } from "class-variance-authority"; import { + AlertCircle, CalendarCheckIcon, CalendarIcon, Circle, ConstructionIcon, + CreditCard, FileTextIcon, Kanban, LogOutIcon, @@ -26,6 +28,7 @@ import { PlusIcon, SendIcon, SettingsIcon, + Sparkles, SquareKanban, } from "lucide-react"; import { useState } from "react"; @@ -34,6 +37,7 @@ import { useTranslation } from "react-i18next"; import { Link as RouterLink, useLocation } from "react-router-dom"; import { twMerge } from "tailwind-merge"; import { useLogout } from "../hooks/auth"; +import { useCreateCheckoutSession, useTrialExpiration } from "../hooks/stripe"; import { isProd, isStaging } from "../lib/env"; import { useIsReadOnlyUser, useUser } from "../providers/UserStoreProvider"; import { getXtabloIcon } from "../utils/iconHelpers"; @@ -279,7 +283,16 @@ export const SideNavigation = ({ isMobileMenuOpen }: { isMobileMenuOpen: boolean export function MainNavigation({ isCollapsed }: { isCollapsed: boolean }) { const location = useLocation(); const isReadOnly = useIsReadOnlyUser(); + const user = useUser(); const { t } = useTranslation("navigation"); + const { daysRemaining } = useTrialExpiration(); + const { mutate: createCheckout, isPending: checkoutPending } = useCreateCheckoutSession(); + + const STANDARD_MONTHLY_PRICE_ID = import.meta.env.VITE_STRIPE_STANDARD_MONTHLY_PRICE_ID || ""; + + // Show upsell for users in trial period (not beta, not paid, and daysRemaining exists) + const shouldShowUpsell = daysRemaining !== null && user.plan === "none" && !user.is_temporary; + const isUrgent = daysRemaining !== null && daysRemaining <= 3; type List = T[]; @@ -391,6 +404,77 @@ export function MainNavigation({ isCollapsed }: { isCollapsed: boolean }) { })}