11import React , { useState , useEffect } from 'react'
22import { useParams } from 'react-router-dom'
33
4- import { User } from 'devu-shared-modules'
5-
4+ import { Course , User } from 'devu-shared-modules'
65import { useActionless } from 'redux/hooks'
76import { UPDATE_USER } from 'redux/types/user.types'
7+ import { SET_ALERT } from 'redux/types/active.types'
88
99import RequestService from 'services/request.service'
1010
@@ -20,26 +20,106 @@ type UrlParams = {
2020}
2121
2222const UserDetailPage = ( { } ) => {
23- const { userId } = useParams ( ) as UrlParams
23+ const { userId } = useParams < UrlParams > ( )
2424 const [ updateUser ] = useActionless ( UPDATE_USER )
25+ const [ setAlert ] = useActionless ( SET_ALERT )
2526
2627 const [ loading , setLoading ] = useState ( true )
2728 const [ user , setUser ] = useState ( { } as User )
2829 const [ error , setError ] = useState ( null )
30+ const [ courses , setCourses ] = useState < Course [ ] > ( [ ] )
2931
3032 useEffect ( ( ) => {
33+ // Fetch user info
3134 RequestService . get < User > ( `/api/users/${ userId } ` )
3235 . then ( setUser )
3336 . catch ( setError )
37+
38+ // Get activeCourses first
39+ RequestService . get < {
40+ instructorCourses : { id : number } [ ] ;
41+ activeCourses : { id : number } [ ] ;
42+ pastCourses : { id : number } [ ] ;
43+ upcomingCourses : { id : number } [ ] ;
44+ } > ( `/api/courses/user/${ userId } ` )
45+ . then ( async ( data ) => {
46+ const activeCourses = data . activeCourses
47+
48+ const courseFetches = await Promise . all (
49+ activeCourses . map ( async ( c ) => {
50+ try {
51+ const courseDetails = await RequestService . get < Course > ( `/api/courses/${ c . id } ` )
52+ return courseDetails
53+ } catch {
54+ return {
55+ id : c . id ,
56+ name : '[No title]' ,
57+ semester : '' ,
58+ number : '' ,
59+ startDate : '' ,
60+ endDate : ''
61+ } as Course
62+ }
63+ } )
64+ )
65+
66+ setCourses ( courseFetches )
67+ } )
68+ . catch ( ( err ) => {
69+ setAlert ( { autoDelete : false , type : 'error' , message : 'Failed to load courses' } )
70+ console . error ( 'Course fetch error:' , err )
71+ } )
3472 . finally ( ( ) => setLoading ( false ) )
3573 } , [ ] )
3674
75+ const handleDropCourse = ( courseId : number ) => {
76+ const confirmDrop = window . confirm ( "Are you sure you want to drop?" ) ;
77+ if ( confirmDrop ) {
78+ RequestService . delete ( `/api/course/${ courseId } /user-courses` )
79+ . then ( ( ) => {
80+ setAlert ( { autoDelete : true , type : 'success' , message : 'Course Dropped' } )
81+ setCourses ( ( prev ) => prev . filter ( ( c ) => c . id !== courseId ) )
82+ } )
83+ . catch ( ( error : Error ) => {
84+ setAlert ( { autoDelete : false , type : 'error' , message : error . message } )
85+ } ) ;
86+ }
87+ }
88+
3789 if ( loading ) return < LoadingOverlay delay = { 250 } />
3890 if ( error ) return < ErrorPage error = { error } />
3991
4092 return (
4193 < PageWrapper className = { styles . container } >
42- < EditUserForm user = { user } onSubmit = { updateUser } />
94+ < div className = { styles . userPageLayout } >
95+ < div className = { styles . userFormSection } >
96+ < EditUserForm user = { user } onSubmit = { updateUser } />
97+ </ div >
98+
99+ < div className = { styles . dropCoursesSection } >
100+ < h2 > Drop Course</ h2 >
101+ < ul className = { styles . courseList } >
102+ { courses . length > 0 ? (
103+ courses . map ( ( course ) => (
104+ < li key = { course . id } className = { styles . courseItem } >
105+ < span className = { styles . courseTitle } >
106+ { course . name } { course . number && `(${ course . number } )` }
107+ </ span >
108+ < span
109+ className = { styles . trashIcon }
110+ onClick = { ( ) => handleDropCourse ( course . id ! ) }
111+ title = "Drop Course"
112+ >
113+ 🗑️
114+ </ span >
115+ </ li >
116+ ) )
117+ ) : (
118+ < p > No enrolled courses.</ p >
119+ ) }
120+ </ ul >
121+ </ div >
122+ </ div >
43123 </ PageWrapper >
44124 )
45125}
0 commit comments