@@ -2,6 +2,7 @@ import { Spinner } from "@/components/ui/Spinner/Spinner";
22import { Alert , AlertDescription , AlertTitle } from "@/components/ui/alert" ;
33import { Badge } from "@/components/ui/badge" ;
44import { Button } from "@/components/ui/button" ;
5+ import { Checkbox , CheckboxWithLabel } from "@/components/ui/checkbox" ;
56import {
67 Dialog ,
78 DialogContent ,
@@ -13,12 +14,12 @@ import { Input } from "@/components/ui/input";
1314import { Textarea } from "@/components/ui/textarea" ;
1415import { ToolTipLabel } from "@/components/ui/tooltip" ;
1516import {
17+ type DeleteCloudHostedInput ,
1618 type EditEngineInstanceInput ,
1719 type EngineInstance ,
18- type RemoveCloudHostedInput ,
20+ useEngineDeleteCloudHosted ,
1921 useEngineEditInstance ,
2022 type useEngineInstances ,
21- useEngineRemoveCloudHosted ,
2223 useEngineRemoveFromDashboard ,
2324} from "@3rdweb-sdk/react/hooks/useEngine" ;
2425import { FormControl , Radio , RadioGroup } from "@chakra-ui/react" ;
@@ -28,15 +29,16 @@ import { TWTable } from "components/shared/TWTable";
2829import { useTrack } from "hooks/analytics/useTrack" ;
2930import {
3031 CircleAlertIcon ,
32+ InfoIcon ,
3133 PencilIcon ,
32- SendIcon ,
3334 Trash2Icon ,
3435 TriangleAlertIcon ,
3536} from "lucide-react" ;
3637import Link from "next/link" ;
3738import { type ReactNode , useState } from "react" ;
3839import { useForm } from "react-hook-form" ;
3940import { toast } from "sonner" ;
41+ import invariant from "tiny-invariant" ;
4042import { FormLabel } from "tw-components" ;
4143
4244interface EngineInstancesTableProps {
@@ -132,7 +134,7 @@ export const EngineInstancesTable: React.FC<EngineInstancesTableProps> = ({
132134 return (
133135 < >
134136 < TWTable
135- title = "engine instances "
137+ title = "Your Engines "
136138 data = { instances }
137139 columns = { columns }
138140 isFetched = { isFetched }
@@ -153,7 +155,7 @@ export const EngineInstancesTable: React.FC<EngineInstancesTableProps> = ({
153155 } ,
154156 {
155157 icon : < Trash2Icon className = "size-4" /> ,
156- text : "Remove " ,
158+ text : "Delete " ,
157159 onClick : ( instance ) => {
158160 trackEvent ( {
159161 category : "engine" ,
@@ -257,7 +259,7 @@ const EditModal = (props: {
257259
258260 < DialogFooter className = "mt-10 gap-2" >
259261 < Button onClick = { ( ) => onOpenChange ( false ) } variant = "outline" >
260- Cancel
262+ Close
261263 </ Button >
262264 < Button
263265 type = "submit"
@@ -294,7 +296,7 @@ const RemoveModal = (props: {
294296 close = { ( ) => onOpenChange ( false ) }
295297 />
296298 ) : (
297- < CancelSubscriptionModalContent
299+ < DeleteSubscriptionModalContent
298300 refetch = { refetch }
299301 instance = { instance }
300302 close = { ( ) => onOpenChange ( false ) }
@@ -317,18 +319,22 @@ function RemoveFromDashboardModalContent(props: {
317319 < >
318320 < DialogHeader >
319321 < DialogTitle className = "mb-3 font-semibold text-2xl tracking-tight" >
320- Remove Engine Instance
322+ Remove Engine
321323 </ DialogTitle >
322324 < DialogDescription className = "text-muted-foreground" >
323325 < span className = "mb-2 block" >
324- Are you sure you want to remove
325- < em className = "font-semibold not-italic" > " { instance . name } " </ em > from
326+ Are you sure you want to remove{ " " }
327+ < em className = "font-semibold not-italic" > { instance . name } </ em > from
326328 your dashboard?
327329 </ span >
328- < span className = "block" >
329- This action does not modify your Engine infrastructure. You can
330- re-add it at any time.
331- </ span >
330+
331+ < Alert variant = "info" >
332+ < InfoIcon className = "size-5" />
333+ < AlertTitle >
334+ This action does not modify your Engine infrastructure.
335+ </ AlertTitle >
336+ < AlertDescription > You can re-add it at any time.</ AlertDescription >
337+ </ Alert >
332338 </ DialogDescription >
333339 </ DialogHeader >
334340
@@ -366,73 +372,67 @@ function RemoveFromDashboardModalContent(props: {
366372 ) ;
367373}
368374
369- function CancelSubscriptionModalContent ( props : {
375+ function DeleteSubscriptionModalContent ( props : {
370376 refetch : ( ) => void ;
371377 instance : EngineInstance ;
372378 close : ( ) => void ;
373379} ) {
374380 const { refetch, instance, close } = props ;
375- const removeCloudHosted = useEngineRemoveCloudHosted ( ) ;
381+ invariant (
382+ instance . deploymentId ,
383+ "Instance must have a deploymentId to be cancelled." ,
384+ ) ;
376385
377- const form = useForm < RemoveCloudHostedInput > ( {
386+ const deleteCloudHosted = useEngineDeleteCloudHosted ( ) ;
387+ const [ ackDeletion , setAckDeletion ] = useState ( false ) ;
388+ const form = useForm < DeleteCloudHostedInput > ( {
378389 defaultValues : {
379- instanceId : instance . id ,
390+ deploymentId : instance . deploymentId ,
380391 } ,
381392 reValidateMode : "onChange" ,
382393 } ) ;
383394
395+ const onSubmit = ( data : DeleteCloudHostedInput ) => {
396+ deleteCloudHosted . mutate ( data , {
397+ onSuccess : ( ) => {
398+ toast . success ( "Deleting Engine. Please check again in a few minutes." , {
399+ dismissible : true ,
400+ duration : 10000 ,
401+ } ) ;
402+
403+ refetch ( ) ;
404+ close ( ) ;
405+ } ,
406+ onError : ( ) => {
407+ toast . error (
408+ "Error deleting Engine. Please visit https://thirdweb.com/support." ,
409+ ) ;
410+ } ,
411+ } ) ;
412+ } ;
413+
384414 return (
385415 < div >
386416 < DialogHeader >
387417 < DialogTitle className = "mb-1 font-semibold text-2xl tracking-tight" >
388- Cancel Engine Subscription
418+ Permanently Delete Engine
389419 </ DialogTitle >
390- < DialogDescription className = "text-muted-foreground" >
391- Complete this form to request to cancel your Engine subscription. This
392- may take up to 2 business days.
393- </ DialogDescription >
394420 </ DialogHeader >
395421
396- < div className = "h-3" />
397-
398- < Alert variant = "destructive" >
399- < TriangleAlertIcon className = "!text-destructive-text size-5" />
400- < AlertTitle > This action is irreversible!</ AlertTitle >
401- < AlertDescription >
402- You will no longer be able to access this Engine's local backend
403- wallets. < strong > Any remaining mainnet funds will be lost.</ strong >
404- </ AlertDescription >
405- </ Alert >
406-
407- < div className = "h-5" />
408-
409- < form
410- onSubmit = { form . handleSubmit ( ( data ) =>
411- removeCloudHosted . mutate ( data , {
412- onSuccess : ( ) => {
413- toast . success (
414- "Submitted a request to cancel your Engine subscription. This may take up to 2 business days." ,
415- {
416- dismissible : true ,
417- duration : 10000 ,
418- } ,
419- ) ;
420-
421- refetch ( ) ;
422- close ( ) ;
423- } ,
424- onError : ( ) => {
425- toast . error (
426- "Error requesting to cancel your Engine subscription" ,
427- ) ;
428- } ,
429- } ) ,
430- ) }
431- >
432- { /* Form */ }
422+ < div className = "h-4" />
423+
424+ < p className = "text-muted-foreground" >
425+ This step will cancel your monthly subscription and immediately delete
426+ all data and infrastructure for this Engine.
427+ </ p >
428+
429+ < div className = "h-4" />
430+
431+ < form onSubmit = { form . handleSubmit ( onSubmit ) } >
432+ { /* Reason */ }
433433 < FormControl isRequired >
434434 < FormLabel className = "!text-base" >
435- Please share any feedback to help us improve
435+ Please share your feedback to help us improve Engine.
436436 </ FormLabel >
437437 < RadioGroup >
438438 < div className = "flex flex-col gap-2" >
@@ -466,13 +466,32 @@ function CancelSubscriptionModalContent(props: {
466466
467467 < div className = "h-2" />
468468
469+ { /* Feedback */ }
469470 < Textarea
470471 className = "mt-3"
471472 placeholder = "Provide additional feedback"
472473 { ...form . register ( "feedback" ) }
473474 />
474475
475- < div className = "h-8" />
476+ < div className = "h-4" />
477+
478+ < Alert variant = "destructive" >
479+ < TriangleAlertIcon className = "!text-destructive-text size-5" />
480+ < AlertTitle > This action is irreversible!</ AlertTitle >
481+
482+ < AlertDescription className = "!pl-0 pt-2" >
483+ < CheckboxWithLabel >
484+ < Checkbox
485+ checked = { ackDeletion }
486+ onCheckedChange = { ( checked ) => setAckDeletion ( ! ! checked ) }
487+ />
488+ I understand that access to my local backend wallets and any
489+ remaining funds will be lost.
490+ </ CheckboxWithLabel >
491+ </ AlertDescription >
492+ </ Alert >
493+
494+ < div className = "h-4" />
476495
477496 < DialogFooter className = "gap-2" >
478497 < Button onClick = { close } variant = "outline" >
@@ -481,15 +500,15 @@ function CancelSubscriptionModalContent(props: {
481500 < Button
482501 type = "submit"
483502 variant = "destructive"
484- disabled = { ! form . formState . isValid }
503+ disabled = {
504+ ! ackDeletion ||
505+ deleteCloudHosted . isPending ||
506+ ! form . formState . isValid
507+ }
485508 className = "gap-2"
486509 >
487- { removeCloudHosted . isPending ? (
488- < Spinner className = "size-4" />
489- ) : (
490- < SendIcon className = "size-4" />
491- ) }
492- Request to cancel
510+ { deleteCloudHosted . isPending && < Spinner className = "size-4" /> }
511+ Permanently Delete Engine
493512 </ Button >
494513 </ DialogFooter >
495514 </ form >
0 commit comments