11'use client' ;
22
33import { api } from '@/api/api' ;
4+ import { CDN_URL } from '@/constants/constants' ;
45import { authAtom } from '@/store/auth' ;
56import type { IActivity } from '@/types/IActivity' ;
67import type { IClassroom } from '@/types/IClassroomCard' ;
8+ import type { ISubmission } from '@/types/ISubmission' ;
9+ import type { IUser } from '@/types/IUser' ;
710import {
11+ Avatar ,
812 Badge ,
913 Box ,
1014 Button ,
@@ -55,6 +59,9 @@ export default function ActivityScreen({
5559 const [ activity , setActivity ] = useState < IActivity | null > ( null ) ;
5660 const [ classroom , setClassroom ] = useState < IClassroom | null > ( null ) ;
5761 const [ isLoading , setIsLoading ] = useState ( true ) ;
62+ const [ submissions , setSubmissions ] = useState < ( ISubmission & { user : IUser } ) [ ] > ( [ ] ) ;
63+ const [ userSubmission , setUserSubmission ] = useState < ISubmission | null > ( null ) ;
64+ const [ isLoadingSubmissions , setIsLoadingSubmissions ] = useState ( false ) ;
5865 const [ files , setFiles ] = useState < File [ ] > ( [ ] ) ;
5966 const [ comment , setComment ] = useState ( '' ) ;
6067 const [ isSubmitting , setIsSubmitting ] = useState ( false ) ;
@@ -119,6 +126,29 @@ export default function ActivityScreen({
119126
120127 setActivity ( activityData ) ;
121128 setClassroom ( classroomData ) ;
129+
130+ if ( classroomData . owner === auth . user ?. id && activityData . type === 'assignment' ) {
131+ setIsLoadingSubmissions ( true ) ;
132+ try {
133+ const submissionsData = await api . activities . getAllSubmissions ( classroomId , activityId ) ;
134+ setSubmissions ( submissionsData ) ;
135+ } catch ( error ) {
136+ console . error ( 'Failed to fetch submissions:' , error ) ;
137+ } finally {
138+ setIsLoadingSubmissions ( false ) ;
139+ }
140+ } else if ( activityData . type === 'assignment' ) {
141+ setIsLoadingSubmissions ( true ) ;
142+ try {
143+ const submission = await api . activities . getUserSubmission ( classroomId , activityId ) ;
144+ setUserSubmission ( submission ) ;
145+ setHasSubmitted ( true ) ;
146+ } catch ( error ) {
147+ console . error ( 'Failed to fetch user submission:' , error ) ;
148+ } finally {
149+ setIsLoadingSubmissions ( false ) ;
150+ }
151+ }
122152 } catch {
123153 toast ( {
124154 title : 'Error' ,
@@ -135,7 +165,7 @@ export default function ActivityScreen({
135165 } ;
136166
137167 fetchData ( ) ;
138- } , [ activityId , classroomId , toast ] ) ;
168+ } , [ activityId , classroomId , toast , auth . user ?. id ] ) ;
139169
140170 const handleSubmit = async ( ) => {
141171 if ( files . length === 0 ) {
@@ -151,20 +181,45 @@ export default function ActivityScreen({
151181 }
152182
153183 setIsSubmitting ( true ) ;
184+ try {
185+ for ( const file of files ) {
186+ const { url } = await api . activities . getSubmissionUrl ( classroomId , activityId , file . name , comment ) ;
154187
155- await new Promise ( ( resolve ) => setTimeout ( resolve , 1000 ) ) ;
188+ const uploadRes = await fetch ( url , {
189+ method : 'PUT' ,
190+ body : file ,
191+ headers : {
192+ 'Content-Type' : file . type
193+ }
194+ } ) ;
156195
157- toast ( {
158- title : 'Éxito' ,
159- description : 'Tu tarea ha sido enviada correctamente' ,
160- status : 'success' ,
161- position : 'top-right' ,
162- duration : 3000 ,
163- isClosable : true
164- } ) ;
196+ if ( ! uploadRes . ok ) {
197+ throw new Error ( `Failed to upload file ${ file . name } ` ) ;
198+ }
199+ }
165200
166- setHasSubmitted ( true ) ;
167- setIsSubmitting ( false ) ;
201+ toast ( {
202+ title : 'Éxito' ,
203+ description : 'Tu tarea ha sido enviada correctamente' ,
204+ status : 'success' ,
205+ position : 'top-right' ,
206+ duration : 3000 ,
207+ isClosable : true
208+ } ) ;
209+
210+ setHasSubmitted ( true ) ;
211+ } catch ( error ) {
212+ toast ( {
213+ title : 'Error' ,
214+ description : error instanceof Error ? error . message : 'Ha ocurrido un error al enviar la tarea' ,
215+ status : 'error' ,
216+ position : 'top-right' ,
217+ duration : 3000 ,
218+ isClosable : true
219+ } ) ;
220+ } finally {
221+ setIsSubmitting ( false ) ;
222+ }
168223 } ;
169224
170225 if ( isLoading ) {
@@ -368,6 +423,92 @@ export default function ActivityScreen({
368423 </ Box >
369424 ) }
370425
426+ { isProfessor && isAssignment && (
427+ < Box
428+ bg = 'brand.dark.900'
429+ p = { 6 }
430+ borderRadius = 'xl'
431+ border = '1px solid'
432+ borderColor = 'brand.dark.800'
433+ >
434+ < Heading size = 'md' mb = { 4 } >
435+ Entregas de Estudiantes
436+ </ Heading >
437+ { isLoadingSubmissions ? (
438+ < Flex justify = 'center' py = { 4 } >
439+ < Spinner />
440+ </ Flex >
441+ ) : submissions . length > 0 ? (
442+ < VStack spacing = { 4 } align = 'stretch' >
443+ { submissions . map ( ( submission ) => (
444+ < Box key = { submission . id } p = { 4 } bg = 'brand.dark.800' borderRadius = 'lg' >
445+ < Flex justify = 'space-between' align = 'center' mb = { 3 } >
446+ < Flex align = 'center' gap = { 3 } >
447+ < Avatar
448+ size = 'sm'
449+ name = { submission . user ?. username }
450+ src = {
451+ submission . user ?. avatar
452+ ? `${ CDN_URL } /avatars/${ submission . user ?. id } /${ submission . user ?. avatar } .png`
453+ : ''
454+ }
455+ />
456+ < VStack align = 'start' spacing = { 0 } >
457+ < Text fontWeight = 'bold' >
458+ { submission . user ?. username }
459+ </ Text >
460+ < Text fontSize = 'sm' color = 'gray.400' >
461+ Entregado el{ ' ' }
462+ { format (
463+ new Date ( submission . submittedAt ) ,
464+ "d 'de' MMMM 'a las' HH:mm" ,
465+ { locale : es }
466+ ) }
467+ </ Text >
468+ </ VStack >
469+ </ Flex >
470+ </ Flex >
471+ < VStack align = 'stretch' spacing = { 2 } >
472+ { submission . comment && (
473+ < Text >
474+ < b > Comentario</ b > : { submission . comment }
475+ </ Text >
476+ ) }
477+ { submission . files . map ( ( file , index ) => (
478+ < Flex
479+ key = { index }
480+ justify = 'space-between'
481+ align = 'center'
482+ p = { 2 }
483+ bg = 'brand.dark.900'
484+ borderRadius = 'md'
485+ >
486+ < Text fontSize = 'sm' > { file . name } </ Text >
487+ < Link
488+ href = { `${ CDN_URL } /submissions/${ file . url } ` }
489+ isExternal
490+ _hover = { { textDecoration : 'none' } }
491+ >
492+ < Button
493+ size = 'sm'
494+ variant = 'ghost'
495+ leftIcon = { < FiDownload /> }
496+ >
497+ Descargar
498+ </ Button >
499+ </ Link >
500+ </ Flex >
501+ ) ) }
502+ </ VStack >
503+ </ Box >
504+ ) ) }
505+ </ VStack >
506+ ) : (
507+ < Text color = 'gray.400' > Aún no hay entregas para esta tarea.</ Text >
508+ ) }
509+ </ Box >
510+ ) }
511+
371512 { ! isProfessor && isAssignment && (
372513 < Box
373514 bg = 'brand.dark.900'
@@ -379,10 +520,68 @@ export default function ActivityScreen({
379520 < Heading size = 'md' mb = { 4 } >
380521 Enviar Tarea
381522 </ Heading >
382- { hasSubmitted ? (
383- < Text color = 'green.400' >
384- ¡Tu tarea ha sido enviada! El profesor la revisará pronto.
385- </ Text >
523+ { isLoadingSubmissions ? (
524+ < Flex justify = 'center' py = { 4 } >
525+ < Spinner />
526+ </ Flex >
527+ ) : hasSubmitted ? (
528+ < VStack spacing = { 4 } align = 'stretch' >
529+ < Text color = 'green.400' mb = { 2 } >
530+ ¡Tu tarea ha sido enviada! El profesor la revisará pronto.
531+ </ Text >
532+ { userSubmission && (
533+ < Box >
534+ { userSubmission . comment && (
535+ < Text fontWeight = 'semibold' color = 'gray.100' >
536+ Comentario:
537+ </ Text >
538+ ) }
539+ { userSubmission . comment && (
540+ < Text mb = { 4 } > { userSubmission . comment } </ Text >
541+ ) }
542+ < Text fontWeight = 'semibold' color = 'gray.1s00' mb = { 2 } >
543+ Archivos enviados:
544+ </ Text >
545+ < VStack spacing = { 2 } align = 'stretch' >
546+ { userSubmission . files . map ( ( file , index ) => (
547+ < Flex
548+ key = { index }
549+ justify = 'space-between'
550+ align = 'center'
551+ p = { 2 }
552+ bg = 'brand.dark.800'
553+ borderRadius = 'md'
554+ >
555+ < Text color = 'gray.300' fontSize = 'sm' >
556+ { file . name }
557+ </ Text >
558+ < Link
559+ href = { `${ CDN_URL } /submissions/${ file . url } ` }
560+ isExternal
561+ _hover = { { textDecoration : 'none' } }
562+ >
563+ < Button
564+ size = 'sm'
565+ variant = 'ghost'
566+ leftIcon = { < FiDownload /> }
567+ >
568+ Descargar
569+ </ Button >
570+ </ Link >
571+ </ Flex >
572+ ) ) }
573+ </ VStack >
574+ < Text fontSize = 'sm' color = 'gray.400' mt = { 2 } >
575+ Enviado el{ ' ' }
576+ { format (
577+ new Date ( userSubmission . submittedAt ) ,
578+ "d 'de' MMMM 'a las' HH:mm" ,
579+ { locale : es }
580+ ) }
581+ </ Text >
582+ </ Box >
583+ ) }
584+ </ VStack >
386585 ) : (
387586 < VStack spacing = { 4 } align = 'stretch' >
388587 < Box
0 commit comments