Drop Course Component
Overview
The DropCourse component provides a user interface for students to request course drops, view pending drop requests, and check their drop request history. The component handles data fetching, user confirmation, and request submission through a clean and responsive interface.
Component Structure
The component is organized into several logical sections: - Currently Enrolled Courses - Pending Drop Requests - Drop Request History - Information Section
Dependencies
import { useState, useEffect } from "react";
import { Link } from "react-router-dom";
import {
FaRegClock, FaBookOpen, FaExclamationTriangle,
FaCheck, FaTimes, FaHistory
} from "react-icons/fa";
import newRequest from '../../utils/newRequest';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
Helper Components
StatusIcon
A utility component that renders appropriate icons based on request status:
const StatusIcon = ({ status }) => {
switch (status) {
case 'Approved':
return <FaCheck className="text-green-500 mr-2" />;
case 'Rejected':
return <FaTimes className="text-red-500 mr-2" />;
case 'Pending':
return <FaRegClock className="text-yellow-500 mr-2" />;
default:
return null;
}
};
Authentication
The component retrieves the user ID from local storage:
const {data:userData} = JSON.parse(localStorage.getItem("currentUser"));
const {userId} = userData.user;
Data Fetching
Enrolled Courses
Fetches the student's currently enrolled courses:
const {
isLoading: coursesLoading,
error: coursesError,
data: enrolledCourses = []
} = useQuery({
queryKey: ["studentCourses"],
queryFn: () =>
newRequest.get(`/student/${userId}/courses`).then((res) => {
return res.data.courses || [];
}),
});
Drop Requests
Fetches all drop requests (both pending and historical):
const {
isLoading: requestsLoading,
error: requestsError,
data: dropRequests = []
} = useQuery({
queryKey: ["dropRequests"],
queryFn: () =>
newRequest.get(`/student/${userId}/drop-requests`).then((res) => {
return res.data || [];
}),
});
State Management
The component uses React Query for state management and data fetching. Local state is derived from the fetched data:
// Create a map of pending course drop requests for filtering
const pendingDropRequestsMap = new Map();
dropRequests.forEach(request => {
if (request.status === 'Pending') {
pendingDropRequestsMap.set(request.courseId, request);
}
});
// Filter out courses that already have pending drop requests
const availableForDrop = enrolledCourses.filter(course =>
!pendingDropRequestsMap.has(course.id)
);
// Get pending drop requests
const pendingRequests = dropRequests.filter(request => request.status === 'Pending');
// Get non-pending (history) drop requests
const historyRequests = dropRequests.filter(request => request.status !== 'Pending');
Mutations
Create Drop Request
Creates a new course drop request:
const createDropRequest = useMutation({
mutationFn: (courseId) => {
return newRequest.post(`/student/${userId}/drop-requests`, { courseId });
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["dropRequests"] });
queryClient.invalidateQueries({ queryKey: ["studentCourses"] });
},
});
Cancel Drop Request
Cancels a pending drop request:
const cancelDropRequest = useMutation({
mutationFn: (requestId) => {
return newRequest.delete(`/student/${userId}/drop-requests/${requestId}`);
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["dropRequests"] });
queryClient.invalidateQueries({ queryKey: ["studentCourses"] });
},
});
Handler Functions
Request Drop Course
Handles the confirmation and submission of drop requests:
const handleRequestDropCourse = (courseId, courseName) => {
if (window.confirm(`Are you sure you want to request dropping ${courseName}?`)) {
createDropRequest.mutate(courseId, {
onSuccess: () => {
alert("Drop request submitted successfully. You will be notified when it's processed.");
},
onError: (error) => {
alert(error.response?.data?.message || "Failed to submit drop request. Please try again.");
}
});
}
};
Cancel Drop Request
Handles the confirmation and cancellation of pending drop requests:
const handleCancelDropRequest = (requestId, courseName) => {
if (window.confirm(`Are you sure you want to cancel your request to drop ${courseName}?`)) {
cancelDropRequest.mutate(requestId, {
onSuccess: () => {
alert("Drop request cancelled successfully.");
},
onError: (error) => {
alert(error.response?.data?.message || "Failed to cancel drop request. Please try again.");
}
});
}
};
UI Components
Loading State
Displays a loading spinner when data is being fetched:
{isLoading ? (
<div className="bg-gray-100 p-8 rounded-lg text-center">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-pink-500 mx-auto mb-4"></div>
<p className="text-gray-700">Loading your courses...</p>
</div>
) : /* ... */}
Error State
Displays an error message if data fetching fails:
{error ? (
<div className="bg-red-100 p-8 rounded-lg text-center">
<p className="text-red-700 text-lg mb-4">{error.message || "Failed to fetch courses"}</p>
<Link
to="/courses"
className="bg-pink-500 text-white py-2 px-6 rounded-md font-medium hover:bg-pink-600 transition duration-300"
>
Back to My Courses
</Link>
</div>
) : /* ... */}
Enrolled Courses Section
Displays courses that are available for dropping:
{availableForDrop.length === 0 ? (
<div className="bg-gray-100 p-6 rounded-lg text-center">
<p className="text-gray-700">You don't have any courses available for dropping.</p>
{enrolledCourses.length > 0 && pendingDropRequestsMap.size > 0 && (
<p className="text-gray-500 text-sm mt-2">
All your enrolled courses already have pending drop requests.
</p>
)}
</div>
) : (
<div className="grid grid-cols-1 gap-4">
{availableForDrop.map((course) => (
<div key={course.id} className="bg-white rounded-lg shadow border border-gray-200 hover:shadow-md transition duration-300">
{/* Course card content */}
</div>
))}
</div>
)}
Pending Drop Requests Section
Displays pending drop requests with an option to cancel:
{pendingRequests.length > 0 && (
<div>
<h2 className="text-xl font-semibold mb-4 text-gray-700 flex items-center">
<FaRegClock className="mr-2 text-yellow-500" />
Pending Drop Requests
</h2>
<div className="grid grid-cols-1 gap-4">
{pendingRequests.map((request) => (
<div key={request._id} className="bg-yellow-50 rounded-lg shadow border border-yellow-200 hover:shadow-md transition duration-300">
{/* Request card content */}
</div>
))}
</div>
</div>
)}
Drop Request History Section
Displays historical drop requests with their status:
{historyRequests.length > 0 && (
<div>
<h2 className="text-xl font-semibold mb-4 text-gray-700 flex items-center">
<FaHistory className="mr-2 text-purple-500" />
Drop Request History
</h2>
<div className="grid grid-cols-1 gap-4">
{historyRequests.map((request) => (
<div key={request._id} className={`bg-white rounded-lg shadow border ${
request.status === 'Approved' ? 'border-green-200' :
request.status === 'Rejected' ? 'border-red-200' :
'border-gray-200'}`}>
{/* History request card content */}
</div>
))}
</div>
</div>
)}
Information Section
Displays important information about the course drop process:
<div className="bg-blue-50 p-5 rounded-lg border border-blue-200">
<h3 className="font-semibold text-blue-800 mb-3 flex items-center">
<FaExclamationTriangle className="mr-2" />
Important Information
</h3>
<ul className="list-disc pl-5 text-blue-700 text-sm space-y-2">
<li>Course drop requests are subject to approval by the administration.</li>
{/* More information items */}
</ul>
</div>
Expected Data Structures
Enrolled Course
{
id: "CS101",
name: "Introduction to Computer Science",
credits: 4,
// Other course properties
}
Drop Request
{
_id: "requestId123",
courseId: "CS101",
courseName: "Introduction to Computer Science",
status: "Pending" | "Approved" | "Rejected",
requestDate: "2025-04-10T14:30:00.000Z",
decisionDate: "2025-04-12T09:15:00.000Z", // Only for Approved/Rejected
remarks: "Request approved based on valid reasons", // Optional
// Other request properties
}
Error Handling
The component handles errors in data fetching and mutations:
- Displays error messages for failed API requests
- Shows user-friendly alerts for mutation failures
- Provides appropriate UI states when data is not available
Navigation
The component includes a navigation link back to the courses page:
<div className="mt-8 text-center">
<Link
to="/courses"
className="inline-flex items-center text-pink-600 hover:text-pink-700"
>
Back to My Courses
</Link>
</div>
Styling
The component uses Tailwind CSS for styling with a consistent color scheme: - Pink for primary actions and branding - Yellow for pending status - Green for approved status - Red for rejected status - Blue for information
Accessibility Features
The component includes: - Semantic HTML structure - Color indicators with text alternatives - Proper button states for loading/disabled conditions - Responsive design for different screen sizes
Best Practices
The component follows several best practices: - Confirmation dialogs for destructive actions - Clear visual indicators for different statuses - Informative feedback after actions - Comprehensive error handling - Clean separation of concerns with helper components