import { useTranslation } from "react-i18next"; import { Lock, ShoppingCart } from "lucide-react"; import type { Edition } from "../../services/licenseService"; import type { FeatureKey } from "../../shared/entitlements"; import { upsellCtaFor } from "../../shared/upsellCta"; import { useLicenseContext } from "../../contexts/LicenseContext"; import PurchaseButton from "./PurchaseButton"; import HaveKeyButton from "./HaveKeyButton"; interface UpsellGateProps { feature: FeatureKey; requiredTier: Edition; /** * Called right after "I already have a key" navigates to the license card. Lets * modal hosts (the ProfileSwitcher upsell dialog, ProfileFormModal) close * themselves — the Sidebar stays mounted across navigations, so an uncontrolled * modal would linger on top of the destination page. The purchase button never * calls it: the product page opens in the browser and the host stays open. * Optional: route-level usage ignores it. */ onNavigate?: () => void; } /** * Full locked screen shown in place of a gated module (soft paywall). * * Two CTAs, the first one decided by `upsellCtaFor` (issue #346): * - "Get " — active for Base, which opens the product page in the browser * (PurchaseButton); disabled for Premium, with a note, since no Premium offer * is sold yet. * - "I already have a key" — navigates to the license card at /settings/users. * Rendered only inside BrowserRouter (HaveKeyButton), so the gate also works in * ProfileFormModal opened from ProfileSelectionPage, outside the router. * * The tier label reuses the existing `license.editions.*` keys; the feature * description comes from `upsell.features.` (FR/EN). * * When the stored key was revoked by the license server (#365), one line says so above * the CTAs: the lock would otherwise appear with no reason given. */ export default function UpsellGate({ feature, requiredTier, onNavigate }: UpsellGateProps) { const { t } = useTranslation(); const { revoked } = useLicenseContext(); const tier = t(`license.editions.${requiredTier}`); const cta = upsellCtaFor(requiredTier); return (

{t("upsell.title", { tier })}

{t(`upsell.features.${feature}`)}

{revoked && (

{t("upsell.revoked")}

)}
{cta.purchasable ? ( ) : (
{cta.noteKey && (

{t(cta.noteKey)}

)}
)}
); }