1- import { Calendar , Layers } from "lucide-react" ;
1+ import { useState } from "react" ;
2+ import { Calendar , Layers , MoreVertical , Trash2 } from "lucide-react" ;
23import { Link } from "react-router" ;
34
5+ import { trpc } from "~/api/trpc" ;
6+ import {
7+ AlertDialog ,
8+ AlertDialogAction ,
9+ AlertDialogCancel ,
10+ AlertDialogContent ,
11+ AlertDialogDescription ,
12+ AlertDialogFooter ,
13+ AlertDialogHeader ,
14+ AlertDialogTitle ,
15+ } from "~/components/ui/alert-dialog" ;
16+ import { Button } from "~/components/ui/button" ;
417import {
518 Card ,
619 CardContent ,
720 CardDescription ,
821 CardHeader ,
922 CardTitle ,
1023} from "~/components/ui/card" ;
24+ import {
25+ DropdownMenu ,
26+ DropdownMenuContent ,
27+ DropdownMenuItem ,
28+ DropdownMenuTrigger ,
29+ } from "~/components/ui/dropdown-menu" ;
1130import { useWorkspace } from "~/components/WorkspaceProvider" ;
1231
1332type EnvironmentCardProps = {
@@ -27,8 +46,26 @@ export const EnvironmentCard: React.FC<EnvironmentCardProps> = ({
2746 system,
2847} ) => {
2948 const { workspace } = useWorkspace ( ) ;
49+ const [ showDeleteDialog , setShowDeleteDialog ] = useState ( false ) ;
3050
3151 const environmentUrl = `/${ workspace . slug } /environments/${ environment . id } ` ;
52+ const utils = trpc . useUtils ( ) ;
53+
54+ const deleteEnvironment = trpc . environment . delete . useMutation ( {
55+ onSuccess : ( ) => {
56+ void utils . environment . list . invalidate ( ) ;
57+ } ,
58+ } ) ;
59+
60+ const handleDelete = ( e : React . MouseEvent ) => {
61+ e . preventDefault ( ) ;
62+ e . stopPropagation ( ) ;
63+ void deleteEnvironment . mutate ( {
64+ workspaceId : workspace . id ,
65+ environmentId : environment . id ,
66+ } ) ;
67+ setShowDeleteDialog ( false ) ;
68+ } ;
3269
3370 const formatDate = ( date ?: string ) => {
3471 if ( ! date ) return null ;
@@ -40,38 +77,95 @@ export const EnvironmentCard: React.FC<EnvironmentCardProps> = ({
4077 } ;
4178
4279 return (
43- < Link to = { environmentUrl } className = "block" >
44- < Card className = "h-56 transition-all hover:border-primary/50 hover:shadow-md" >
45- < CardHeader >
46- < CardTitle className = "flex items-center justify-between" >
47- < span className = "truncate" > { environment . name } </ span >
48- </ CardTitle >
80+ < >
81+ < Link to = { environmentUrl } className = "block" >
82+ < Card className = "h-56 transition-all hover:border-primary/50 hover:shadow-md" >
83+ < CardHeader >
84+ < CardTitle className = "flex items-center justify-between" >
85+ < span className = "truncate" > { environment . name } </ span >
86+ < DropdownMenu >
87+ < DropdownMenuTrigger
88+ asChild
89+ onClick = { ( e ) => e . preventDefault ( ) }
90+ >
91+ < Button
92+ variant = "ghost"
93+ size = "icon"
94+ className = "h-8 w-8 text-muted-foreground hover:text-foreground"
95+ >
96+ < MoreVertical className = "h-4 w-4" />
97+ </ Button >
98+ </ DropdownMenuTrigger >
99+ < DropdownMenuContent
100+ align = "end"
101+ onClick = { ( e ) => e . preventDefault ( ) }
102+ >
103+ < DropdownMenuItem
104+ className = "text-destructive focus:text-destructive"
105+ onClick = { ( e ) => {
106+ e . preventDefault ( ) ;
107+ e . stopPropagation ( ) ;
108+ setShowDeleteDialog ( true ) ;
109+ } }
110+ >
111+ < Trash2 className = "mr-2 h-4 w-4" />
112+ Delete
113+ </ DropdownMenuItem >
114+ </ DropdownMenuContent >
115+ </ DropdownMenu >
116+ </ CardTitle >
117+
118+ < CardDescription className = "text-xs" > { system . name } </ CardDescription >
49119
50- < CardDescription className = "text-xs" > { system . name } </ CardDescription >
120+ { environment . description && (
121+ < p className = "mt-2 text-xs text-muted-foreground" >
122+ { environment . description }
123+ </ p >
124+ ) }
125+ </ CardHeader >
126+ < CardContent className = "space-y-3" >
127+ { environment . resourceSelector ?. cel && (
128+ < div className = "flex items-start gap-2" >
129+ < Layers className = "mt-0.5 h-4 w-4 flex-shrink-0 text-muted-foreground" />
130+ < code className = "block truncate rounded bg-muted px-2 py-1 text-xs" >
131+ { environment . resourceSelector . cel }
132+ </ code >
133+ </ div >
134+ ) }
135+ { environment . createdAt && (
136+ < div className = "flex items-center gap-2 text-xs text-muted-foreground" >
137+ < Calendar className = "h-3 w-3" />
138+ < span > Created { formatDate ( environment . createdAt ) } </ span >
139+ </ div >
140+ ) }
141+ </ CardContent >
142+ </ Card >
143+ </ Link >
51144
52- { environment . description && (
53- < p className = "mt-2 text-xs text-muted-foreground" >
54- { environment . description }
55- </ p >
56- ) }
57- </ CardHeader >
58- < CardContent className = "space-y-3" >
59- { environment . resourceSelector ?. cel && (
60- < div className = "flex items-start gap-2" >
61- < Layers className = "mt-0.5 h-4 w-4 flex-shrink-0 text-muted-foreground" />
62- < code className = "block truncate rounded bg-muted px-2 py-1 text-xs" >
63- { environment . resourceSelector . cel }
64- </ code >
65- </ div >
66- ) }
67- { environment . createdAt && (
68- < div className = "flex items-center gap-2 text-xs text-muted-foreground" >
69- < Calendar className = "h-3 w-3" />
70- < span > Created { formatDate ( environment . createdAt ) } </ span >
71- </ div >
72- ) }
73- </ CardContent >
74- </ Card >
75- </ Link >
145+ < AlertDialog open = { showDeleteDialog } onOpenChange = { setShowDeleteDialog } >
146+ < AlertDialogContent onClick = { ( e ) => e . stopPropagation ( ) } >
147+ < AlertDialogHeader >
148+ < AlertDialogTitle > Delete Environment</ AlertDialogTitle >
149+ < AlertDialogDescription >
150+ Are you sure you want to delete{ " " }
151+ < strong > { environment . name } </ strong > ? This action cannot be undone
152+ and will permanently remove the environment and all its associated
153+ data.
154+ </ AlertDialogDescription >
155+ </ AlertDialogHeader >
156+ < AlertDialogFooter >
157+ < AlertDialogCancel onClick = { ( e ) => e . stopPropagation ( ) } >
158+ Cancel
159+ </ AlertDialogCancel >
160+ < AlertDialogAction
161+ onClick = { handleDelete }
162+ className = "bg-destructive text-destructive-foreground hover:bg-destructive/90"
163+ >
164+ Delete
165+ </ AlertDialogAction >
166+ </ AlertDialogFooter >
167+ </ AlertDialogContent >
168+ </ AlertDialog >
169+ </ >
76170 ) ;
77171} ;
0 commit comments