11'use client' ;
22
3+ import { ImageUploadDialog } from '@components/admin/content/image-upload-dialog' ;
34import { Input } from '@components/ui/input' ;
45import { Label } from '@components/ui/label' ;
56import {
@@ -10,9 +11,10 @@ import {
1011 SelectValue ,
1112} from '@components/ui/select' ;
1213import { Textarea } from '@components/ui/textarea' ;
14+ import { createClient } from '@lib/supabase/client' ;
1315import { ComponentInstance } from '@lib/types/about-page-components' ;
1416import { cn } from '@lib/utils' ;
15- import { Plus , Trash2 , X } from 'lucide-react' ;
17+ import { Plus , Trash2 , Upload , X } from 'lucide-react' ;
1618
1719import React , { useEffect , useRef , useState } from 'react' ;
1820
@@ -40,6 +42,8 @@ export const ContextMenu: React.FC<ContextMenuProps> = ({
4042} ) => {
4143 const modalRef = useRef < HTMLDivElement > ( null ) ;
4244 const [ position , setPosition ] = useState ( { x, y } ) ;
45+ const [ isUploadDialogOpen , setIsUploadDialogOpen ] = useState ( false ) ;
46+ const [ userId , setUserId ] = useState < string | null > ( null ) ;
4347
4448 useEffect ( ( ) => {
4549 if ( modalRef . current ) {
@@ -79,6 +83,27 @@ export const ContextMenu: React.FC<ContextMenuProps> = ({
7983 } ;
8084 } , [ onClose ] ) ;
8185
86+ // Fetch current user ID on mount
87+ useEffect ( ( ) => {
88+ let isMounted = true ;
89+
90+ const fetchUserId = async ( ) => {
91+ const supabase = createClient ( ) ;
92+ const {
93+ data : { user } ,
94+ } = await supabase . auth . getUser ( ) ;
95+ if ( isMounted && user ) {
96+ setUserId ( user . id ) ;
97+ }
98+ } ;
99+
100+ fetchUserId ( ) ;
101+
102+ return ( ) => {
103+ isMounted = false ;
104+ } ;
105+ } , [ ] ) ;
106+
82107 /**
83108 * Validate if a value is a valid CSS dimension
84109 *
@@ -516,6 +541,46 @@ export const ContextMenu: React.FC<ContextMenuProps> = ({
516541 ) ;
517542 }
518543
544+ // Special handling for src field in image component
545+ if (
546+ key === 'src' &&
547+ component . type === 'image' &&
548+ typeof value === 'string'
549+ ) {
550+ return (
551+ < div key = { key } className = "space-y-2" >
552+ < Label htmlFor = { fieldId } className = "text-sm capitalize" >
553+ { key }
554+ </ Label >
555+ < div className = "flex gap-2" >
556+ < Input
557+ id = { fieldId }
558+ type = "text"
559+ value = { String ( value || '' ) }
560+ onChange = { e => handleInputChange ( key , e . target . value ) }
561+ placeholder = "Enter image URL"
562+ className = "h-8 flex-1 text-sm"
563+ />
564+ < button
565+ type = "button"
566+ onClick = { ( ) => setIsUploadDialogOpen ( true ) }
567+ disabled = { ! userId }
568+ className = { cn (
569+ 'flex h-8 items-center gap-1.5 rounded-md border px-3 text-xs font-medium transition-colors' ,
570+ 'border-stone-300 bg-white text-stone-700 hover:bg-stone-50' ,
571+ 'dark:border-stone-600 dark:bg-stone-800 dark:text-stone-200 dark:hover:bg-stone-700' ,
572+ 'disabled:cursor-not-allowed disabled:opacity-50'
573+ ) }
574+ title = "Upload local image"
575+ >
576+ < Upload className = "h-3.5 w-3.5" />
577+ < span > Upload</ span >
578+ </ button >
579+ </ div >
580+ </ div >
581+ ) ;
582+ }
583+
519584 // String fields
520585 if ( typeof value === 'string' ) {
521586 return (
@@ -591,6 +656,19 @@ export const ContextMenu: React.FC<ContextMenuProps> = ({
591656 renderPropertyField ( 'secondaryButton' , undefined ) }
592657 </ div >
593658 </ div >
659+
660+ { /* Image Upload Dialog */ }
661+ { userId && (
662+ < ImageUploadDialog
663+ isOpen = { isUploadDialogOpen }
664+ onClose = { ( ) => setIsUploadDialogOpen ( false ) }
665+ onUploadSuccess = { url => {
666+ // Auto-fill the uploaded image URL to src field
667+ handleInputChange ( 'src' , url ) ;
668+ } }
669+ userId = { userId }
670+ />
671+ ) }
594672 </ >
595673 ) ;
596674} ;
0 commit comments