@@ -54,19 +54,63 @@ const RequestPage = () => {
5454 const [ showDiscordDialog , setShowDiscordDialog ] = useState ( false ) ;
5555 const [ showAuthDialog , setShowAuthDialog ] = useState ( false ) ;
5656
57+ const validateForm = ( ) => {
58+ // Title validation
59+ if ( ! projectRequest . title . trim ( ) ) {
60+ return { isValid : false , message : 'Please enter a project title' } ;
61+ }
62+ if ( projectRequest . title . length < 3 ) {
63+ return { isValid : false , message : 'Title must be at least 3 characters long' } ;
64+ }
65+ if ( projectRequest . title . length > 100 ) {
66+ return { isValid : false , message : 'Title must be less than 100 characters' } ;
67+ }
68+
69+ // GitHub URL validation
70+ if ( ! projectRequest . githubLink . trim ( ) ) {
71+ return { isValid : false , message : 'Please enter a GitHub repository URL' } ;
72+ }
73+ const githubRegex = / ^ h t t p s : \/ \/ g i t h u b \. c o m \/ [ a - z A - Z 0 - 9 . _ - ] + \/ [ a - z A - Z 0 - 9 . _ - ] + \/ ? $ / ;
74+ if ( ! githubRegex . test ( projectRequest . githubLink ) ) {
75+ return { isValid : false , message : 'Please enter a valid GitHub repository URL (e.g., https://github.com/owner/repo)' } ;
76+ }
77+
78+ // Description validation
79+ if ( ! projectRequest . description . trim ( ) ) {
80+ return { isValid : false , message : 'Please describe what this project is about' } ;
81+ }
82+ if ( projectRequest . description . length < 10 ) {
83+ return { isValid : false , message : 'Description must be at least 10 characters long' } ;
84+ }
85+ if ( projectRequest . description . length > 1000 ) {
86+ return { isValid : false , message : 'Description must be less than 1000 characters' } ;
87+ }
88+
89+ // Reason validation
90+ if ( ! projectRequest . reason . trim ( ) ) {
91+ return { isValid : false , message : 'Please explain why you think this is a good project' } ;
92+ }
93+ if ( projectRequest . reason . length < 10 ) {
94+ return { isValid : false , message : 'Reason must be at least 10 characters long' } ;
95+ }
96+ if ( projectRequest . reason . length > 1000 ) {
97+ return { isValid : false , message : 'Reason must be less than 1000 characters' } ;
98+ }
99+
100+ return { isValid : true , message : '' } ;
101+ } ;
102+
57103 const handleProjectRequest = async ( ) => {
58104 if ( ! isAuthenticated || ! user ) {
59105 setShowAuthDialog ( true ) ;
60106 return ;
61107 }
62108
63- if ( ! projectRequest . title . trim ( ) ||
64- ! projectRequest . githubLink . trim ( ) ||
65- ! projectRequest . description . trim ( ) ||
66- ! projectRequest . reason . trim ( ) ) {
109+ const validation = validateForm ( ) ;
110+ if ( ! validation . isValid ) {
67111 setSubmissionStatus ( {
68112 status : 'error' ,
69- message : 'Please fill in all fields before submitting'
113+ message : validation . message
70114 } ) ;
71115 return ;
72116 }
@@ -109,7 +153,12 @@ const handleFinalSubmit = async () => {
109153 const responseData = await response . json ( ) ;
110154
111155 if ( ! response . ok ) {
112- throw new Error ( responseData . error || "Failed to submit request" ) ;
156+ // Display the specific error message from the API
157+ setSubmissionStatus ( {
158+ status : "error" ,
159+ message : responseData . error || "Failed to submit request"
160+ } ) ;
161+ return ;
113162 }
114163
115164 setSubmissionStatus ( {
@@ -259,36 +308,59 @@ const handleFinalSubmit = async () => {
259308 </ CardHeader >
260309 < CardContent className = "space-y-4" >
261310 < div className = "space-y-4" >
262- < Input
263- placeholder = "Project Title"
264- value = { projectRequest . title }
265- onChange = { ( e ) => setProjectRequest ( prev => ( { ...prev , title : e . target . value } ) ) }
266- className = "bg-slate-800/50 border-slate-700 text-white placeholder:text-gray-400"
267- />
268-
269- < div className = "flex items-center gap-2" >
270- < Github className = "text-white shrink-0" />
311+ < div >
271312 < Input
272- placeholder = "GitHub Link "
273- value = { projectRequest . githubLink }
274- onChange = { ( e ) => setProjectRequest ( prev => ( { ...prev , githubLink : e . target . value } ) ) }
313+ placeholder = "Project Title "
314+ value = { projectRequest . title }
315+ onChange = { ( e ) => setProjectRequest ( prev => ( { ...prev , title : e . target . value } ) ) }
275316 className = "bg-slate-800/50 border-slate-700 text-white placeholder:text-gray-400"
317+ maxLength = { 100 }
276318 />
319+ < p className = "text-xs text-gray-400 mt-1" >
320+ { projectRequest . title . length } /100 characters (min. 3)
321+ </ p >
277322 </ div >
278323
279- < Textarea
280- placeholder = "What is this project about?"
281- value = { projectRequest . description }
282- onChange = { ( e ) => setProjectRequest ( prev => ( { ...prev , description : e . target . value } ) ) }
283- className = "bg-slate-800/50 border-slate-700 text-white placeholder:text-gray-400 min-h-24"
284- />
285-
286- < Textarea
287- placeholder = "Why do you think this is a good project?"
288- value = { projectRequest . reason }
289- onChange = { ( e ) => setProjectRequest ( prev => ( { ...prev , reason : e . target . value } ) ) }
290- className = "bg-slate-800/50 border-slate-700 text-white placeholder:text-gray-400 min-h-24"
291- />
324+ < div >
325+ < div className = "flex items-center gap-2" >
326+ < Github className = "text-white shrink-0" />
327+ < Input
328+ placeholder = "GitHub Link (e.g., https://github.com/owner/repo)"
329+ value = { projectRequest . githubLink }
330+ onChange = { ( e ) => setProjectRequest ( prev => ( { ...prev , githubLink : e . target . value } ) ) }
331+ className = "bg-slate-800/50 border-slate-700 text-white placeholder:text-gray-400"
332+ />
333+ </ div >
334+ < p className = "text-xs text-gray-400 mt-1 ml-8" >
335+ Must be a valid GitHub repository URL
336+ </ p >
337+ </ div >
338+
339+ < div >
340+ < Textarea
341+ placeholder = "What is this project about? Describe its purpose and main features."
342+ value = { projectRequest . description }
343+ onChange = { ( e ) => setProjectRequest ( prev => ( { ...prev , description : e . target . value } ) ) }
344+ className = "bg-slate-800/50 border-slate-700 text-white placeholder:text-gray-400 min-h-24"
345+ maxLength = { 1000 }
346+ />
347+ < p className = "text-xs text-gray-400 mt-1" >
348+ { projectRequest . description . length } /1000 characters (min. 10)
349+ </ p >
350+ </ div >
351+
352+ < div >
353+ < Textarea
354+ placeholder = "Why do you think this is a good project? What value does it provide?"
355+ value = { projectRequest . reason }
356+ onChange = { ( e ) => setProjectRequest ( prev => ( { ...prev , reason : e . target . value } ) ) }
357+ className = "bg-slate-800/50 border-slate-700 text-white placeholder:text-gray-400 min-h-24"
358+ maxLength = { 1000 }
359+ />
360+ < p className = "text-xs text-gray-400 mt-1" >
361+ { projectRequest . reason . length } /1000 characters (min. 10)
362+ </ p >
363+ </ div >
292364 </ div >
293365
294366 { submissionStatus . status && (
0 commit comments