@@ -23,6 +23,7 @@ import {
2323 DropdownMenuTrigger ,
2424} from "@/components/ui/dropdown-menu" ;
2525import { ImageUpload } from "@/components/ui/image-upload" ;
26+ import { Input } from "@/components/ui/input" ;
2627import { Skeleton } from "@/components/ui/skeleton" ;
2728import { useThirdwebClient } from "@/constants/thirdweb.client" ;
2829import { useDashboardRouter } from "@/lib/DashboardRouter" ;
@@ -159,6 +160,10 @@ export function EcosystemHeader(props: {
159160 const [ isDialogOpen , setIsDialogOpen ] = useState ( false ) ;
160161 const [ selectedFile , setSelectedFile ] = useState < File | null > ( null ) ;
161162
163+ // Name editing state
164+ const [ isNameDialogOpen , setIsNameDialogOpen ] = useState ( false ) ;
165+ const [ tempName , setTempName ] = useState ( ecosystem . name ) ;
166+
162167 const storageUpload = useDashboardStorageUpload ( ) ;
163168 const router = useDashboardRouter ( ) ;
164169
@@ -170,7 +175,7 @@ export function EcosystemHeader(props: {
170175 } ,
171176 {
172177 onSuccess : ( ) => {
173- toast . success ( "Ecosystem image updated" ) ;
178+ toast . success ( "Ecosystem updated" ) ;
174179 setIsDialogOpen ( false ) ;
175180 router . refresh ( ) ;
176181 } ,
@@ -209,6 +214,25 @@ export function EcosystemHeader(props: {
209214 }
210215 }
211216
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+
212236 return (
213237 < div className = "border-b py-8" >
214238 < div className = "container flex flex-col gap-8" >
@@ -296,6 +320,52 @@ export function EcosystemHeader(props: {
296320 ) : (
297321 < h2 className = "font-semibold text-3xl text-foreground tracking-tight" >
298322 { 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 >
299369 </ h2 >
300370 ) }
301371 { ! ecosystem . slug ? (
@@ -306,7 +376,7 @@ export function EcosystemHeader(props: {
306376 textToCopy = { `ecosystem.${ ecosystem . slug } ` }
307377 textToShow = { `ecosystem.${ ecosystem . slug } ` }
308378 copyIconPosition = "right"
309- tooltip = "Copy Ecosytem slug"
379+ tooltip = "Copy Ecosystem slug"
310380 variant = "ghost"
311381 className = "-translate-x-2 px-2 py-0.5 text-muted-foreground"
312382 />
0 commit comments