@@ -10,6 +10,7 @@ import {
1010} from "../components/ui/card" ;
1111import { Separator } from "../components/ui/separator" ;
1212import {
13+ PaymentPlanId ,
1314 SubscriptionStatus ,
1415 parsePaymentPlanId ,
1516 prettyPaymentPlanName ,
@@ -29,12 +30,12 @@ export default function AccountPage({ user }: { user: User }) {
2930 { ! ! user . email && (
3031 < div className = "px-6 py-4" >
3132 < div className = "grid grid-cols-1 sm:grid-cols-3 sm:gap-4" >
32- < dt className = "text-muted-foreground text-sm font-medium" >
33+ < div className = "text-muted-foreground text-sm font-medium" >
3334 Email address
34- </ dt >
35- < dd className = "text-foreground mt-1 text-sm sm:col-span-2 sm:mt-0" >
35+ </ div >
36+ < div className = "text-foreground mt-1 text-sm sm:col-span-2 sm:mt-0" >
3637 { user . email }
37- </ dd >
38+ </ div >
3839 </ div >
3940 </ div >
4041 ) }
@@ -43,41 +44,52 @@ export default function AccountPage({ user }: { user: User }) {
4344 < Separator />
4445 < div className = "px-6 py-4" >
4546 < div className = "grid grid-cols-1 sm:grid-cols-3 sm:gap-4" >
46- < dt className = "text-muted-foreground text-sm font-medium" >
47+ < div className = "text-muted-foreground text-sm font-medium" >
4748 Username
48- </ dt >
49- < dd className = "text-foreground mt-1 text-sm sm:col-span-2 sm:mt-0" >
49+ </ div >
50+ < div className = "text-foreground mt-1 text-sm sm:col-span-2 sm:mt-0" >
5051 { user . username }
51- </ dd >
52+ </ div >
5253 </ div >
5354 </ div >
5455 </ >
5556 ) }
5657 < Separator />
5758 < div className = "px-6 py-4" >
5859 < div className = "grid grid-cols-1 sm:grid-cols-3 sm:gap-4" >
59- < dt className = "text-muted-foreground text-sm font-medium" >
60+ < div className = "text-muted-foreground text-sm font-medium" >
6061 Your Plan
61- </ dt >
62- < UserCurrentPaymentPlan
63- subscriptionStatus = {
64- user . subscriptionStatus as SubscriptionStatus
65- }
62+ </ div >
63+ < UserCurrentSubscriptionPlan
6664 subscriptionPlan = { user . subscriptionPlan }
65+ subscriptionStatus = { user . subscriptionStatus }
6766 datePaid = { user . datePaid }
68- credits = { user . credits }
6967 />
7068 </ div >
7169 </ div >
7270 < Separator />
7371 < div className = "px-6 py-4" >
7472 < div className = "grid grid-cols-1 sm:grid-cols-3 sm:gap-4" >
75- < dt className = "text-muted-foreground text-sm font-medium" >
73+ < div className = "text-muted-foreground text-sm font-medium" >
74+ Credits
75+ </ div >
76+ < div className = "text-foreground mt-1 text-sm sm:col-span-1 sm:mt-0" >
77+ { user . credits } credits
78+ </ div >
79+ < div className = "ml-auto mt-4 sm:mt-0" >
80+ < BuyMoreButton subscriptionStatus = { user . subscriptionStatus } />
81+ </ div >
82+ </ div >
83+ </ div >
84+ < Separator />
85+ < div className = "px-6 py-4" >
86+ < div className = "grid grid-cols-1 sm:grid-cols-3 sm:gap-4" >
87+ < div className = "text-muted-foreground text-sm font-medium" >
7688 About
77- </ dt >
78- < dd className = "text-foreground mt-1 text-sm sm:col-span-2 sm:mt-0" >
89+ </ div >
90+ < div className = "text-foreground mt-1 text-sm sm:col-span-2 sm:mt-0" >
7991 I'm a cool customer.
80- </ dd >
92+ </ div >
8193 </ div >
8294 </ div >
8395 </ div >
@@ -87,129 +99,97 @@ export default function AccountPage({ user }: { user: User }) {
8799 ) ;
88100}
89101
90- type UserCurrentPaymentPlanProps = {
91- subscriptionPlan : string | null ;
92- subscriptionStatus : SubscriptionStatus | null ;
93- datePaid : Date | null ;
94- credits : number ;
95- } ;
96-
97- function UserCurrentPaymentPlan ( {
102+ function UserCurrentSubscriptionPlan ( {
98103 subscriptionPlan,
99104 subscriptionStatus,
100105 datePaid,
101- credits,
102- } : UserCurrentPaymentPlanProps ) {
103- if ( subscriptionStatus && subscriptionPlan && datePaid ) {
104- return (
105- < >
106- < dd className = "text-foreground mt-1 text-sm sm:col-span-1 sm:mt-0" >
107- { getUserSubscriptionStatusDescription ( {
108- subscriptionPlan,
109- subscriptionStatus,
110- datePaid,
111- } ) }
112- </ dd >
113- { subscriptionStatus !== SubscriptionStatus . Deleted ? (
114- < CustomerPortalButton />
115- ) : (
116- < BuyMoreButton />
117- ) }
118- </ >
106+ } : Pick < User , "subscriptionPlan" | "subscriptionStatus" | "datePaid" > ) {
107+ let subscriptionPlanMessage = "Free Plan" ;
108+ if (
109+ subscriptionPlan !== null &&
110+ subscriptionStatus !== null &&
111+ datePaid !== null
112+ ) {
113+ subscriptionPlanMessage = formatSubscriptionStatusMessage (
114+ parsePaymentPlanId ( subscriptionPlan ) ,
115+ datePaid ,
116+ subscriptionStatus as SubscriptionStatus ,
119117 ) ;
120118 }
121119
122120 return (
123121 < >
124- < dd className = "text-foreground mt-1 text-sm sm:col-span-1 sm:mt-0" >
125- Credits remaining: { credits }
126- </ dd >
127- < BuyMoreButton />
122+ < div className = "text-foreground mt-1 text-sm sm:col-span-1 sm:mt-0" >
123+ { subscriptionPlanMessage }
124+ </ div >
125+ < div className = "ml-auto mt-4 sm:mt-0" >
126+ < CustomerPortalButton />
127+ </ div >
128128 </ >
129129 ) ;
130130}
131131
132- function getUserSubscriptionStatusDescription ( {
133- subscriptionPlan,
134- subscriptionStatus,
135- datePaid,
136- } : {
137- subscriptionPlan : string ;
138- subscriptionStatus : SubscriptionStatus ;
139- datePaid : Date ;
140- } ) {
141- const planName = prettyPaymentPlanName ( parsePaymentPlanId ( subscriptionPlan ) ) ;
142- const endOfBillingPeriod = prettyPrintEndOfBillingPeriod ( datePaid ) ;
143- return prettyPrintStatus ( planName , subscriptionStatus , endOfBillingPeriod ) ;
144- }
145-
146- function prettyPrintStatus (
147- planName : string ,
132+ function formatSubscriptionStatusMessage (
133+ subscriptionPlan : PaymentPlanId ,
134+ datePaid : Date ,
148135 subscriptionStatus : SubscriptionStatus ,
149- endOfBillingPeriod : string ,
150136) : string {
137+ const paymentPlanName = prettyPaymentPlanName ( subscriptionPlan ) ;
151138 const statusToMessage : Record < SubscriptionStatus , string > = {
152- active : `${ planName } ` ,
153- past_due : `Payment for your ${ planName } plan is past due! Please update your subscription payment information.` ,
154- cancel_at_period_end : `Your ${ planName } plan subscription has been canceled, but remains active until the end of the current billing period${ endOfBillingPeriod } ` ,
139+ active : `${ paymentPlanName } ` ,
140+ past_due : `Payment for your ${ paymentPlanName } plan is past due! Please update your subscription payment information.` ,
141+ cancel_at_period_end : `Your ${ paymentPlanName } plan subscription has been canceled, but remains active until the end of the current billing period: ${ prettyPrintEndOfBillingPeriod (
142+ datePaid ,
143+ ) } `,
155144 deleted : `Your previous subscription has been canceled and is no longer active.` ,
156145 } ;
157- if ( Object . keys ( statusToMessage ) . includes ( subscriptionStatus ) ) {
158- return statusToMessage [ subscriptionStatus ] ;
159- } else {
160- throw new Error ( `Invalid subscriptionStatus: ${ subscriptionStatus } ` ) ;
146+
147+ if ( ! statusToMessage [ subscriptionStatus ] ) {
148+ throw new Error ( `Invalid subscription status: ${ subscriptionStatus } ` ) ;
161149 }
150+
151+ return statusToMessage [ subscriptionStatus ] ;
162152}
163153
164154function prettyPrintEndOfBillingPeriod ( date : Date ) {
165155 const oneMonthFromNow = new Date ( date ) ;
166156 oneMonthFromNow . setMonth ( oneMonthFromNow . getMonth ( ) + 1 ) ;
167- return ": " + oneMonthFromNow . toLocaleDateString ( ) ;
157+ return oneMonthFromNow . toLocaleDateString ( ) ;
168158}
169159
170- function BuyMoreButton ( ) {
160+ function CustomerPortalButton ( ) {
161+ const { data : customerPortalUrl , isLoading : isCustomerPortalUrlLoading } =
162+ useQuery ( getCustomerPortalUrl ) ;
163+
164+ if ( ! customerPortalUrl ) {
165+ return null ;
166+ }
167+
171168 return (
172- < div className = "ml-4 flex-shrink-0 sm:col-span-1 sm:mt-0" >
173- < WaspRouterLink
174- to = { routes . PricingPageRoute . to }
175- className = "text-primary hover:text-primary/80 text-sm font-medium transition-colors duration-200"
176- >
177- Buy More/Upgrade
178- </ WaspRouterLink >
179- </ div >
169+ < a href = { customerPortalUrl } target = "_blank" rel = "noopener noreferrer" >
170+ < Button disabled = { isCustomerPortalUrlLoading } variant = "link" >
171+ Manage Payment Details
172+ </ Button >
173+ </ a >
180174 ) ;
181175}
182176
183- function CustomerPortalButton ( ) {
184- const {
185- data : customerPortalUrl ,
186- isLoading : isCustomerPortalUrlLoading ,
187- error : customerPortalUrlError ,
188- } = useQuery ( getCustomerPortalUrl ) ;
189-
190- const handleClick = ( ) => {
191- if ( customerPortalUrlError ) {
192- console . error ( "Error fetching customer portal url" ) ;
193- }
194-
195- if ( customerPortalUrl ) {
196- window . open ( customerPortalUrl , "_blank" ) ;
197- } else {
198- console . error ( "Customer portal URL is not available" ) ;
199- }
200- } ;
177+ function BuyMoreButton ( {
178+ subscriptionStatus,
179+ } : Pick < User , "subscriptionStatus" > ) {
180+ if (
181+ subscriptionStatus === SubscriptionStatus . Active ||
182+ subscriptionStatus === SubscriptionStatus . CancelAtPeriodEnd
183+ ) {
184+ return null ;
185+ }
201186
202187 return (
203- < div className = "ml-4 flex-shrink-0 sm:col-span-1 sm:mt-0" >
204- < Button
205- onClick = { handleClick }
206- disabled = { isCustomerPortalUrlLoading }
207- variant = "outline"
208- size = "sm"
209- className = "text-sm font-medium"
210- >
211- Manage Subscription
212- </ Button >
213- </ div >
188+ < WaspRouterLink
189+ to = { routes . PricingPageRoute . to }
190+ className = "text-primary hover:text-primary/80 text-sm font-medium transition-colors duration-200"
191+ >
192+ < Button variant = "link" > Buy More Credits</ Button >
193+ </ WaspRouterLink >
214194 ) ;
215195}
0 commit comments