6
6
AlertDialogContent ,
7
7
AlertDialogDescription ,
8
8
AlertDialogHeader ,
9
- AlertDialogTitle ,
10
9
AlertDialogTrigger ,
11
10
} from "@/components/ui/alert-dialog" ;
12
11
import { Cross1Icon } from "@radix-ui/react-icons" ;
@@ -16,6 +15,7 @@ import { toast } from 'sonner';
16
15
import { Button } from "@/components/ui/button" ;
17
16
import { Icons } from "@/components/ui/icons" ;
18
17
import { Textarea } from '../ui/textarea' ;
18
+ import UploadComponent from './UploadComponent' ;
19
19
20
20
const backendUrl = import . meta. env . VITE_BACKEND_URL || 'http://localhost:5000' ;
21
21
@@ -24,6 +24,7 @@ interface Project {
24
24
description : string ;
25
25
repoLink : string ;
26
26
tags : Tag [ ] ;
27
+ imageUrl ?: string ;
27
28
}
28
29
29
30
interface Tag {
@@ -37,8 +38,10 @@ const AddProject: React.FC<{ onProjectChange: () => void }> = ({ onProjectChange
37
38
description : '' ,
38
39
repoLink : '' ,
39
40
tags : [ ] ,
41
+ imageUrl : '' , // Add image URL state
40
42
} ) ;
41
43
44
+ const [ imageFile , setImageFile ] = useState < File | null > ( null ) ; // State for handling image file
42
45
const username = localStorage . getItem ( 'devhub_username' ) ;
43
46
const [ isLoading , setIsLoading ] = useState ( false ) ;
44
47
@@ -50,16 +53,39 @@ const AddProject: React.FC<{ onProjectChange: () => void }> = ({ onProjectChange
50
53
} ) ) ;
51
54
} ;
52
55
56
+ // Function to upload image to Cloudinary via the backend
57
+ const uploadImage = async ( ) => {
58
+ if ( ! imageFile ) return '' ; // Return empty string if no image is uploaded
59
+
60
+ const formData = new FormData ( ) ;
61
+ formData . append ( 'image' , imageFile ) ;
62
+
63
+ try {
64
+ const response = await axios . post ( `${ backendUrl } /project/upload` , formData , {
65
+ headers : {
66
+ 'Content-Type' : 'multipart/form-data' ,
67
+ } ,
68
+ } ) ;
69
+
70
+ return response . data . imageUrl ; // Return the image URL
71
+ } catch ( error ) {
72
+ console . error ( 'Image upload failed:' , error ) ;
73
+ toast . error ( 'Failed to upload image' ) ;
74
+ return '' ;
75
+ }
76
+ } ;
77
+
53
78
const handleAddProject = async ( ) => {
54
79
if ( ! username ) {
55
80
toast . error ( 'Username not found' ) ;
56
81
return ;
57
82
}
58
83
59
- setIsLoading ( true ) ;
84
+ setIsLoading ( true ) ;
60
85
61
86
try {
62
- // Convert tags array to a comma-separated string
87
+ const imageUrl = await uploadImage ( ) ; // Upload the image and get the URL
88
+
63
89
const tagsString = newProject . tags . map ( ( tag ) => tag . value ) . join ( ',' ) ;
64
90
65
91
const response = await axios . post (
@@ -68,14 +94,16 @@ const AddProject: React.FC<{ onProjectChange: () => void }> = ({ onProjectChange
68
94
title : newProject . title ,
69
95
description : newProject . description ,
70
96
repo_link : newProject . repoLink ,
71
- tags : tagsString , // Send as comma-separated string
97
+ tags : tagsString , // Send as comma-separated string
98
+ imageUrl, // Include the uploaded image URL
72
99
} ,
73
100
{ withCredentials : true } ,
74
101
) ;
75
102
76
103
if ( response . status === 200 || response . status === 201 ) {
77
104
toast . success ( 'Project added successfully' ) ;
78
- setNewProject ( { title : '' , description : '' , repoLink : '' , tags : [ ] } ) ; // Reset form
105
+ setNewProject ( { title : '' , description : '' , repoLink : '' , tags : [ ] , imageUrl : '' } ) ; // Reset form
106
+ setImageFile ( null ) ; // Reset the image file
79
107
onProjectChange ( ) ;
80
108
} else {
81
109
toast . error ( 'Failed to add project' ) ;
@@ -84,80 +112,78 @@ const AddProject: React.FC<{ onProjectChange: () => void }> = ({ onProjectChange
84
112
console . error ( 'Failed to add project:' , error ) ;
85
113
toast . error ( 'An error occurred while adding the project' ) ;
86
114
} finally {
87
- setIsLoading ( false ) ; // Reset loading state
115
+ setIsLoading ( false ) ; // Reset loading state
88
116
}
89
117
} ;
90
118
91
-
92
-
93
-
94
119
return (
95
120
< AlertDialog >
96
121
< AlertDialogTrigger className = "w-[120px] ml-5 mt-5 mb-3" >
97
122
< Button variant = "outline" className = "h-[50px]" > Add Project</ Button >
98
123
</ AlertDialogTrigger >
99
124
< AlertDialogContent >
100
125
< div className = 'flex' >
101
- < AlertDialogHeader className = 'text-2xl' > Add New Project</ AlertDialogHeader >
126
+ < AlertDialogHeader className = 'text-2xl mt-1.5 ' > Add New Project</ AlertDialogHeader >
102
127
< div className = 'flex-grow' > </ div >
103
- < AlertDialogCancel > < Cross1Icon className = 'h-3 w-3' /> </ AlertDialogCancel >
128
+ < AlertDialogCancel > < Cross1Icon className = 'h-3 w-3' /> </ AlertDialogCancel >
104
129
</ div >
105
- < AlertDialogDescription >
106
- < AlertDialogTitle > </ AlertDialogTitle >
107
- < div className = "grid gap-6 sm:w-80" >
108
- < div >
109
- < Input
110
- id = "newProjectTitle"
111
- name = "title"
112
- value = { newProject . title }
113
- onChange = { handleProjectChange }
114
- disabled = { isLoading }
115
- placeholder = "Project title"
116
- className = "mt-2"
117
- />
118
- </ div >
119
- < div >
120
- < Textarea
121
- id = "newProjectDescription"
122
- name = "description"
123
- value = { newProject . description }
124
- onChange = { handleProjectChange }
125
- disabled = { isLoading }
126
- placeholder = "# Project description"
127
- className = ""
128
- />
129
- </ div >
130
- < div >
131
- < TagInput
132
- selectedTags = { newProject . tags }
133
- onTagsChange = { ( tags ) => setNewProject ( { ...newProject , tags } ) }
134
- />
135
- </ div >
136
- < div >
137
-
138
- < Input
139
- id = "newProjectRepoLink"
140
- name = "repoLink"
141
- value = { newProject . repoLink }
142
- onChange = { handleProjectChange }
143
- disabled = { isLoading }
144
- placeholder = "Repository link"
145
- className = ""
146
- />
147
- </ div >
148
- < Button onClick = { handleAddProject } disabled = { isLoading } className = "w-full mt-4" >
149
- { isLoading ? (
150
- < >
151
- < Icons . spinner className = "mr-2 h-4 w-4 animate-spin" />
152
- Adding...
153
- </ >
154
- ) : (
155
- 'Add Project'
156
- ) }
157
- </ Button >
158
- </ div >
130
+ < AlertDialogDescription >
131
+ < div className = "grid gap-6 sm:w-80" >
132
+ < div >
133
+ < UploadComponent onFileChange = { setImageFile } />
134
+ </ div >
135
+ < div >
136
+ < Input
137
+ id = "newProjectTitle"
138
+ name = "title"
139
+ value = { newProject . title }
140
+ onChange = { handleProjectChange }
141
+ disabled = { isLoading }
142
+ placeholder = "Project title"
143
+ className = "mt-2"
144
+ />
145
+ </ div >
146
+ < div >
147
+ < Textarea
148
+ id = "newProjectDescription"
149
+ name = "description"
150
+ value = { newProject . description }
151
+ onChange = { handleProjectChange }
152
+ disabled = { isLoading }
153
+ placeholder = "# Project description"
154
+ className = ""
155
+ />
156
+ </ div >
157
+ < div >
158
+ < TagInput
159
+ selectedTags = { newProject . tags }
160
+ onTagsChange = { ( tags ) => setNewProject ( { ...newProject , tags } ) }
161
+ />
162
+ </ div >
163
+ < div >
164
+ < Input
165
+ id = "newProjectRepoLink"
166
+ name = "repoLink"
167
+ value = { newProject . repoLink }
168
+ onChange = { handleProjectChange }
169
+ disabled = { isLoading }
170
+ placeholder = "Repository link"
171
+ className = ""
172
+ />
173
+ </ div >
159
174
160
- </ AlertDialogDescription >
175
+ < Button onClick = { handleAddProject } disabled = { isLoading } className = "w-full mt-4" >
176
+ { isLoading ? (
177
+ < >
178
+ < Icons . spinner className = "mr-2 h-4 w-4 animate-spin" />
179
+ Adding...
180
+ </ >
181
+ ) : (
182
+ 'Add Project'
183
+ ) }
184
+ </ Button >
185
+ </ div >
186
+ </ AlertDialogDescription >
161
187
</ AlertDialogContent >
162
188
</ AlertDialog >
163
189
) ;
0 commit comments