-
Notifications
You must be signed in to change notification settings - Fork 1.7k
feat: add subscription management to settings panel #2868
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
a73b440
5f9a038
be0b674
f0b642d
19f437a
e69617c
303af09
231d873
df4daab
2bf01f1
291233a
3de3840
ec39c3b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,182 @@ | ||||||||||||||||||||||||||||||||||||||||||||||
| 'use client'; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| import { useStateManager } from '@/components/store/state'; | ||||||||||||||||||||||||||||||||||||||||||||||
| import { api } from '@/trpc/react'; | ||||||||||||||||||||||||||||||||||||||||||||||
| import { Button } from '@onlook/ui/button'; | ||||||||||||||||||||||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||||||||||||||||||||||
| DropdownMenu, | ||||||||||||||||||||||||||||||||||||||||||||||
| DropdownMenuContent, | ||||||||||||||||||||||||||||||||||||||||||||||
| DropdownMenuItem, | ||||||||||||||||||||||||||||||||||||||||||||||
| DropdownMenuTrigger, | ||||||||||||||||||||||||||||||||||||||||||||||
| } from '@onlook/ui/dropdown-menu'; | ||||||||||||||||||||||||||||||||||||||||||||||
| import { Icons } from '@onlook/ui/icons'; | ||||||||||||||||||||||||||||||||||||||||||||||
| import { Separator } from '@onlook/ui/separator'; | ||||||||||||||||||||||||||||||||||||||||||||||
| import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@onlook/ui/dialog'; | ||||||||||||||||||||||||||||||||||||||||||||||
| import { observer } from 'mobx-react-lite'; | ||||||||||||||||||||||||||||||||||||||||||||||
| import { useState } from 'react'; | ||||||||||||||||||||||||||||||||||||||||||||||
| import { useSubscription } from '../pricing-modal/use-subscription'; | ||||||||||||||||||||||||||||||||||||||||||||||
| import { ProductType, ScheduledSubscriptionAction } from '@onlook/stripe'; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| export const SubscriptionTab = observer(() => { | ||||||||||||||||||||||||||||||||||||||||||||||
| const stateManager = useStateManager(); | ||||||||||||||||||||||||||||||||||||||||||||||
| const { data: user } = api.user.get.useQuery(); | ||||||||||||||||||||||||||||||||||||||||||||||
| const { subscription, isPro } = useSubscription(); | ||||||||||||||||||||||||||||||||||||||||||||||
| const [showCancelModal, setShowCancelModal] = useState(false); | ||||||||||||||||||||||||||||||||||||||||||||||
| const [isManageDropdownOpen, setIsManageDropdownOpen] = useState(false); | ||||||||||||||||||||||||||||||||||||||||||||||
| const [isLoadingPortal, setIsLoadingPortal] = useState(false); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| const manageSubscriptionMutation = api.subscription.manageSubscription.useMutation({ | ||||||||||||||||||||||||||||||||||||||||||||||
| onSuccess: (session) => { | ||||||||||||||||||||||||||||||||||||||||||||||
| if (session?.url) { | ||||||||||||||||||||||||||||||||||||||||||||||
| window.open(session.url, '_blank'); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||
| onError: (error) => { | ||||||||||||||||||||||||||||||||||||||||||||||
| console.error('Failed to create portal session:', error); | ||||||||||||||||||||||||||||||||||||||||||||||
Kitenite marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||
| onSettled: () => { | ||||||||||||||||||||||||||||||||||||||||||||||
| setIsLoadingPortal(false); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| const handleUpgradePlan = () => { | ||||||||||||||||||||||||||||||||||||||||||||||
| stateManager.isSubscriptionModalOpen = true; | ||||||||||||||||||||||||||||||||||||||||||||||
| stateManager.isSettingsModalOpen = false; | ||||||||||||||||||||||||||||||||||||||||||||||
| setIsManageDropdownOpen(false); | ||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| const handleCancelSubscription = () => { | ||||||||||||||||||||||||||||||||||||||||||||||
| setShowCancelModal(true); | ||||||||||||||||||||||||||||||||||||||||||||||
| setIsManageDropdownOpen(false); | ||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| const handleConfirmCancel = () => { | ||||||||||||||||||||||||||||||||||||||||||||||
| // Cancellation logic will be implemented later | ||||||||||||||||||||||||||||||||||||||||||||||
| setShowCancelModal(false); | ||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
| const handleConfirmCancel = () => { | |
| // Cancellation logic will be implemented later | |
| setShowCancelModal(false); | |
| }; | |
| const handleConfirmCancel = async () => { | |
| try { | |
| // Call the subscription cancellation API | |
| await cancelUserSubscription(); | |
| // Show success message to user | |
| toast.success("Your subscription has been successfully canceled"); | |
| // Close the modal | |
| setShowCancelModal(false); | |
| } catch (error) { | |
| // Handle errors | |
| console.error("Failed to cancel subscription:", error); | |
| toast.error("Failed to cancel your subscription. Please try again or contact support."); | |
| } | |
| }; | |
Spotted by Diamond
Is this helpful? React 👍 or 👎 to let us know.
Kitenite marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential null pointer exception when accessing subscription.scheduledChange.scheduledChangeAt. If scheduledChange exists but scheduledChangeAt is null/undefined, calling toLocaleDateString() will throw a runtime error. Add null checking: subscription.scheduledChange?.scheduledChangeAt?.toLocaleDateString()
| subscription?.scheduledChange?.scheduledAction === ScheduledSubscriptionAction.CANCELLATION ? ( | |
| <>Pro plan (cancelling on {subscription.scheduledChange.scheduledChangeAt.toLocaleDateString()})</> | |
| subscription?.scheduledChange?.scheduledAction === ScheduledSubscriptionAction.CANCELLATION ? ( | |
| <>Pro plan (cancelling on {subscription.scheduledChange?.scheduledChangeAt?.toLocaleDateString()})</> | |
Spotted by Diamond
Is this helpful? React 👍 or 👎 to let us know.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The 'user' data from the API query is fetched but never used. Consider removing it if not needed.