Skip to content

Commit 103e23e

Browse files
committed
added the drop course section on the account Page
1 parent 9bd2e23 commit 103e23e

File tree

2 files changed

+149
-6
lines changed

2 files changed

+149
-6
lines changed
Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,67 @@
1-
.container {
1+
.userPageLayout {
22
display: flex;
3-
justify-content: center;
3+
justify-content: space-between;
4+
gap: 2rem;
5+
padding: 2rem;
6+
flex-wrap: wrap;
7+
}
8+
9+
.userFormSection {
10+
flex: 1;
11+
min-width: 300px;
12+
}
13+
14+
.dropCoursesSection {
15+
flex: 1;
16+
max-width: 500px;
17+
max-height: 300px;
18+
background: #f4f4f4;
19+
padding: 2.7rem;
20+
border-radius: 8px;
21+
overflow-y: auto;
22+
23+
h2 {
24+
margin-bottom: 1rem;
25+
}
26+
27+
scrollbar-width: thin;
28+
29+
&::-webkit-scrollbar {
30+
width: 6px;
31+
}
32+
33+
&::-webkit-scrollbar-thumb {
34+
background-color: #aaa;
35+
border-radius: 3px;
36+
}
37+
}
38+
39+
.courseList {
40+
list-style: none;
41+
padding: 0;
42+
margin: 0;
43+
}
44+
45+
.courseItem {
46+
display: flex;
47+
justify-content: space-between;
48+
align-items: center;
49+
padding: 0.75rem 1rem;
50+
border-bottom: 1px solid #ddd;
51+
color: #222;
52+
font-size: 1rem;
53+
}
54+
55+
.courseTitle {
56+
flex: 1;
57+
word-break: break-word;
58+
}
59+
60+
.trashIcon {
61+
cursor: pointer;
62+
font-size: 1.2rem;
63+
64+
&:hover {
65+
color: red;
66+
}
467
}

devU-client/src/components/pages/users/userDetailPage.tsx

Lines changed: 84 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import React, { useState, useEffect } from 'react'
22
import { useParams } from 'react-router-dom'
33

4-
import { User } from 'devu-shared-modules'
5-
4+
import { Course, User } from 'devu-shared-modules'
65
import { useActionless } from 'redux/hooks'
76
import { UPDATE_USER } from 'redux/types/user.types'
7+
import { SET_ALERT } from 'redux/types/active.types'
88

99
import RequestService from 'services/request.service'
1010

@@ -20,26 +20,106 @@ type UrlParams = {
2020
}
2121

2222
const 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

Comments
 (0)