1+ "use client" ;
2+
3+ import { useRouter } from "next/navigation" ;
4+ import { useQuery , useAction } from "convex/react" ;
5+ import { api } from "@/convex/_generated/api" ;
6+ import { Button } from "@/components/ui/button" ;
7+ import { Card , CardContent , CardDescription , CardHeader , CardTitle } from "@/components/ui/card" ;
8+ import { Loader2 , CreditCard , Calendar , AlertCircle } from "lucide-react" ;
9+ import { format } from "date-fns" ;
10+
11+ export default function AccountPage ( ) {
12+ const router = useRouter ( ) ;
13+ const subscription = useQuery ( api . subscriptions . getUserSubscription ) ;
14+ const pageUsage = useQuery ( api . subscriptions . getUserPageUsage ) ;
15+ const createPortalSession = useAction ( api . stripe . createPortalSession ) ;
16+
17+ const handleManageSubscription = async ( ) => {
18+ try {
19+ const { url } = await createPortalSession ( ) ;
20+ window . location . href = url ;
21+ } catch ( error ) {
22+ console . error ( "Error creating portal session:" , error ) ;
23+ }
24+ } ;
25+
26+ if ( ! subscription || ! pageUsage ) {
27+ return (
28+ < div className = "flex items-center justify-center min-h-screen" >
29+ < Loader2 className = "h-8 w-8 animate-spin" />
30+ </ div >
31+ ) ;
32+ }
33+
34+ const isFreePlan = ! subscription . stripeSubscriptionId ;
35+ const nextBillingDate = subscription . currentPeriodEnd
36+ ? new Date ( subscription . currentPeriodEnd )
37+ : null ;
38+
39+ return (
40+ < div className = "container mx-auto py-10 max-w-4xl" >
41+ < h1 className = "text-3xl font-bold mb-8" > Account Settings</ h1 >
42+
43+ < div className = "space-y-6" >
44+ { /* Subscription Status */ }
45+ < Card >
46+ < CardHeader >
47+ < CardTitle > Subscription</ CardTitle >
48+ < CardDescription > Manage your subscription and billing</ CardDescription >
49+ </ CardHeader >
50+ < CardContent className = "space-y-4" >
51+ < div className = "flex justify-between items-center" >
52+ < div >
53+ < p className = "text-sm text-muted-foreground" > Current Plan</ p >
54+ < p className = "text-xl font-semibold" data-cy = "subscription-plan" >
55+ { subscription . plan ?. name || "Free Plan" }
56+ </ p >
57+ </ div >
58+ < div className = "text-right" >
59+ < p className = "text-sm text-muted-foreground" data-cy = "subscription-status" >
60+ { subscription . status === "active" ? "Active" :
61+ subscription . status === "past_due" ? "Past due" :
62+ subscription . status === "canceled" ? "Canceled" :
63+ subscription . status === "incomplete" ? "Incomplete" : "Active" }
64+ </ p >
65+ { nextBillingDate && ! subscription . cancelAtPeriodEnd && (
66+ < p className = "text-sm" data-cy = "next-billing-date" >
67+ Next billing: { format ( nextBillingDate , "PPP" ) }
68+ </ p >
69+ ) }
70+ { subscription . cancelAtPeriodEnd && (
71+ < p className = "text-sm text-warning" data-cy = "cancellation-date" >
72+ Cancels on: { format ( nextBillingDate ! , "PPP" ) }
73+ </ p >
74+ ) }
75+ </ div >
76+ </ div >
77+
78+ { /* Credit Usage */ }
79+ < div className = "border-t pt-4" >
80+ < div className = "flex justify-between items-center" >
81+ < div >
82+ < p className = "text-sm text-muted-foreground" > Page Credits</ p >
83+ < p className = "text-lg" >
84+ < span data-cy = "credits-used" > { pageUsage . used } </ span > /
85+ < span data-cy = "credits-limit" > { pageUsage . limit } </ span > used
86+ </ p >
87+ </ div >
88+ < div >
89+ < p className = "text-2xl font-bold" data-cy = "credits-remaining" >
90+ { pageUsage . remaining } remaining
91+ </ p >
92+ </ div >
93+ </ div >
94+ </ div >
95+
96+ { /* Actions */ }
97+ < div className = "flex gap-2 pt-4" >
98+ { isFreePlan ? (
99+ < Button
100+ onClick = { ( ) => router . push ( "/dashboard/upgrade" ) }
101+ className = "flex-1"
102+ data-cy = "upgrade-subscription"
103+ >
104+ < CreditCard className = "mr-2 h-4 w-4" />
105+ Upgrade Subscription
106+ </ Button >
107+ ) : (
108+ < Button
109+ onClick = { handleManageSubscription }
110+ className = "flex-1"
111+ variant = "outline"
112+ data-cy = "manage-subscription"
113+ >
114+ < CreditCard className = "mr-2 h-4 w-4" />
115+ Manage Subscription
116+ </ Button >
117+ ) }
118+ </ div >
119+
120+ { subscription . status === "past_due" && (
121+ < div className = "flex items-center gap-2 p-3 bg-red-50 text-red-700 rounded-md" data-cy = "payment-failed-banner" >
122+ < AlertCircle className = "h-4 w-4" />
123+ < p className = "text-sm" >
124+ Your payment failed. Please update your payment method.
125+ </ p >
126+ < Button
127+ size = "sm"
128+ variant = "outline"
129+ onClick = { handleManageSubscription }
130+ className = "ml-auto"
131+ data-cy = "update-payment-method"
132+ >
133+ Update Payment
134+ </ Button >
135+ </ div >
136+ ) }
137+
138+ { subscription . cancelAtPeriodEnd && (
139+ < div className = "flex items-center gap-2 p-3 bg-amber-50 text-amber-700 rounded-md" data-cy = "subscription-ending-notice" >
140+ < AlertCircle className = "h-4 w-4" />
141+ < p className = "text-sm" >
142+ Your subscription is scheduled to cancel at the end of the billing period.
143+ </ p >
144+ < Button
145+ size = "sm"
146+ variant = "outline"
147+ onClick = { handleManageSubscription }
148+ className = "ml-auto"
149+ data-cy = "reactivate-before-cancellation"
150+ >
151+ Reactivate
152+ </ Button >
153+ </ div >
154+ ) }
155+ </ CardContent >
156+ </ Card >
157+
158+ { /* Payment History */ }
159+ < Card >
160+ < CardHeader >
161+ < CardTitle > Billing History</ CardTitle >
162+ < CardDescription > View your payment history and invoices</ CardDescription >
163+ </ CardHeader >
164+ < CardContent >
165+ { isFreePlan ? (
166+ < p className = "text-sm text-muted-foreground" >
167+ No billing history available for free accounts.
168+ </ p >
169+ ) : (
170+ < Button
171+ onClick = { handleManageSubscription }
172+ variant = "outline"
173+ data-cy = "payment-history"
174+ >
175+ < Calendar className = "mr-2 h-4 w-4" />
176+ View Payment History
177+ </ Button >
178+ ) }
179+ </ CardContent >
180+ </ Card >
181+
182+ { /* Credit History */ }
183+ < Card >
184+ < CardHeader >
185+ < CardTitle > Credit Usage History</ CardTitle >
186+ < CardDescription > Track your monthly credit usage</ CardDescription >
187+ </ CardHeader >
188+ < CardContent >
189+ < Button
190+ onClick = { ( ) => router . push ( "/dashboard/credits" ) }
191+ variant = "outline"
192+ data-cy = "credit-history"
193+ >
194+ View Credit History
195+ </ Button >
196+ </ CardContent >
197+ </ Card >
198+ </ div >
199+ </ div >
200+ ) ;
201+ }
0 commit comments