diff options
Diffstat (limited to 'apps')
| -rw-r--r-- | apps/web/components/views/billing.tsx | 62 | ||||
| -rw-r--r-- | apps/web/components/views/profile.tsx | 69 |
2 files changed, 99 insertions, 32 deletions
diff --git a/apps/web/components/views/billing.tsx b/apps/web/components/views/billing.tsx index d6d85884..f3ef2605 100644 --- a/apps/web/components/views/billing.tsx +++ b/apps/web/components/views/billing.tsx @@ -7,7 +7,7 @@ import { import { Button } from "@ui/components/button" import { HeadingH3Bold } from "@ui/text/heading/heading-h3-bold" import { useCustomer } from "autumn-js/react" -import { CheckCircle, LoaderIcon, X } from "lucide-react" +import { AlertTriangle, CheckCircle, LoaderIcon, X } from "lucide-react" import { motion } from "motion/react" import Link from "next/link" import { useEffect, useState } from "react" @@ -24,11 +24,16 @@ export function BillingView() { const { data: status = { - consumer_pro: null, + consumer_pro: { allowed: false, status: null }, }, isLoading: isCheckingStatus, } = fetchSubscriptionStatus(autumn, !autumn.isLoading) + const proStatus = status.consumer_pro + const proProductStatus = proStatus?.status + const isPastDue = proProductStatus === "past_due" + const hasProProduct = proProductStatus !== null + const { data: memoriesCheck } = fetchMemoriesFeature( autumn, !autumn.isLoading && !isCheckingStatus, @@ -37,7 +42,10 @@ export function BillingView() { const memoriesUsed = memoriesCheck?.usage ?? 0 const memoriesLimit = memoriesCheck?.included_usage ?? 0 - const { data: connectionsCheck } = fetchConnectionsFeature(autumn, !autumn.isLoading && !isCheckingStatus) + const { data: connectionsCheck } = fetchConnectionsFeature( + autumn, + !autumn.isLoading && !isCheckingStatus, + ) const connectionsUsed = connectionsCheck?.usage ?? 0 @@ -66,8 +74,6 @@ export function BillingView() { }) } - const isPro = status.consumer_pro - if (user?.isAnonymous) { return ( <motion.div @@ -92,7 +98,7 @@ export function BillingView() { ) } - if (isPro) { + if (hasProProduct) { return ( <motion.div animate={{ opacity: 1, y: 0 }} @@ -102,24 +108,45 @@ export function BillingView() { <div className="space-y-3"> <HeadingH3Bold className="text-foreground flex items-center gap-2"> Pro Plan - <span className="text-xs bg-green-500/20 text-green-600 dark:text-green-400 px-2 py-0.5 rounded-full"> - Active - </span> + {isPastDue ? ( + <span className="text-xs bg-red-500/20 text-red-600 dark:text-red-400 px-2 py-0.5 rounded-full"> + Past Due + </span> + ) : ( + <span className="text-xs bg-green-500/20 text-green-600 dark:text-green-400 px-2 py-0.5 rounded-full"> + Active + </span> + )} </HeadingH3Bold> <p className="text-sm text-muted-foreground"> - You're enjoying expanded memory capacity with supermemory Pro! + {isPastDue + ? "Your payment is past due. Please update your payment method to restore access." + : "You're enjoying expanded memory capacity with supermemory Pro!"} </p> </div> + {isPastDue && ( + <div className="p-4 bg-red-500/10 border border-red-500/20 rounded-lg flex items-start gap-3"> + <AlertTriangle className="h-5 w-5 text-red-600 dark:text-red-400 flex-shrink-0 mt-0.5" /> + <div className="flex-1"> + <p className="text-sm text-red-600 dark:text-red-400 font-medium mb-1"> + Payment Required + </p> + <p className="text-xs text-red-600/80 dark:text-red-400/80"> + Your payment method failed or payment is past due. Update your + payment information to restore access to all Pro features. + </p> + </div> + </div> + )} + {/* Current Usage */} <div className="space-y-3"> <h4 className="text-sm font-medium text-foreground">Current Usage</h4> <div className="space-y-2"> <div className="flex justify-between items-center"> <span className="text-sm text-muted-foreground">Memories</span> - <span className="text-sm text-foreground"> - Unlimited - </span> + <span className="text-sm text-foreground">Unlimited</span> </div> </div> <div className="space-y-2"> @@ -132,8 +159,13 @@ export function BillingView() { </div> </div> - <Button onClick={handleManageBilling} size="sm" variant="default"> - Manage Billing + <Button + onClick={handleManageBilling} + size="sm" + variant="default" + className={isPastDue ? "bg-red-600 hover:bg-red-700 text-white" : ""} + > + {isPastDue ? "Pay Past Due" : "Manage Billing"} </Button> </motion.div> ) diff --git a/apps/web/components/views/profile.tsx b/apps/web/components/views/profile.tsx index e4db2171..c60f4310 100644 --- a/apps/web/components/views/profile.tsx +++ b/apps/web/components/views/profile.tsx @@ -10,7 +10,14 @@ import { Button } from "@repo/ui/components/button" import { Skeleton } from "@repo/ui/components/skeleton" import { HeadingH3Bold } from "@repo/ui/text/heading/heading-h3-bold" import { useCustomer } from "autumn-js/react" -import { CheckCircle, CreditCard, LoaderIcon, User, X } from "lucide-react" +import { + AlertTriangle, + CheckCircle, + CreditCard, + LoaderIcon, + User, + X, +} from "lucide-react" import { motion } from "motion/react" import Link from "next/link" import { useState } from "react" @@ -23,12 +30,16 @@ export function ProfileView() { const { data: status = { - consumer_pro: null, + consumer_pro: { allowed: false, status: null }, }, isLoading: isCheckingStatus, } = fetchSubscriptionStatus(autumn, !autumn.isLoading) - const isPro = status.consumer_pro + const proStatus = status.consumer_pro + const isPro = proStatus?.allowed ?? false + const proProductStatus = proStatus?.status + const isPastDue = proProductStatus === "past_due" + const hasProProduct = proProductStatus !== null const { data: memoriesCheck } = fetchMemoriesFeature( autumn, @@ -176,24 +187,43 @@ export function ProfileView() { </div> <div className="flex-1"> <HeadingH3Bold className="text-foreground text-sm"> - {isPro ? "Pro Plan" : "Free Plan"} - {isPro && ( + {hasProProduct ? "Pro Plan" : "Free Plan"} + {isPastDue ? ( + <span className="ml-2 text-xs bg-red-500/20 text-red-600 dark:text-red-400 px-2 py-0.5 rounded-full"> + Past Due + </span> + ) : isPro ? ( <span className="ml-2 text-xs bg-green-500/20 text-green-600 dark:text-green-400 px-2 py-0.5 rounded-full"> Active </span> - )} + ) : null} </HeadingH3Bold> <p className="text-muted-foreground text-xs"> - {isPro ? "Expanded memory capacity" : "Basic plan"} + {hasProProduct ? "Expanded memory capacity" : "Basic plan"} </p> </div> </div> + {isPastDue && ( + <div className="p-3 bg-red-500/10 border border-red-500/20 rounded-lg flex items-start gap-2"> + <AlertTriangle className="h-4 w-4 text-red-600 dark:text-red-400 flex-shrink-0 mt-0.5" /> + <div className="flex-1"> + <p className="text-sm text-red-600 dark:text-red-400 font-medium"> + Payment Required + </p> + <p className="text-xs text-red-600/80 dark:text-red-400/80 mt-1"> + Your payment is past due. Please update your payment method to + restore access to Pro features. + </p> + </div> + </div> + )} + {/* Usage Stats */} <div className="space-y-2"> <div className="flex justify-between items-center"> <span className="text-sm text-muted-foreground">Memories</span> - {isPro ? ( + {hasProProduct ? ( <span className="text-sm text-foreground">Unlimited</span> ) : ( <span @@ -203,15 +233,11 @@ export function ProfileView() { </span> )} </div> - {!isPro && ( + {!hasProProduct && ( <div className="w-full bg-muted-foreground/50 rounded-full h-2"> <div className={`h-2 rounded-full transition-all ${ - memoriesUsed >= memoriesLimit - ? "bg-red-500" - : isPro - ? "bg-green-500" - : "bg-blue-500" + memoriesUsed >= memoriesLimit ? "bg-red-500" : "bg-blue-500" }`} style={{ width: `${Math.min((memoriesUsed / memoriesLimit) * 100, 100)}%`, @@ -221,7 +247,7 @@ export function ProfileView() { )} </div> - {isPro && ( + {hasProProduct && ( <div className="flex justify-between items-center"> <span className="text-sm text-muted-foreground">Connections</span> <span className="text-sm text-foreground"> @@ -232,7 +258,16 @@ export function ProfileView() { {/* Billing Actions */} <div className="pt-2"> - {isPro ? ( + {isPastDue ? ( + <Button + className="w-full bg-red-600 hover:bg-red-700 text-white border-0" + onClick={handleManageBilling} + size="sm" + variant="default" + > + Pay Past Due + </Button> + ) : hasProProduct ? ( <Button className="w-full" onClick={handleManageBilling} @@ -266,7 +301,7 @@ export function ProfileView() { )} </div> - {!isPro && ( + {!hasProProduct && ( <div className="space-y-4"> <div className="grid grid-cols-1 sm:grid-cols-2 gap-3 sm:gap-4"> {/* Free Plan */} |