From 3781a5bc3ac722ae390c2c8a2d5b28a0374499d7 Mon Sep 17 00:00:00 2001 From: Tony Giorgio Date: Sun, 13 Jul 2025 15:17:35 -0500 Subject: [PATCH 1/4] Remove starter plan --- frontend/src/components/Marketing.tsx | 7 +- frontend/src/config/pricingConfig.tsx | 6 +- frontend/src/routes/pricing.tsx | 378 ++++++++++++++------------ 3 files changed, 209 insertions(+), 182 deletions(-) diff --git a/frontend/src/components/Marketing.tsx b/frontend/src/components/Marketing.tsx index 0a97a365..00048df2 100644 --- a/frontend/src/components/Marketing.tsx +++ b/frontend/src/components/Marketing.tsx @@ -660,8 +660,11 @@ export function Marketing() {

-
- {PRICING_PLANS.map((plan) => ( +
+ {PRICING_PLANS.filter((plan) => { + // Always hide Starter plan on marketing page + return plan.name.toLowerCase() !== "starter"; + }).map((plan) => ( }, { - text: "5x more messages than Starter", + text: "Generous usage for power users", included: true, icon: }, @@ -145,7 +145,7 @@ export const PRICING_PLANS: PricingPlan[] = [ icon: }, { - text: "8x more messages than Starter per user", + text: "Even more usage per team member", included: true, icon: }, diff --git a/frontend/src/routes/pricing.tsx b/frontend/src/routes/pricing.tsx index b9aaee5f..40ffee9a 100644 --- a/frontend/src/routes/pricing.tsx +++ b/frontend/src/routes/pricing.tsx @@ -80,8 +80,8 @@ function PricingFAQ() { Free: 10 messages per week, resets Sunday 00:00 UTC. Max length on individual messages. -
  • Starter: Enough messages per month for a casual user
  • -
  • Pro: Great for heavier workloads with a high monthly cap
  • +
  • Pro: Generous usage for power users with a high monthly cap
  • +
  • Team: Even more usage per team member with unified billing
  • Enterprise: Message us at team@opensecret.cloud
  • @@ -562,8 +562,7 @@ function PricingPage() { } /> -
    - +
    @@ -587,7 +586,7 @@ function PricingPage() { -
    +
    @@ -674,165 +673,188 @@ function PricingPage() {
    -
    - {PRICING_PLANS.map((plan) => { - // Find the matching product from server data - const product = products?.find( - (p) => p.name.toLowerCase() === plan.name.toLowerCase() && p.active - ); - - // If no product found from server, use plan data - if (!product && plan.name !== "Free") return null; + {(() => { + const filteredPlans = PRICING_PLANS.filter((plan) => { + // Always hide Starter plan unless user is currently on Starter + const isStarterPlan = plan.name.toLowerCase() === "starter"; + const isUserOnStarter = freshBillingStatus?.product_name?.toLowerCase() === "starter"; - const isCurrentPlan = - isLoggedIn && - freshBillingStatus?.product_name?.toLowerCase() === plan.name.toLowerCase(); - const isTeamPlan = plan.name.toLowerCase().includes("team"); - const isFreeplan = plan.name.toLowerCase().includes("free"); + if (isStarterPlan && !isUserOnStarter) { + return false; + } - // Calculate prices - let monthlyOriginalPrice = plan.price.replace("$", ""); - let monthlyPrice = monthlyOriginalPrice; + return true; + }); - if (product) { - monthlyOriginalPrice = (product.default_price.unit_amount / 100).toFixed(2); - monthlyPrice = monthlyOriginalPrice; - } + const gridColumns = filteredPlans.length === 4 ? "md:grid-cols-4" : "md:grid-cols-3"; - // Calculate yearly prices for Bitcoin (10% off) - const yearlyDiscountedPrice = product - ? (Math.floor(product.default_price.unit_amount * 12 * 0.9) / 100).toFixed(2) - : (Number(monthlyOriginalPrice) * 12 * 0.9).toFixed(2); - - // Calculate monthly equivalent of yearly Bitcoin price - const monthlyEquivalentPrice = (Number(yearlyDiscountedPrice) / 12).toFixed(2); - - const displayPrice = useBitcoin && !isFreeplan ? monthlyEquivalentPrice : monthlyPrice; - - return ( -
    - {plan.popular && !isCurrentPlan && ( -
    - Most Popular -
    - )} - {isCurrentPlan && ( - - Current Plan - - )} - {!isFreeplan && useBitcoin && !isTeamPlan && ( - - 10% OFF - - )} -
    -

    - {plan.name} - {useBitcoin && !isFreeplan && !isTeamPlan ? " (Yearly)" : ""} - {isCurrentPlan && } -

    - -

    - {isTeamPlan && useBitcoin - ? "Team plan is not available with Bitcoin payment." - : plan.description} -

    - - {/* Features List */} -
    - {plan.features.map((feature, index) => ( -
    - {feature.text !== "" && - (feature.icon || - (feature.included ? ( - - ) : null))} - - {feature.text} - + return ( +
    + {filteredPlans.map((plan) => { + // Find the matching product from server data + const product = products?.find( + (p) => p.name.toLowerCase() === plan.name.toLowerCase() && p.active + ); + + // If no product found from server, use plan data + if (!product && plan.name !== "Free") return null; + + const isCurrentPlan = + isLoggedIn && + freshBillingStatus?.product_name?.toLowerCase() === plan.name.toLowerCase(); + const isTeamPlan = plan.name.toLowerCase().includes("team"); + const isFreeplan = plan.name.toLowerCase().includes("free"); + + // Calculate prices + let monthlyOriginalPrice = plan.price.replace("$", ""); + let monthlyPrice = monthlyOriginalPrice; + + if (product) { + monthlyOriginalPrice = (product.default_price.unit_amount / 100).toFixed(2); + monthlyPrice = monthlyOriginalPrice; + } + + // Calculate yearly prices for Bitcoin (10% off) + const yearlyDiscountedPrice = product + ? (Math.floor(product.default_price.unit_amount * 12 * 0.9) / 100).toFixed(2) + : (Number(monthlyOriginalPrice) * 12 * 0.9).toFixed(2); + + // Calculate monthly equivalent of yearly Bitcoin price + const monthlyEquivalentPrice = (Number(yearlyDiscountedPrice) / 12).toFixed(2); + + const displayPrice = + useBitcoin && !isFreeplan ? monthlyEquivalentPrice : monthlyPrice; + + return ( +
    + {plan.popular && !isCurrentPlan && ( +
    + Most Popular
    - ))} -
    - -
    - {!isFreeplan ? ( - <> -
    - ${displayPrice} - {useBitcoin && !isTeamPlan && ( - - ${monthlyOriginalPrice} + )} + {isCurrentPlan && ( + + Current Plan + + )} + {!isFreeplan && useBitcoin && !isTeamPlan && ( + + 10% OFF + + )} +
    +

    + {plan.name} + {useBitcoin && !isFreeplan && !isTeamPlan ? " (Yearly)" : ""} + {isCurrentPlan && } +

    + +

    + {isTeamPlan && useBitcoin + ? "Team plan is not available with Bitcoin payment." + : plan.description} +

    + + {/* Features List */} +
    + {plan.features.map((feature, index) => ( +
    + {feature.text !== "" && + (feature.icon || + (feature.included ? ( + + ) : null))} + + {feature.text} - )} -
    - - {isTeamPlan ? "per user" : ""} +
    + ))} +
    + +
    + {!isFreeplan ? ( + <> +
    + + ${displayPrice} + + {useBitcoin && !isTeamPlan && ( + + ${monthlyOriginalPrice} + + )} +
    + + {isTeamPlan ? "per user" : ""} + + + per month + +
    +
    +
    + {useBitcoin && !isTeamPlan && ( + <> +

    + {isTeamPlan + ? `Billed yearly at $${yearlyDiscountedPrice} per user` + : `Billed yearly at $${yearlyDiscountedPrice}`} +

    +

    + Save 10% with annual billing +

    + + )} +
    + + ) : ( +
    + ${monthlyPrice} + + per month - per month
    -
    -
    - {useBitcoin && !isTeamPlan && ( - <> -

    - {isTeamPlan - ? `Billed yearly at $${yearlyDiscountedPrice} per user` - : `Billed yearly at $${yearlyDiscountedPrice}`} -

    -

    - Save 10% with annual billing -

    - - )} -
    - - ) : ( -
    - ${monthlyPrice} - - per month - + )}
    - )} -
    - -
    -
    - ); - })} -
    + )} + +
    +
    + ); + })} +
    + ); + })()} From cd70fcf686e59bf562324c4649abb5666962fe26 Mon Sep 17 00:00:00 2001 From: Tony Giorgio Date: Mon, 14 Jul 2025 15:22:33 -0500 Subject: [PATCH 2/4] Implement max plan --- frontend/src/components/AccountMenu.tsx | 5 ++-- frontend/src/components/BillingStatus.tsx | 6 ++-- frontend/src/components/ChatBox.tsx | 4 ++- frontend/src/components/CreditUsage.tsx | 6 ++++ frontend/src/components/Marketing.tsx | 2 +- frontend/src/components/ModelSelector.tsx | 13 ++++++--- frontend/src/config/pricingConfig.tsx | 35 +++++++++++++++++++++++ frontend/src/routes/pricing.tsx | 4 ++- 8 files changed, 63 insertions(+), 12 deletions(-) diff --git a/frontend/src/components/AccountMenu.tsx b/frontend/src/components/AccountMenu.tsx index 1846df4d..5f8271e2 100644 --- a/frontend/src/components/AccountMenu.tsx +++ b/frontend/src/components/AccountMenu.tsx @@ -86,10 +86,11 @@ export function AccountMenu() { const hasStripeAccount = billingStatus?.stripe_customer_id !== null; const productName = billingStatus?.product_name || ""; const isPro = productName.toLowerCase().includes("pro"); + const isMax = productName.toLowerCase().includes("max"); const isStarter = productName.toLowerCase().includes("starter"); const isTeamPlan = productName.toLowerCase().includes("team"); - const showUpgrade = !isPro && !isTeamPlan; - const showManage = (isPro || isStarter || isTeamPlan) && hasStripeAccount; + const showUpgrade = !isMax && !isTeamPlan; + const showManage = (isPro || isMax || isStarter || isTeamPlan) && hasStripeAccount; // Fetch team status if user has team plan const { data: teamStatus } = useQuery({ diff --git a/frontend/src/components/BillingStatus.tsx b/frontend/src/components/BillingStatus.tsx index 81fa4118..9ff291df 100644 --- a/frontend/src/components/BillingStatus.tsx +++ b/frontend/src/components/BillingStatus.tsx @@ -48,7 +48,7 @@ export function BillingStatus() { } const isFree = billingStatus.product_name.toLowerCase().includes("free"); - const isPro = billingStatus.product_name.toLowerCase().includes("pro"); + const isMax = billingStatus.product_name.toLowerCase().includes("max"); const getChatsText = () => { if (isFree) { @@ -58,7 +58,7 @@ export function BillingStatus() { return `Free Plan — ${billingStatus.chats_remaining} Message${billingStatus.chats_remaining === 1 ? "" : "s"} Left This Week`; } if (!billingStatus.can_chat) { - if (isPro) { + if (isMax) { return "Contact us to increase your limits"; } return "You've run out of messages, upgrade to keep chatting!"; @@ -80,7 +80,7 @@ export function BillingStatus() {
    -
    +
    {PRICING_PLANS.filter((plan) => { // Always hide Starter plan on marketing page return plan.name.toLowerCase() !== "starter"; diff --git a/frontend/src/components/ModelSelector.tsx b/frontend/src/components/ModelSelector.tsx index 60122cca..526e5876 100644 --- a/frontend/src/components/ModelSelector.tsx +++ b/frontend/src/components/ModelSelector.tsx @@ -171,14 +171,19 @@ export function ModelSelector({ const planName = billingStatus?.product_name?.toLowerCase() || ""; - // Check if user is on Pro or Team plan (for requiresPro models) + // Check if user is on Pro, Max, or Team plan (for requiresPro models) if (config?.requiresPro) { - return planName.includes("pro") || planName.includes("team"); + return planName.includes("pro") || planName.includes("max") || planName.includes("team"); } - // Check if user is on Starter, Pro, or Team plan (for requiresStarter models) + // Check if user is on Starter, Pro, Max, or Team plan (for requiresStarter models) if (config?.requiresStarter) { - return planName.includes("starter") || planName.includes("pro") || planName.includes("team"); + return ( + planName.includes("starter") || + planName.includes("pro") || + planName.includes("max") || + planName.includes("team") + ); } return true; diff --git a/frontend/src/config/pricingConfig.tsx b/frontend/src/config/pricingConfig.tsx index 4504b4e9..3749a5db 100644 --- a/frontend/src/config/pricingConfig.tsx +++ b/frontend/src/config/pricingConfig.tsx @@ -134,6 +134,41 @@ export const PRICING_PLANS: PricingPlan[] = [ ctaText: "Start Chatting", popular: true }, + { + name: "Max", + price: "$100", + description: "Maximum usage for power users", + features: [ + { + text: "All features from Pro", + included: true, + icon: + }, + { + text: "10x more usage than Pro", + included: true, + icon: + }, + { + text: "Priority support", + included: true, + icon: + }, + { text: "Gemma 3 27B", included: true, icon: }, + { + text: "DeepSeek R1 70B", + included: true, + icon: + }, + { text: "Image Upload", included: true, icon: }, + { + text: "Document Upload", + included: true, + icon: + } + ], + ctaText: "Start Chatting" + }, { name: "Team", price: "$30", diff --git a/frontend/src/routes/pricing.tsx b/frontend/src/routes/pricing.tsx index 40ffee9a..1327db48 100644 --- a/frontend/src/routes/pricing.tsx +++ b/frontend/src/routes/pricing.tsx @@ -81,6 +81,7 @@ function PricingFAQ() { messages.
  • Pro: Generous usage for power users with a high monthly cap
  • +
  • Max: 10x more usage than Pro for maximum power users
  • Team: Even more usage per team member with unified billing
  • Enterprise: Message us at team@opensecret.cloud
  • @@ -562,7 +563,8 @@ function PricingPage() { } /> -
    +
    + From 124daf0c079bf29e2460e455c230fcc99336d522 Mon Sep 17 00:00:00 2001 From: Tony Giorgio Date: Mon, 14 Jul 2025 15:33:35 -0500 Subject: [PATCH 3/4] Better row management for mid screens --- frontend/src/routes/pricing.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/routes/pricing.tsx b/frontend/src/routes/pricing.tsx index 1327db48..c88ad9d5 100644 --- a/frontend/src/routes/pricing.tsx +++ b/frontend/src/routes/pricing.tsx @@ -688,7 +688,7 @@ function PricingPage() { return true; }); - const gridColumns = filteredPlans.length === 4 ? "md:grid-cols-4" : "md:grid-cols-3"; + const gridColumns = filteredPlans.length === 4 ? "lg:grid-cols-4" : "lg:grid-cols-3"; return (
    Date: Tue, 15 Jul 2025 09:44:16 -0500 Subject: [PATCH 4/4] Clarify 20x max plan --- frontend/src/config/pricingConfig.tsx | 2 +- frontend/src/routes/pricing.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/config/pricingConfig.tsx b/frontend/src/config/pricingConfig.tsx index 3749a5db..8654be2e 100644 --- a/frontend/src/config/pricingConfig.tsx +++ b/frontend/src/config/pricingConfig.tsx @@ -145,7 +145,7 @@ export const PRICING_PLANS: PricingPlan[] = [ icon: }, { - text: "10x more usage than Pro", + text: "20x more usage than Pro", included: true, icon: }, diff --git a/frontend/src/routes/pricing.tsx b/frontend/src/routes/pricing.tsx index c88ad9d5..17a7f97f 100644 --- a/frontend/src/routes/pricing.tsx +++ b/frontend/src/routes/pricing.tsx @@ -81,7 +81,7 @@ function PricingFAQ() { messages.
  • Pro: Generous usage for power users with a high monthly cap
  • -
  • Max: 10x more usage than Pro for maximum power users
  • +
  • Max: 20x more usage than Pro for maximum power users
  • Team: Even more usage per team member with unified billing
  • Enterprise: Message us at team@opensecret.cloud