@@ -2,6 +2,7 @@ import { loadStripe } from "@stripe/stripe-js";
22import clsx from "clsx" ;
33import {
44 AlertTriangle ,
5+ Bell ,
56 CheckIcon ,
67 CreditCard ,
78 FileText ,
@@ -25,7 +26,17 @@ import {
2526 CardTitle ,
2627} from "@/components/ui/card" ;
2728import { NumberInput } from "@/components/ui/input" ;
29+ import {
30+ Dialog ,
31+ DialogContent ,
32+ DialogDescription ,
33+ DialogHeader ,
34+ DialogTitle ,
35+ DialogTrigger ,
36+ } from "@/components/ui/dialog" ;
37+ import { Label } from "@/components/ui/label" ;
2838import { Progress } from "@/components/ui/progress" ;
39+ import { Switch } from "@/components/ui/switch" ;
2940import { Tabs , TabsList , TabsTrigger } from "@/components/ui/tabs" ;
3041import { cn } from "@/lib/utils" ;
3142import { api } from "@/utils/api" ;
@@ -90,6 +101,8 @@ export const ShowBilling = () => {
90101 api . stripe . createCustomerPortalSession . useMutation ( ) ;
91102 const { mutateAsync : upgradeSubscription , isPending : isUpgrading } =
92103 api . stripe . upgradeSubscription . useMutation ( ) ;
104+ const { mutateAsync : updateInvoiceNotifications } =
105+ api . stripe . updateInvoiceNotifications . useMutation ( ) ;
93106 const utils = api . useUtils ( ) ;
94107
95108 const [ hobbyServerQuantity , setHobbyServerQuantity ] = useState ( 1 ) ;
@@ -151,14 +164,68 @@ export const ShowBilling = () => {
151164 < div className = "w-full" >
152165 < Card className = "bg-sidebar p-2.5 rounded-xl max-w-6xl mx-auto" >
153166 < div className = "rounded-xl bg-background shadow-md" >
154- < CardHeader >
155- < CardTitle className = "text-xl flex flex-row gap-2" >
156- < CreditCard className = "size-6 text-muted-foreground self-center" />
157- Billing
158- </ CardTitle >
159- < CardDescription >
160- Manage your subscription and invoices
161- </ CardDescription >
167+ < CardHeader className = "flex flex-row items-start justify-between" >
168+ < div >
169+ < CardTitle className = "text-xl flex flex-row gap-2" >
170+ < CreditCard className = "size-6 text-muted-foreground self-center" />
171+ Billing
172+ </ CardTitle >
173+ < CardDescription >
174+ Manage your subscription and invoices
175+ </ CardDescription >
176+ </ div >
177+ { ( admin ?. user . stripeSubscriptionId || isEnterpriseCloud ) && (
178+ < Dialog >
179+ < DialogTrigger asChild >
180+ < Button variant = "outline" size = "icon" >
181+ < Bell className = "size-4" />
182+ </ Button >
183+ </ DialogTrigger >
184+ < DialogContent className = "sm:max-w-md" >
185+ < DialogHeader >
186+ < DialogTitle > Notification Settings</ DialogTitle >
187+ < DialogDescription >
188+ Configure your billing email notifications.
189+ </ DialogDescription >
190+ </ DialogHeader >
191+ < div className = "flex items-center justify-between rounded-lg border p-4" >
192+ < div className = "space-y-0.5" >
193+ < Label htmlFor = "invoice-notifications" >
194+ Invoice Notifications
195+ </ Label >
196+ < p className = "text-sm text-muted-foreground" >
197+ Receive email notifications for payments and failed
198+ charges.
199+ </ p >
200+ </ div >
201+ < Switch
202+ id = "invoice-notifications"
203+ checked = {
204+ admin ?. user . sendInvoiceNotifications ?? false
205+ }
206+ onCheckedChange = { async ( checked ) => {
207+ await updateInvoiceNotifications ( {
208+ enabled : checked ,
209+ } )
210+ . then ( ( ) => {
211+ utils . user . get . invalidate ( ) ;
212+ toast . success (
213+ checked
214+ ? "Invoice notifications enabled"
215+ : "Invoice notifications disabled" ,
216+ ) ;
217+ } )
218+ . catch ( ( ) => {
219+ toast . error (
220+ "Failed to update invoice notifications" ,
221+ ) ;
222+ } ) ;
223+ } }
224+ />
225+ </ div >
226+ </ DialogContent >
227+ </ Dialog >
228+ ) }
162229 </ CardHeader >
163230 < CardContent className = "space-y-4 py-4 border-t" >
164231 < nav className = "flex space-x-2 border-b" >
0 commit comments