{relatedCourse.title}
{relatedCourse.category}
{/* Add rating/instructor if available in related data */}// components/CourseDetailPage.jsx /** * Course Detail Page Component * * Fetches and displays detailed information for a single course, * including overview, curriculum, instructor details, reviews, * and related courses. Uses tabs for content organization. */ // React, Hooks, PropTypes are globally available function CourseDetailPage({ courseId }) { // --- State --- const [course, setCourse] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [activeTab, setActiveTab] = useState("overview"); // Default tab // --- API Endpoint --- const COURSE_DETAIL_ENDPOINT = "/api/get_course_detail.php"; // --- Effects --- // Fetch course details when courseId changes useEffect(() => { const fetchCourse = async () => { // Reset state for new fetch setLoading(true); setError(null); setCourse(null); setActiveTab("overview"); // Reset tab on new course load // Ensure courseId is a valid number before proceeding const numericCourseId = parseInt(courseId, 10); if (isNaN(numericCourseId) || numericCourseId <= 0) { setError("Invalid Course ID provided."); setLoading(false); console.error("CourseDetailPage: Invalid courseId prop received:", courseId); return; } console.log(`CourseDetailPage: Fetching details for course ID: ${numericCourseId}`); try { const response = await fetch(`${COURSE_DETAIL_ENDPOINT}?id=${numericCourseId}`); // Check for network or server errors (non-2xx status) if (!response.ok) { let errorMsg = `HTTP error! Status: ${response.status}`; try { // Try to parse JSON error from backend const errData = await response.json(); errorMsg = errData.error || errorMsg; } catch (e) { /* Ignore parsing error if body isn't JSON */ } throw new Error(errorMsg); } const data = await response.json(); // Check for application-level errors (e.g., { success: false, error: '...' }) if (!data.success) { // Handle specific "Not Found" error from API if (response.status === 404 || data.error?.toLowerCase().includes('not found')) { throw new Error(`Course with ID ${numericCourseId} not found.`); } throw new Error(data.error || "Failed to fetch course details"); } console.log("CourseDetailPage: Course details fetched:", data.data); setCourse(data.data); // Set the fetched course data } catch (err) { console.error("CourseDetailPage: Error fetching course details:", err); setError(err.message); // Set error state to display message } finally { setLoading(false); // Ensure loading state is turned off } }; fetchCourse(); // Scroll to top when courseId changes for better UX window.scrollTo(0, 0); }, [courseId]); // Re-run effect if courseId changes // --- Helper Functions --- // Safely splits text by newline, handling null/undefined input and trimming lines const splitLines = (text) => { return text ? text.split('\n').map(line => line.trim()).filter(line => line !== '') : []; }; // Renders star ratings using Font Awesome icons const renderStars = (rating) => { const numRating = parseFloat(rating) || 0; const fullStars = Math.floor(numRating); // Use Math.round to decide half star more conventionally const halfStar = Math.round(numRating - fullStars) === 1 ? 1 : 0; // Correct calculation for empty stars const emptyStars = 5 - fullStars - halfStar; return ( <> {/* Render full stars */} {fullStars > 0 && Array(fullStars).fill(null).map((_, i) => )} {/* Render half star */} {halfStar > 0 && } {/* Render empty stars */} {emptyStars > 0 && Array(emptyStars).fill(null).map((_, i) => )} > ); }; // Handles image loading errors for various images on the page const handleImageError = (e, placeholderType = 'course') => { e.target.onerror = null; // Prevent infinite loop let placeholderUrl = 'https://via.placeholder.com/400x225?text=No+Image'; // Default if (placeholderType === 'instructor') { placeholderUrl = 'https://via.placeholder.com/100?text=Inst'; } else if (placeholderType === 'user') { placeholderUrl = 'https://via.placeholder.com/50?text=User'; } else if (placeholderType === 'related') { placeholderUrl = 'https://via.placeholder.com/300x150?text=Related'; } e.target.src = placeholderUrl; }; // --- Render Logic --- // Loading State if (loading) { return
Learning objectives not specified for this course.
}No specific prerequisites or requirements listed.
}{course.detailed_description || course.description || "No detailed description available."}
No lessons listed in this module.
}Curriculum details are not available for this course.
}{course.instructor_title}
}{course.instructor_bio}
) : (No biography available for this instructor.
)}Instructor information is not available for this course.
}{review.content}
) : (No comment provided.
)}Be the first to review this course!
} {/* TODO: Add a "Write a Review" button/form here if user is enrolled and hasn't reviewed */}{course.description}
{relatedCourse.category}
{/* Add rating/instructor if available in related data */}