@@ -45,6 +45,8 @@ export default function JuradoProyectosPage() {
4545 const [ confirmAction , setConfirmAction ] = useState < "review" | null > ( null )
4646 const [ judgingStage , setJudgingStage ] = useState < "admin" | "judge" > ( "admin" )
4747 const [ myReviews , setMyReviews ] = useState < Set < string > > ( new Set ( ) )
48+ const [ myReviewByProject , setMyReviewByProject ] = useState < Record < string , any > > ( { } )
49+ const [ myReviewDocIdByProject , setMyReviewDocIdByProject ] = useState < Record < string , string > > ( { } )
4850
4951 useEffect ( ( ) => {
5052 if ( ! db ) return
@@ -79,6 +81,18 @@ export default function JuradoProyectosPage() {
7981 unsubReviews = onSnapshot ( reviewsQuery , ( snapshot ) => {
8082 const reviewedIds = new Set ( snapshot . docs . map ( doc => doc . data ( ) . projectId ) )
8183 setMyReviews ( reviewedIds )
84+
85+ const reviewsByProject : Record < string , any > = { }
86+ const reviewDocIdsByProject : Record < string , string > = { }
87+ snapshot . docs . forEach ( ( d ) => {
88+ const data = d . data ( ) as any
89+ if ( data . projectId ) {
90+ reviewsByProject [ data . projectId ] = data
91+ reviewDocIdsByProject [ data . projectId ] = d . id
92+ }
93+ } )
94+ setMyReviewByProject ( reviewsByProject )
95+ setMyReviewDocIdByProject ( reviewDocIdsByProject )
8296 } )
8397 }
8498
@@ -106,10 +120,31 @@ export default function JuradoProyectosPage() {
106120 }
107121 } , [ db , user ?. id ] )
108122
123+ useEffect ( ( ) => {
124+ if ( ! selectedProject ) return
125+
126+ const defaultScores : Record < string , number > = { }
127+ scoringCriteria . filter ( c => ( c . targetRole || "judge" ) === "judge" ) . forEach ( c => {
128+ defaultScores [ c . id ] = 10
129+ } )
130+
131+ const existingReview = myReviewByProject [ selectedProject . id ]
132+ if ( existingReview ) {
133+ const prefilledScores = { ...defaultScores , ...( existingReview . rawScores || { } ) }
134+ setReviewScores ( prefilledScores )
135+ setReviewComment ( existingReview . comment || "" )
136+ return
137+ }
138+
139+ setReviewScores ( defaultScores )
140+ setReviewComment ( "" )
141+ } , [ selectedProject ?. id , scoringCriteria , myReviewByProject ] )
142+
109143 const handleReview = async ( ) => {
110144 if ( ! db || ! selectedProject || ! user ) return
111145 setSubmittingReview ( true )
112146 try {
147+ const nowIso = new Date ( ) . toISOString ( )
113148 const scoresWithWeights : Record < string , number > = { }
114149 let totalWeightedScore = 0
115150
@@ -132,9 +167,17 @@ export default function JuradoProyectosPage() {
132167 totalScore : totalWeightedScore ,
133168 comment : reviewComment ,
134169 disqualified : false ,
135- createdAt : new Date ( ) . toISOString ( )
170+ updatedAt : nowIso ,
171+ }
172+ const existingReviewDocId = myReviewDocIdByProject [ selectedProject . id ]
173+ if ( existingReviewDocId ) {
174+ await updateDoc ( doc ( db , "projectReviews" , existingReviewDocId ) , reviewData )
175+ } else {
176+ await addDoc ( collection ( db , "projectReviews" ) , {
177+ ...reviewData ,
178+ createdAt : nowIso ,
179+ } )
136180 }
137- await addDoc ( collection ( db , "projectReviews" ) , reviewData )
138181
139182 // Calculate new averages from all relevant reviews (judges and admins)
140183 const reviewsSnap = await getDocs ( query ( collection ( db , "projectReviews" ) , where ( "projectId" , "==" , selectedProject . id ) ) )
@@ -177,7 +220,7 @@ export default function JuradoProyectosPage() {
177220 setSelectedProject ( ( prev : any ) => ( { ...prev , status : "reviewed" , disqualified : anyDisqualified , totalScore : avgTotal , reviewCount : judgeReviews . length } ) )
178221 setShowDetails ( false )
179222 setReviewComment ( "" )
180- toast ( { title : locale === "es" ? "Evaluación enviada" : "Evaluation submitted" } )
223+ toast ( { title : existingReviewDocId ? ( locale === "es" ? "Evaluación actualizada" : "Evaluation updated" ) : ( locale === "es" ? "Evaluación enviada" : "Evaluation submitted" ) } )
181224 } catch ( error ) {
182225 console . error ( "Error submitting review:" , error )
183226 } finally {
@@ -186,12 +229,16 @@ export default function JuradoProyectosPage() {
186229 }
187230
188231 const filteredProjects = useMemo ( ( ) => {
189- // Judges don't see anything during admin screening phase
190- if ( judgingStage === "admin" ) return [ ]
232+ const hasFinalists = projects . some ( p => p . isFinalist && ! p . disqualified )
191233
192234 return projects . filter ( p => {
193- // Judges only evaluate finalists and never disqualified ones
194- if ( ! p . isFinalist || p . disqualified ) return false
235+ // Prefer finalists once they exist; otherwise fallback to submitted/reviewed projects.
236+ if ( p . disqualified ) return false
237+ if ( hasFinalists ) {
238+ if ( ! p . isFinalist ) return false
239+ } else {
240+ if ( p . status !== "submitted" && p . status !== "reviewed" ) return false
241+ }
195242
196243 const isReviewedByMe = myReviews . has ( p . id )
197244 const matchesSearch = p . title ?. toLowerCase ( ) . includes ( searchTerm . toLowerCase ( ) ) ||
@@ -203,7 +250,7 @@ export default function JuradoProyectosPage() {
203250
204251 return matchesSearch && statusMatch
205252 } )
206- } , [ projects , searchTerm , statusFilter , judgingStage , myReviews ] )
253+ } , [ projects , searchTerm , statusFilter , myReviews ] )
207254
208255 const getCategoryName = ( categoryId : string ) => {
209256 if ( ! categoryId ) return "-"
@@ -244,9 +291,9 @@ export default function JuradoProyectosPage() {
244291 < section >
245292 < div className = "flex flex-col gap-2 mb-6" >
246293 < h3 className = "font-pixel text-2xl text-brand-yellow font-pixel" > { t . judge . projectsToScore } </ h3 >
247- { judgingStage === "admin" && (
294+ { judgingStage === "admin" && ! projects . some ( p => p . isFinalist && ! p . disqualified ) && (
248295 < div className = "p-3 text-center rounded-md bg-brand-orange/10 border border-brand-orange/30 text-brand-orange text-xs font-pixel" >
249- WAITING FOR ADMINS TO SELECT FINALISTS.. .
296+ NO FINALISTS YET. SHOWING SUBMITTED PROJECTS .
250297 </ div >
251298 ) }
252299 </ div >
@@ -288,27 +335,30 @@ export default function JuradoProyectosPage() {
288335 </ div >
289336
290337 < Dialog open = { showDetails } onOpenChange = { setShowDetails } >
291- < DialogContent className = "glass-effect border-brand-cyan/30 max-w-2xl max-h-[90vh] overflow-y-auto" >
338+ < DialogContent className = "glass-effect border-brand-cyan/30 w-[95vw] max-w-2xl max-h-[90vh] overflow-y-auto overflow-x-hidden " >
292339 < DialogHeader >
293- < DialogTitle className = "font-pixel text-brand-yellow flex items-center justify-between" >
294- < div className = "flex items-center gap-2" > < FileText size = { 18 } /> { selectedProject ?. title } </ div >
340+ < DialogTitle className = "font-pixel text-brand-yellow flex items-start justify-between gap-2" >
341+ < div className = "flex min-w-0 items-start gap-2" >
342+ < FileText size = { 18 } className = "shrink-0" />
343+ < span className = "break-all leading-snug" > { selectedProject ?. title } </ span >
344+ </ div >
295345 </ DialogTitle >
296346 </ DialogHeader >
297347 { selectedProject && (
298348 < div className = "space-y-6 mt-4" >
299349 < div className = "grid grid-cols-1 md:grid-cols-2 gap-4" >
300350 < div className = "p-3 rounded bg-brand-navy/60 border border-brand-cyan/10" >
301351 < p className = "text-[10px] text-brand-cyan/60 uppercase mb-1" > Team</ p >
302- < p className = "text-brand-yellow text-sm font-pixel" > { selectedProject . teamName } </ p >
352+ < p className = "text-brand-yellow text-sm font-pixel break-all " > { selectedProject . teamName } </ p >
303353 </ div >
304354 < div className = "p-3 rounded bg-brand-navy/60 border border-brand-cyan/10" >
305355 < p className = "text-[10px] text-brand-cyan/60 uppercase mb-1" > Category</ p >
306- < p className = "text-brand-cyan text-sm" > { getCategoryName ( selectedProject . categoryId ) } </ p >
356+ < p className = "text-brand-cyan text-sm break-words " > { getCategoryName ( selectedProject . categoryId ) } </ p >
307357 </ div >
308358 </ div >
309359 < div className = "p-4 rounded bg-black/40 border border-brand-cyan/10" >
310360 < p className = "text-[10px] text-brand-cyan/60 uppercase mb-2" > Description</ p >
311- < p className = "text-brand-cyan/80 text-sm whitespace-pre-wrap" > { selectedProject . description } </ p >
361+ < p className = "text-brand-cyan/80 text-sm whitespace-pre-wrap break-all " > { selectedProject . description } </ p >
312362 </ div >
313363 < div className = "flex flex-wrap gap-4" >
314364 { selectedProject . githubRepoUrl && < a href = { selectedProject . githubRepoUrl . startsWith ( 'http' ) ? selectedProject . githubRepoUrl : `https://${ selectedProject . githubRepoUrl } ` } target = "_blank" rel = "noreferrer" className = "flex items-center gap-2 px-3 py-2 rounded bg-brand-cyan/10 text-brand-cyan hover:bg-brand-cyan/20 text-xs" > < Github size = { 14 } /> GitHub</ a > }
@@ -333,9 +383,13 @@ export default function JuradoProyectosPage() {
333383 </ div >
334384 ) }
335385
336- { ! myReviews . has ( selectedProject . id ) && selectedProject . status !== "disqualified" ? (
386+ { selectedProject . status !== "disqualified" ? (
337387 < div className = "mt-8 pt-6 border-t border-brand-cyan/20" >
338- < h3 className = "font-pixel text-lg text-brand-yellow mb-4" > Project Review</ h3 >
388+ < h3 className = "font-pixel text-lg text-brand-yellow mb-4" >
389+ { myReviews . has ( selectedProject . id )
390+ ? ( locale === "es" ? "Editar Evaluación" : "Edit Evaluation" )
391+ : "Project Review" }
392+ </ h3 >
339393 < div className = "space-y-4" >
340394 { scoringCriteria . filter ( c => ( c . targetRole || "judge" ) === "judge" ) . map ( criteria => (
341395 < div key = { criteria . id } className = "grid grid-cols-4 items-center gap-4" >
@@ -374,7 +428,9 @@ export default function JuradoProyectosPage() {
374428 disabled = { submittingReview }
375429 >
376430 < CheckCircle className = "mr-2 w-4 h-4" />
377- Submit Evaluation
431+ { myReviews . has ( selectedProject . id )
432+ ? ( locale === "es" ? "Actualizar Evaluación" : "Update Evaluation" )
433+ : "Submit Evaluation" }
378434 </ Button >
379435 </ div >
380436 </ div >
@@ -383,14 +439,7 @@ export default function JuradoProyectosPage() {
383439 < div className = "mt-8 pt-6 border-t border-red-500/20 text-center" >
384440 < p className = "text-red-500 font-pixel text-sm uppercase" > This project has been disqualified.</ p >
385441 </ div >
386- ) : (
387- < div className = "mt-8 pt-6 border-t border-brand-cyan/20 text-center" >
388- < div className = "flex flex-col items-center gap-2 text-brand-cyan/60" >
389- < CheckCircle className = "w-8 h-8 text-green-400" />
390- < p className = "font-pixel text-sm uppercase" > { locale === "es" ? "Ya has evaluado este proyecto" : "You have already evaluated this project" } </ p >
391- </ div >
392- </ div >
393- ) }
442+ ) : null }
394443 </ div >
395444 ) }
396445 </ DialogContent >
0 commit comments