eny.space Landingpage
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat(dashboard): refine subscription status logic and UI indicators

+77 -29
+64 -20
app/actions/subscription.ts
··· 46 46 limit: 10, 47 47 }); 48 48 49 - // Find active or trialing subscription 49 + // Find active or trialing subscription (still counts as subscribed even if cancel_at_period_end) 50 50 const activeSubscription = subscriptions.data.find( 51 51 (sub) => 52 - (sub.status === "active" || sub.status === "trialing") && 53 - !sub.cancel_at_period_end 52 + sub.status === "active" || sub.status === "trialing" 54 53 ); 55 54 56 55 return activeSubscription || null; ··· 61 60 } 62 61 63 62 /** 64 - * Get subscription status for UI (always from Stripe) 63 + * Get latest subscription for UI (shows canceled history too) 65 64 */ 66 65 export async function getSubscriptionStatus() { 67 - const subscription = await getActiveSubscription(); 66 + const customerId = await getStripeCustomerId(); 67 + if (!customerId) { 68 + return { 69 + subscribed: false, 70 + subscription: null, 71 + }; 72 + } 68 73 69 - return { 70 - subscribed: !!subscription, 71 - subscription: subscription 72 - ? { 73 - status: subscription.status, 74 - cancel_at_period_end: subscription.cancel_at_period_end, 75 - current_period_end: new Date( 76 - subscription.current_period_end * 1000 77 - ).toISOString(), 78 - current_period_start: new Date( 79 - subscription.current_period_start * 1000 80 - ).toISOString(), 81 - } 82 - : null, 83 - }; 74 + try { 75 + const subscriptions = await stripe.subscriptions.list({ 76 + customer: customerId, 77 + status: "all", 78 + limit: 10, 79 + }); 80 + 81 + if (!subscriptions.data.length) { 82 + return { 83 + subscribed: false, 84 + subscription: null, 85 + }; 86 + } 87 + 88 + // Pick the most recently created subscription 89 + const latest = subscriptions.data.reduce<Stripe.Subscription | null>( 90 + (acc, sub) => { 91 + if (!acc) return sub; 92 + return sub.created > acc.created ? sub : acc; 93 + }, 94 + null 95 + ); 96 + 97 + if (!latest) { 98 + return { 99 + subscribed: false, 100 + subscription: null, 101 + }; 102 + } 103 + 104 + const isCurrentlySubscribed = 105 + (latest.status === "active" || latest.status === "trialing") && 106 + latest.cancel_at_period_end === false; 107 + 108 + return { 109 + subscribed: isCurrentlySubscribed, 110 + subscription: { 111 + status: latest.status, 112 + cancel_at_period_end: latest.cancel_at_period_end, 113 + current_period_end: new Date( 114 + latest.current_period_end * 1000 115 + ).toISOString(), 116 + current_period_start: new Date( 117 + latest.current_period_start * 1000 118 + ).toISOString(), 119 + }, 120 + }; 121 + } catch (error) { 122 + console.error("Error fetching subscription status from Stripe:", error); 123 + return { 124 + subscribed: false, 125 + subscription: null, 126 + }; 127 + } 84 128 } 85 129 86 130 /**
+13 -9
app/dashboard/dashboard-client.tsx
··· 28 28 const handleSubscribe = async () => { 29 29 if (!priceId) { 30 30 alert( 31 - "Stripe price ID not configured. Please set NEXT_PUBLIC_STRIPE_PRICE_ID in your environment variables." 31 + "Stripe price ID not configured. Please set NEXT_PUBLIC_STRIPE_PRICE_ID in your environment variables.", 32 32 ); 33 33 return; 34 34 } ··· 63 63 } catch (error) { 64 64 console.error("Error making server call:", error); 65 65 alert( 66 - `Error: ${error instanceof Error ? error.message : "Unknown error"}` 66 + `Error: ${error instanceof Error ? error.message : "Unknown error"}`, 67 67 ); 68 68 } 69 69 }; ··· 95 95 if (isCanceled) { 96 96 return ( 97 97 <div className="space-y-3 text-white"> 98 - <Heading as="h2" className="text-base font-semibold"> 98 + <Heading as="h2" className="text-base font-semibold text-rose-300"> 99 99 Subscription Canceled 100 100 </Heading> 101 101 <Paragraph className="text-sm text-white/80"> 102 - Your subscription has been canceled. Subscribe again to regain 103 - access. 102 + Your subscription has been canceled. Subscribe again to regain access. 104 103 </Paragraph> 105 104 <Button 106 105 onClick={handleSubscribe} ··· 116 115 const handleCancel = async () => { 117 116 if ( 118 117 !confirm( 119 - "Are you sure you want to cancel your subscription? You'll have access until the end of your billing period." 118 + "Are you sure you want to cancel your subscription? You'll have access until the end of your billing period.", 120 119 ) 121 120 ) { 122 121 return; ··· 127 126 const result = await cancelSubscription(); 128 127 if (result.success) { 129 128 alert( 130 - "Subscription canceled. You'll have access until the end of your billing period." 129 + "Subscription canceled. You'll have access until the end of your billing period.", 131 130 ); 132 131 window.location.reload(); 133 132 } else { ··· 180 179 181 180 return ( 182 181 <div className="space-y-4 text-white"> 183 - <Heading as="h2" className="text-base font-semibold"> 184 - {isCanceling ? "Subscription Canceling" : "Active Subscription"} 182 + <Heading 183 + as="h2" 184 + className={`text-base font-semibold ${ 185 + isCanceling ? "text-amber-300" : "text-emerald-300" 186 + }`} 187 + > 188 + {isCanceling ? "Cancellation scheduled" : "Active Subscription"} 185 189 </Heading> 186 190 {subscription && ( 187 191 <div className="space-y-1 text-sm text-white/80">