@@ -23,6 +23,7 @@ import {
23
23
DropdownMenuTrigger ,
24
24
} from "@/components/ui/dropdown-menu" ;
25
25
import { ImageUpload } from "@/components/ui/image-upload" ;
26
+ import { Input } from "@/components/ui/input" ;
26
27
import { Skeleton } from "@/components/ui/skeleton" ;
27
28
import { useThirdwebClient } from "@/constants/thirdweb.client" ;
28
29
import { useDashboardRouter } from "@/lib/DashboardRouter" ;
@@ -159,6 +160,10 @@ export function EcosystemHeader(props: {
159
160
const [ isDialogOpen , setIsDialogOpen ] = useState ( false ) ;
160
161
const [ selectedFile , setSelectedFile ] = useState < File | null > ( null ) ;
161
162
163
+ // Name editing state
164
+ const [ isNameDialogOpen , setIsNameDialogOpen ] = useState ( false ) ;
165
+ const [ tempName , setTempName ] = useState ( ecosystem . name ) ;
166
+
162
167
const storageUpload = useDashboardStorageUpload ( ) ;
163
168
const router = useDashboardRouter ( ) ;
164
169
@@ -170,7 +175,7 @@ export function EcosystemHeader(props: {
170
175
} ,
171
176
{
172
177
onSuccess : ( ) => {
173
- toast . success ( "Ecosystem image updated" ) ;
178
+ toast . success ( "Ecosystem updated" ) ;
174
179
setIsDialogOpen ( false ) ;
175
180
router . refresh ( ) ;
176
181
} ,
@@ -209,6 +214,25 @@ export function EcosystemHeader(props: {
209
214
}
210
215
}
211
216
217
+ async function handleNameSave ( ) {
218
+ const trimmed = tempName . trim ( ) ;
219
+ if ( ! trimmed || trimmed === ecosystem . name ) {
220
+ setIsNameDialogOpen ( false ) ;
221
+ return ;
222
+ }
223
+ try {
224
+ await updateEcosystem ( {
225
+ ...ecosystem ,
226
+ name : trimmed ,
227
+ } ) ;
228
+ setIsNameDialogOpen ( false ) ;
229
+ router . refresh ( ) ;
230
+ } catch ( err ) {
231
+ console . error ( err ) ;
232
+ toast . error ( "Failed to update name" ) ;
233
+ }
234
+ }
235
+
212
236
return (
213
237
< div className = "border-b py-8" >
214
238
< div className = "container flex flex-col gap-8" >
@@ -296,6 +320,52 @@ export function EcosystemHeader(props: {
296
320
) : (
297
321
< h2 className = "font-semibold text-3xl text-foreground tracking-tight" >
298
322
{ ecosystem . name }
323
+ < Dialog
324
+ open = { isNameDialogOpen }
325
+ onOpenChange = { setIsNameDialogOpen }
326
+ >
327
+ < DialogTrigger asChild >
328
+ < Button
329
+ variant = "ghost"
330
+ size = "icon"
331
+ className = "ml-2 h-5 w-5 rounded-full p-1 hover:bg-accent"
332
+ aria-label = "Edit name"
333
+ >
334
+ < PencilIcon className = "h-4 w-4" />
335
+ </ Button >
336
+ </ DialogTrigger >
337
+
338
+ < DialogContent className = "max-w-[480px]" >
339
+ < DialogHeader >
340
+ < DialogTitle > Edit Ecosystem Name</ DialogTitle >
341
+ </ DialogHeader >
342
+
343
+ < div className = "flex flex-col gap-4 py-2" >
344
+ < Input
345
+ value = { tempName }
346
+ onChange = { ( e ) => setTempName ( e . target . value ) }
347
+ />
348
+ </ div >
349
+
350
+ < DialogFooter className = "mt-4" >
351
+ < DialogClose asChild >
352
+ < Button variant = "outline" disabled = { isUpdating } >
353
+ Cancel
354
+ </ Button >
355
+ </ DialogClose >
356
+ < Button
357
+ onClick = { handleNameSave }
358
+ disabled = { isUpdating || ! tempName . trim ( ) }
359
+ >
360
+ { isUpdating ? (
361
+ < Spinner className = "h-4 w-4" />
362
+ ) : (
363
+ "Save"
364
+ ) }
365
+ </ Button >
366
+ </ DialogFooter >
367
+ </ DialogContent >
368
+ </ Dialog >
299
369
</ h2 >
300
370
) }
301
371
{ ! ecosystem . slug ? (
@@ -306,7 +376,7 @@ export function EcosystemHeader(props: {
306
376
textToCopy = { `ecosystem.${ ecosystem . slug } ` }
307
377
textToShow = { `ecosystem.${ ecosystem . slug } ` }
308
378
copyIconPosition = "right"
309
- tooltip = "Copy Ecosytem slug"
379
+ tooltip = "Copy Ecosystem slug"
310
380
variant = "ghost"
311
381
className = "-translate-x-2 px-2 py-0.5 text-muted-foreground"
312
382
/>
0 commit comments