@@ -8,34 +8,125 @@ import { useChain } from '../../hooks/useChain';
88import { useInteractiveConfig } from '@/interactive/InteractiveConfigContext' ;
99import { SidebarGroup , SidebarGroupLabel , SidebarMenu , SidebarMenuButton , SidebarMenuItem } from '@/components/ui/sidebar' ;
1010import { Input } from '@/components/ui/input' ;
11+ import { Dialog , DialogContent , DialogDescription , DialogFooter , DialogHeader , DialogTitle } from '@/components/ui/dialog' ;
12+ import { Button } from '@/components/ui/button' ;
13+ import { LuDownload } from 'react-icons/lu' ;
14+ import { Label } from '@/components/ui/label' ;
1115
12- export default function ChainPanel ( {
13- showCreateDialog,
14- setShowCreateDialog,
15- } : {
16- showCreateDialog : boolean ;
17- setShowCreateDialog : ( show : boolean ) => void ;
18- } ) {
16+ export default function ChainPanel ( ) {
17+ return (
18+ < div >
19+ < SidebarGroup >
20+ < SidebarGroupLabel > Select Chain</ SidebarGroupLabel >
21+ < SidebarMenuButton className = 'group-data-[state=expanded]:hidden' >
22+ < ArrowBigLeft />
23+ </ SidebarMenuButton >
24+ < div className = 'w-full group-data-[collapsible=icon]:hidden' >
25+ < ChainSelector />
26+ </ div >
27+ </ SidebarGroup >
28+ < SidebarGroup >
29+ < SidebarGroupLabel > Chain Functions</ SidebarGroupLabel >
30+ < SidebarMenu >
31+ < ChainCreate />
32+ < ChainRename />
33+ < ChainExport />
34+ < ChainDelete />
35+ </ SidebarMenu >
36+ </ SidebarGroup >
37+ </ div >
38+ ) ;
39+ }
40+
41+ export function ChainCreate ( ) {
42+ const [ showCreateDialog , setShowCreateDialog ] = useState ( false ) ;
43+ const context = useInteractiveConfig ( ) ;
44+ const router = useRouter ( ) ;
45+ const [ newChainName , setNewChainName ] = useState ( '' ) ;
46+
47+ const handleNewChain = async ( ) => {
48+ await context . sdk . addChain ( newChainName ) ;
49+ router . push ( `/settings/chains?chain=${ newChainName } ` ) ;
50+ setShowCreateDialog ( false ) ;
51+ } ;
52+
53+ const handleChainImport = async ( event : React . ChangeEvent < HTMLInputElement > ) => {
54+ const files = Array . from ( event . target . files ?? [ ] ) ;
55+ for ( const file of files ) {
56+ const fileContent = await file . text ( ) ;
57+ if ( newChainName === '' ) {
58+ const filename = file . name . replace ( '.json' , '' ) ;
59+ setNewChainName ( filename ) ;
60+ }
61+ const steps = JSON . parse ( fileContent ) ;
62+ await context . sdk . addChain ( newChainName ) ;
63+ await context . sdk . importChain ( newChainName , steps ) ;
64+ router . push ( `/chains?chain=${ newChainName } ` ) ;
65+ }
66+ setShowCreateDialog ( false ) ;
67+ } ;
68+
69+ return (
70+ < >
71+ < SidebarMenuItem >
72+ < SidebarMenuButton side = 'left' tooltip = 'Create Chain' onClick = { ( ) => setShowCreateDialog ( true ) } >
73+ < Plus />
74+ < span > Create Chain</ span >
75+ </ SidebarMenuButton >
76+ </ SidebarMenuItem >
77+ < Dialog open = { showCreateDialog } onOpenChange = { setShowCreateDialog } >
78+ < DialogContent >
79+ < DialogHeader >
80+ < DialogTitle > Create New Chain</ DialogTitle >
81+ </ DialogHeader >
82+ < div className = 'grid gap-4 py-4' >
83+ < div className = 'grid grid-cols-4 items-center gap-4' >
84+ < Label htmlFor = 'chain-name' className = 'text-right' >
85+ Chain Name
86+ </ Label >
87+ < Input
88+ id = 'chain-name'
89+ value = { newChainName }
90+ onChange = { ( e ) => setNewChainName ( e . target . value ) }
91+ className = 'col-span-3'
92+ />
93+ </ div >
94+ < div className = 'grid grid-cols-4 items-center gap-4' >
95+ < Label htmlFor = 'import-chain' className = 'text-right' >
96+ Import Chain
97+ </ Label >
98+ < Input id = 'import-chain' type = 'file' onChange = { handleChainImport } className = 'col-span-3' />
99+ </ div >
100+ </ div >
101+ < DialogFooter >
102+ < Button variant = 'outline' onClick = { ( ) => setShowCreateDialog ( false ) } >
103+ Cancel
104+ </ Button >
105+ < Button onClick = { handleNewChain } > Create Chain</ Button >
106+ </ DialogFooter >
107+ </ DialogContent >
108+ </ Dialog >
109+ </ >
110+ ) ;
111+ }
112+
113+ export function ChainRename ( ) {
114+ const [ isRenameDialogOpen , setIsRenameDialogOpen ] = useState ( false ) ;
19115 const [ renaming , setRenaming ] = useState ( false ) ;
20116 const [ newName , setNewName ] = useState ( '' ) ;
21117 const context = useInteractiveConfig ( ) ;
22118 const router = useRouter ( ) ;
23119 const pathname = usePathname ( ) ;
24120 const searchParams = useSearchParams ( ) ;
25121
26- const { data : chainData , error } = useChain ( searchParams . get ( 'chain' ) ?? undefined ) ;
122+ const { data : chainData } = useChain ( searchParams . get ( 'chain' ) ?? undefined ) ;
27123
28124 useEffect ( ( ) => {
29125 if ( renaming ) {
30126 setNewName ( searchParams . get ( 'chain' ) ?? '' ) ;
31127 }
32128 } , [ renaming ] ) ;
33129
34- const handleDelete = async ( ) => {
35- await context . sdk . deleteChain ( searchParams . get ( 'chain' ) ?? '' ) ;
36- router . push ( pathname ) ;
37- } ;
38-
39130 const handleRename = async ( ) => {
40131 if ( ( newName && newName !== searchParams . get ( 'chain' ) ) ?? '' ) {
41132 await context . sdk . renameChain ( searchParams . get ( 'chain' ) ?? '' , newName ) ;
@@ -46,6 +137,45 @@ export default function ChainPanel({
46137 }
47138 } ;
48139
140+ return (
141+ < >
142+ < SidebarMenuItem >
143+ < SidebarMenuButton
144+ side = 'left'
145+ tooltip = 'Rename Chain'
146+ onClick = { ( ) => setIsRenameDialogOpen ( true ) }
147+ disabled = { ! chainData }
148+ >
149+ < Pencil className = 'size-4' />
150+ < span > Rename Chain</ span >
151+ </ SidebarMenuButton >
152+ </ SidebarMenuItem >
153+ < Dialog open = { isRenameDialogOpen } onOpenChange = { setIsRenameDialogOpen } >
154+ < DialogContent >
155+ < DialogHeader >
156+ < DialogTitle > Rename Chain</ DialogTitle >
157+ </ DialogHeader >
158+ < div className = 'grid gap-4 py-4' >
159+ < Input value = { newName } onChange = { ( e ) => setNewName ( e . target . value ) } placeholder = 'Enter new name' />
160+ </ div >
161+ < DialogFooter >
162+ < Button variant = 'outline' onClick = { ( ) => setIsRenameDialogOpen ( false ) } >
163+ Cancel
164+ </ Button >
165+ < Button onClick = { handleRename } > Rename</ Button >
166+ </ DialogFooter >
167+ </ DialogContent >
168+ </ Dialog >
169+ </ >
170+ ) ;
171+ }
172+
173+ export function ChainExport ( ) {
174+ const context = useInteractiveConfig ( ) ;
175+ const searchParams = useSearchParams ( ) ;
176+
177+ const { data : chainData } = useChain ( searchParams . get ( 'chain' ) ?? undefined ) ;
178+
49179 const handleExportChain = async ( ) => {
50180 const chainData = await context . sdk . getChain ( searchParams . get ( 'chain' ) ?? '' ) ;
51181 const element = document . createElement ( 'a' ) ;
@@ -58,56 +188,56 @@ export default function ChainPanel({
58188 } ;
59189
60190 return (
61- < div className = 'space-y-4' >
62- < SidebarGroup >
63- < SidebarGroupLabel > Select Chain</ SidebarGroupLabel >
64- < SidebarMenuButton className = 'group-data-[state=expanded]:hidden' >
65- < ArrowBigLeft />
191+ < SidebarMenuItem >
192+ < SidebarMenuButton side = 'left' tooltip = 'Export Chain' onClick = { handleExportChain } disabled = { ! chainData } >
193+ < LuDownload className = 'size-4' />
194+ < span > Export Chain</ span >
195+ </ SidebarMenuButton >
196+ </ SidebarMenuItem >
197+ ) ;
198+ }
199+
200+ export function ChainDelete ( ) {
201+ const [ isDeleteDialogOpen , setIsDeleteDialogOpen ] = useState ( false ) ;
202+ const context = useInteractiveConfig ( ) ;
203+ const router = useRouter ( ) ;
204+ const pathname = usePathname ( ) ;
205+ const searchParams = useSearchParams ( ) ;
206+ const { data : chainData } = useChain ( searchParams . get ( 'chain' ) ?? undefined ) ;
207+
208+ const handleDelete = async ( ) => {
209+ await context . sdk . deleteChain ( searchParams . get ( 'chain' ) ?? '' ) ;
210+ router . push ( pathname ) ;
211+ setIsDeleteDialogOpen ( false ) ;
212+ } ;
213+
214+ return (
215+ < >
216+ < SidebarMenuItem >
217+ < SidebarMenuButton
218+ side = 'left'
219+ tooltip = 'Delete Chain'
220+ onClick = { ( ) => setIsDeleteDialogOpen ( true ) }
221+ disabled = { ! chainData }
222+ >
223+ < Trash2 />
224+ < span > Delete Chain</ span >
66225 </ SidebarMenuButton >
67- < div className = 'w-full group-data-[collapsible=icon]:hidden' >
68- { renaming ? (
69- < Input value = { newName } onChange = { ( e ) => setNewName ( e . target . value ) } className = 'w-full' />
70- ) : (
71- < ChainSelector />
72- ) }
73- </ div >
74- < SidebarGroupLabel > Chain Functions</ SidebarGroupLabel >
75- < SidebarMenu >
76- { [
77- {
78- title : 'Create Chain' ,
79- icon : Plus ,
80- func : ( ) => setShowCreateDialog ( true ) ,
81- disabled : renaming || showCreateDialog ,
82- } ,
83- {
84- title : renaming ? 'Save Name' : 'Rename Chain' ,
85- icon : renaming ? Check : Pencil ,
86- func : renaming ? handleRename : ( ) => setRenaming ( true ) ,
87- disabled : ! chainData || ( renaming && ( ! newName || newName === searchParams . get ( 'chain' ) ) ) ,
88- } ,
89- {
90- title : 'Export Chain' ,
91- icon : Download ,
92- func : handleExportChain ,
93- disabled : ! chainData || renaming ,
94- } ,
95- {
96- title : 'Delete Chain' ,
97- icon : Trash2 ,
98- func : handleDelete ,
99- disabled : ! chainData || renaming ,
100- } ,
101- ] . map ( ( item ) => (
102- < SidebarMenuItem key = { item . title } >
103- < SidebarMenuButton side = 'left' tooltip = { item . title } onClick = { item . func } disabled = { item . disabled } >
104- { item . icon && < item . icon /> }
105- < span > { item . title } </ span >
106- </ SidebarMenuButton >
107- </ SidebarMenuItem >
108- ) ) }
109- </ SidebarMenu >
110- </ SidebarGroup >
111- </ div >
226+ </ SidebarMenuItem >
227+ < Dialog open = { isDeleteDialogOpen } onOpenChange = { setIsDeleteDialogOpen } >
228+ < DialogContent >
229+ < DialogHeader >
230+ < DialogTitle > Delete Chain</ DialogTitle >
231+ </ DialogHeader >
232+ < DialogDescription > Are you sure you want to delete this chain? This action cannot be undone.</ DialogDescription >
233+ < DialogFooter >
234+ < Button variant = 'outline' onClick = { ( ) => setIsDeleteDialogOpen ( false ) } >
235+ Cancel
236+ </ Button >
237+ < Button onClick = { handleDelete } > Delete</ Button >
238+ </ DialogFooter >
239+ </ DialogContent >
240+ </ Dialog >
241+ </ >
112242 ) ;
113243}
0 commit comments