-
+
+
@@ -228,25 +229,12 @@ const Dashboard = () => {
{trainerCourseStats?.data?.stats?.myCourses || 0}
+
Click to view all courses
-
+
-
-
-
-
-
-
-
Published
-
- {trainerCourseStats?.data?.stats?.myPublishedCourses || 0}
-
-
-
-
-
-
+
@@ -256,9 +244,10 @@ const Dashboard = () => {
{enrollmentStats?.data?.stats?.myStudents || 0}
+
Click to view all students
-
+
@@ -286,15 +275,15 @@ const Dashboard = () => {
Quick Actions
-
-
-
+
+
+ Manage Students
+
diff --git a/frontend/src/pages/TrainerCourses.js b/frontend/src/pages/TrainerCourses.js
new file mode 100644
index 0000000..129d823
--- /dev/null
+++ b/frontend/src/pages/TrainerCourses.js
@@ -0,0 +1,253 @@
+import React, { useState } from 'react';
+import { useQuery } from 'react-query';
+import { Link } from 'react-router-dom';
+import { useAuth } from '../contexts/AuthContext';
+import { coursesAPI } from '../services/api';
+import {
+ BookOpenIcon,
+ EyeIcon,
+ PencilIcon,
+ PlusIcon,
+ CheckCircleIcon,
+ ClockIcon,
+ UserGroupIcon,
+ ChartBarIcon,
+} from '@heroicons/react/24/outline';
+import LoadingSpinner from '../components/LoadingSpinner';
+import toast from 'react-hot-toast';
+
+const TrainerCourses = () => {
+ const { user } = useAuth();
+ const [filter, setFilter] = useState('all'); // all, published, unpublished
+
+ // Fetch trainer's courses
+ const { data: coursesData, isLoading, error } = useQuery(
+ ['trainer-courses', filter],
+ () => coursesAPI.getTrainerCourses(user?.id, {
+ isPublished: filter === 'all' ? undefined : filter === 'published'
+ }),
+ { enabled: !!user?.id }
+ );
+
+ const handlePublishToggle = async (courseId, currentStatus) => {
+ try {
+ await coursesAPI.publish(courseId, !currentStatus);
+ toast.success(`Course ${currentStatus ? 'unpublished' : 'published'} successfully!`);
+ // Refetch the data
+ window.location.reload();
+ } catch (error) {
+ toast.error('Failed to update course status');
+ }
+ };
+
+ if (isLoading) return
;
+ if (error) return
Error loading courses: {error.message}
;
+
+ const courses = coursesData?.courses || [];
+ const publishedCount = courses.filter(c => c.isPublished).length;
+ const unpublishedCount = courses.filter(c => !c.isPublished).length;
+
+ return (
+
+ {/* Header */}
+
+
+
+
My Courses
+
Manage your published and unpublished courses
+
+
+
+
Create Course
+
+
+
+
+ {/* Stats Cards */}
+
+
+
+
+
+
+
+
Total Courses
+
{courses.length}
+
+
+
+
+
+
+
+
+
+
+
Published
+
{publishedCount}
+
+
+
+
+
+
+
+
+
+
+
Drafts
+
{unpublishedCount}
+
+
+
+
+
+ {/* Filter Tabs */}
+
+
+
+
+
+
+ {/* Courses Grid */}
+ {courses.length === 0 ? (
+
+
+
No courses
+
+ {filter === 'all'
+ ? "You haven't created any courses yet."
+ : filter === 'published'
+ ? "You don't have any published courses."
+ : "You don't have any draft courses."
+ }
+
+
+
+
+ Create your first course
+
+
+
+ ) : (
+
+ {courses.map((course) => (
+
+ {/* Course Image */}
+
+ {course.thumbnail ? (
+

+ ) : (
+
+
+
+ )}
+
+
+ {/* Course Info */}
+
+
+
+ {course.title}
+
+
+ {course.isPublished ? 'Published' : 'Draft'}
+
+
+
+
+ {course.shortDescription || course.description}
+
+
+ {/* Course Stats */}
+
+
+
+ {course.enrolledStudents || 0} students
+
+
+
+ {course.level || 'Beginner'}
+
+
+
+ {/* Action Buttons */}
+
+
+
+
View
+
+
+
+
+
Edit
+
+
+
+ {/* Publish/Unpublish Toggle */}
+
+
+
+ ))}
+
+ )}
+
+ );
+};
+
+export default TrainerCourses;
diff --git a/frontend/src/pages/TrainerStudents.js b/frontend/src/pages/TrainerStudents.js
new file mode 100644
index 0000000..8c77057
--- /dev/null
+++ b/frontend/src/pages/TrainerStudents.js
@@ -0,0 +1,299 @@
+import React, { useState, useRef, useEffect } from 'react';
+import { useQuery } from 'react-query';
+import { Link } from 'react-router-dom';
+import { useAuth } from '../contexts/AuthContext';
+import { enrollmentsAPI, coursesAPI } from '../services/api';
+import {
+ UsersIcon,
+ BookOpenIcon,
+ CalendarIcon,
+ ChartBarIcon,
+ EllipsisVerticalIcon,
+ UserIcon,
+ EyeIcon,
+ XMarkIcon,
+} from '@heroicons/react/24/outline';
+import LoadingSpinner from '../components/LoadingSpinner';
+import toast from 'react-hot-toast';
+
+const TrainerStudents = () => {
+ const { user } = useAuth();
+ const [selectedCourse, setSelectedCourse] = useState('all');
+ const [showActionsDropdown, setShowActionsDropdown] = useState(null);
+ const dropdownRef = useRef(null);
+
+ // Close dropdown when clicking outside
+ useEffect(() => {
+ const handleClickOutside = (event) => {
+ if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
+ setShowActionsDropdown(null);
+ }
+ };
+ document.addEventListener('mousedown', handleClickOutside);
+ return () => {
+ document.removeEventListener('mousedown', handleClickOutside);
+ };
+ }, []);
+
+ // Fetch trainer's courses
+ const { data: coursesData, isLoading: coursesLoading } = useQuery(
+ ['trainer-courses'],
+ () => coursesAPI.getTrainerCourses(user?.id),
+ { enabled: !!user?.id }
+ );
+
+ // Fetch enrollments for selected course
+ const { data: enrollmentsData, isLoading: enrollmentsLoading } = useQuery(
+ ['trainer-enrollments', selectedCourse],
+ () => selectedCourse === 'all'
+ ? enrollmentsAPI.getTrainerEnrollments(user?.id)
+ : enrollmentsAPI.getCourseTrainees(selectedCourse),
+ { enabled: !!user?.id && !!selectedCourse }
+ );
+
+ const handleRemoveStudent = async (enrollmentId, studentName, courseTitle) => {
+ if (window.confirm(`Are you sure you want to remove ${studentName} from ${courseTitle}?`)) {
+ try {
+ await enrollmentsAPI.delete(enrollmentId);
+ toast.success(`${studentName} removed from ${courseTitle} successfully!`);
+ // Refetch the data
+ window.location.reload();
+ } catch (error) {
+ toast.error('Failed to remove student from course');
+ }
+ }
+ };
+
+ if (coursesLoading || enrollmentsLoading) return
;
+
+ const courses = coursesData?.courses || [];
+ const enrollments = enrollmentsData?.enrollments || enrollmentsData?.trainees || [];
+
+ // Get unique students across all courses
+ const uniqueStudents = enrollments.reduce((acc, enrollment) => {
+ const studentId = enrollment.user?.id || enrollment.userId;
+ if (!acc.find(s => s.id === studentId)) {
+ acc.push({
+ id: studentId,
+ firstName: enrollment.user?.firstName || enrollment.user?.firstName,
+ lastName: enrollment.user?.lastName || enrollment.user?.lastName,
+ email: enrollment.user?.email || enrollment.user?.email,
+ avatar: enrollment.user?.avatar || enrollment.user?.avatar,
+ enrollments: []
+ });
+ }
+
+ const student = acc.find(s => s.id === studentId);
+ student.enrollments.push({
+ courseId: enrollment.course?.id || enrollment.courseId,
+ courseTitle: enrollment.course?.title || enrollment.course?.title,
+ status: enrollment.status,
+ enrolledAt: enrollment.enrolledAt,
+ progress: enrollment.progress || 0,
+ enrollmentId: enrollment.id
+ });
+
+ return acc;
+ }, []);
+
+ return (
+
+ {/* Header */}
+
+
+
+
My Students
+
Manage students enrolled in your courses
+
+
+
+
+ {/* Stats Cards */}
+
+
+
+
+
+
+
+
Total Students
+
{uniqueStudents.length}
+
+
+
+
+
+
+
+
+
+
+
Total Courses
+
{courses.length}
+
+
+
+
+
+
+
+
+
+
+
Total Enrollments
+
{enrollments.length}
+
+
+
+
+
+ {/* Course Filter */}
+
+
+
+
+
+ {/* Students List */}
+ {uniqueStudents.length === 0 ? (
+
+
+
No students
+
+ {selectedCourse === 'all'
+ ? "You don't have any students enrolled in your courses yet."
+ : "No students are enrolled in this course."
+ }
+
+
+ ) : (
+
+ {uniqueStudents.map((student) => (
+
+ {/* Student Header */}
+
+
+
+ {student.avatar ? (
+

+ ) : (
+
+
+
+ )}
+
+
+
+ {student.firstName} {student.lastName}
+
+
{student.email}
+
+
+
+
+
+
+
View Profile
+
+
+ {/* Actions Dropdown */}
+
+
+
+ {showActionsDropdown === student.id && (
+
+
+
+
+
+ )}
+
+
+
+
+ {/* Enrollments */}
+
+
Course Enrollments
+ {student.enrollments.map((enrollment) => (
+
+
+
+
{enrollment.courseTitle}
+
+ {enrollment.status}
+
+
+
+
+
+
+ Enrolled: {new Date(enrollment.enrolledAt).toLocaleDateString()}
+
+
+
+ Progress: {enrollment.progress}%
+
+
+
+
+
+
+ ))}
+
+
+ ))}
+
+ )}
+
+ );
+};
+
+export default TrainerStudents;
diff --git a/frontend/src/services/api.js b/frontend/src/services/api.js
index f893445..dfcdecd 100644
--- a/frontend/src/services/api.js
+++ b/frontend/src/services/api.js
@@ -83,6 +83,8 @@ export const coursesAPI = {
getAvailableTrainers: () => api.get('/courses/trainers/available').then(res => res.data),
getCategories: () => api.get('/courses/categories/all'),
getStats: () => api.get('/courses/stats/overview'),
+ // New trainer-specific endpoints
+ getTrainerCourses: (trainerId, params) => api.get(`/courses/trainer/${trainerId}`, { params }).then(res => res.data),
uploadCourseImage: (courseName, file) => {
const formData = new FormData();
formData.append('image', file);
@@ -127,7 +129,9 @@ export const enrollmentsAPI = {
bulkEnroll: (data) => api.post('/enrollments/bulk', data),
assignTrainee: (data) => api.post('/enrollments/assign', data),
getCourseTrainees: (courseId, params) => api.get(`/enrollments/course/${courseId}/trainees`, { params }).then(res => res.data),
- getAvailableTrainees: (params) => api.get('/enrollments/available-trainees', { params }).then(res => res.data)
+ getAvailableTrainees: (params) => api.get('/enrollments/available-trainees', { params }).then(res => res.data),
+ // New trainer-specific endpoints
+ getTrainerEnrollments: (trainerId, params) => api.get(`/enrollments/trainer/${trainerId}`, { params }).then(res => res.data),
};
// Attendance API
diff --git a/version.txt b/version.txt
index b924b02..043c86d 100644
--- a/version.txt
+++ b/version.txt
@@ -1,57 +1,168 @@
-CourseWorx v1.2.0
-
-CHANGELOG:
-
-v1.2.0 (2025-08-20)
-===================
-
-FEATURES & IMPROVEMENTS:
-- โจ Implemented responsive dropdown menu for course action buttons
-- ๐ง Fixed trainer assignment dropdown population issue
-- ๐ ๏ธ Resolved available trainees API routing conflict
-- ๐ฑ Enhanced mobile responsiveness across the application
-- โฟ Improved accessibility with ARIA labels and keyboard navigation
-
-BUG FIXES:
-- ๐ Fixed trainer assignment dropdown showing "No available trainers"
-- ๐ Resolved 500 Internal Server Error in available-trainees endpoint
-- ๐ Fixed route ordering issue where /:id was catching /available-trainees
-- ๐ Corrected API response structure for getAvailableTrainers function
-- ๐ Fixed setup page redirect not working after Super Admin creation
-- ๐ Resolved ESLint warnings in AuthContext and Setup components
-
-TECHNICAL IMPROVEMENTS:
-- ๐ Reordered Express.js routes to prevent conflicts
-- ๐ Added comprehensive logging for debugging trainer assignment
-- ๐ฏ Improved API response handling in frontend services
-- ๐ Enhanced user experience with smooth dropdown animations
-- ๐จ Implemented consistent hover effects and transitions
-
-RESPONSIVE DESIGN:
-- ๐ฑ Converted horizontal action buttons to compact 3-dots dropdown
-- ๐จ Added professional dropdown styling with shadows and rings
-- ๐ Implemented click-outside functionality for dropdown menus
-- โจ๏ธ Added keyboard navigation support (Enter/Space keys)
-- ๐ฏ Optimized positioning for all screen sizes
-
-CODE QUALITY:
-- ๐งน Fixed React Hook dependency warnings
-- ๐ซ Removed unused variables and imports
-- ๐ Added comprehensive code documentation
-- ๐ฏ Improved error handling and user feedback
-- ๐ Enhanced debugging capabilities
-
-PREVIOUS VERSIONS:
+CourseWorx v1.3.0
==================
-v1.1.0 (2025-08-20)
-- Initial CourseWorx application setup
-- User authentication and role management
-- Course management system
-- Basic enrollment functionality
-- File upload capabilities
+๐ฏ MAJOR FEATURE: Course Content Section for Trainers
+=====================================================
-v1.0.0 (2025-08-20)
-- Project initialization
-- Basic project structure
-- Development environment setup
\ No newline at end of file
+๐ NEW FEATURES
+---------------
+
+**1. Enhanced Trainer Dashboard**
+- โ Removed duplicate "Published" card (was duplication)
+- ๐ Made "My Courses" card clickable โ Navigates to /trainer/courses
+- ๐ Made "My Students" card clickable โ Navigates to /trainer/students
+- ๐จ Enhanced Quick Actions with proper navigation links
+- โจ Added hover effects and helpful text hints
+
+**2. New Trainer Courses Page (/trainer/courses)**
+- ๐ Statistics Cards: Total, Published, Drafts counts
+- ๐ Filter Tabs: All Courses, Published, Drafts
+- ๐ฏ Course Grid: Shows course thumbnails, titles, descriptions
+- ๐ฑ Responsive Design: Works perfectly on all devices
+- โก Quick Actions: View, Edit, Publish/Unpublish toggle
+- ๐ Navigation: Direct links to course creation and management
+- ๐จ Beautiful UI with hover effects and transitions
+
+**3. New Trainer Students Page (/trainer/students)**
+- ๐ Statistics Cards: Total Students, Total Courses, Total Enrollments
+- ๐ฏ Course Filter: Filter students by specific course or view all
+- ๐ค Student Cards: Individual student profiles with avatars
+- ๐ Enrollment Details: Course name, status, enrollment date, progress
+- ๐ฏ Student Actions: View Profile link, 3-dots dropdown for future actions
+- โ Remove Student: X button to remove student from specific course
+- ๐ Navigation: Links to student profiles
+
+**4. Backend API Endpoints**
+- `GET /api/courses/trainer/:trainerId` - Get trainer's courses
+- `GET /api/enrollments/trainer/:trainerId` - Get trainer's enrollments
+- ๐ Security: Trainers can only access their own data
+- ๐ Filtering: Support for published/unpublished, course-specific filtering
+
+**5. Enhanced API Services**
+- `coursesAPI.getTrainerCourses()` - Frontend service for trainer courses
+- `enrollmentsAPI.getTrainerEnrollments()` - Frontend service for trainer enrollments
+- ๐ React Query Integration: Efficient data fetching and caching
+
+๐ง TECHNICAL IMPROVEMENTS
+-------------------------
+
+**Frontend Components:**
+- Created `TrainerCourses.js` - Complete course management interface
+- Created `TrainerStudents.js` - Comprehensive student management interface
+- Updated `Dashboard.js` - Enhanced trainer dashboard with clickable cards
+- Updated `App.js` - New routing for trainer pages (/trainer/courses, /trainer/students)
+
+**Backend Enhancements:**
+- Added secure trainer-specific API endpoints
+- Implemented role-based access control
+- Added efficient data filtering and pagination
+- Enhanced security for trainer data access
+
+**API Integration:**
+- React Query for optimal data management
+- Proper error handling and loading states
+- Responsive design with Tailwind CSS
+- Consistent styling with the rest of the application
+
+๐จ DESIGN FEATURES
+------------------
+
+**Responsive Design:**
+- Mobile-first approach
+- Grid layouts that adapt to screen size
+- Touch-friendly buttons and interactions
+
+**User Experience:**
+- Clear visual hierarchy
+- Intuitive navigation
+- Helpful hover effects and transitions
+- Consistent styling throughout
+
+**Accessibility:**
+- Proper ARIA labels
+- Keyboard navigation support
+- Screen reader friendly structure
+
+๐ฑ USER FLOW
+------------
+
+**For Trainers:**
+1. Login to the system
+2. Dashboard shows clickable "My Courses" and "My Students" cards
+3. Click "My Courses" to see all courses with publish/unpublish controls
+4. Click "My Students" to manage student enrollments
+5. Filter students by specific course or view all
+6. Remove students from courses as needed
+7. View student profiles for detailed information
+
+**Navigation Flow:**
+```
+Dashboard โ My Courses โ /trainer/courses
+Dashboard โ My Students โ /trainer/students
+```
+
+๐ SECURITY FEATURES
+--------------------
+
+- Trainers can only access their own course data
+- Role-based access control for all endpoints
+- Secure API authentication required
+- Data isolation between different trainers
+
+๐ PERFORMANCE IMPROVEMENTS
+---------------------------
+
+- Efficient React Query implementation
+- Optimized database queries
+- Lazy loading of components
+- Responsive image handling
+
+๐ BUG FIXES
+-------------
+
+- Fixed phone number login functionality
+- Removed temporary debug endpoints
+- Cleaned up authentication logging
+- Resolved routing conflicts
+
+๐ PREVIOUS VERSIONS
+====================
+
+v1.2.0 - Course Management & User Experience
+- Implemented first-time setup flow for Super Admin
+- Made phone number field mandatory
+- Added login with email or phone number
+- Fixed trainer assignment dropdown issues
+- Resolved trainee assignment modal problems
+- Made course detail page responsive with 3-dots dropdown
+- Fixed routing order issues in enrollments
+- Resolved ESLint warnings and errors
+
+v1.1.0 - Authentication & Core Features
+- Fixed JSON parsing errors during login
+- Resolved authentication persistence issues
+- Implemented proper error handling
+- Added comprehensive startup scripts
+- Fixed port conflict detection
+
+v1.0.0 - Initial Release
+- Basic CourseWorx application structure
+- User authentication system
+- Course management functionality
+- User role management (Super Admin, Trainer, Trainee)
+- Basic dashboard and navigation
+
+๐ NEXT STEPS
+=============
+
+Future enhancements planned:
+- Advanced student analytics and reporting
+- Course content management system
+- Assignment and grading system
+- Attendance tracking improvements
+- Enhanced notification system
+- Mobile application development
+
+---
+Release Date: August 20, 2025
+Developed by: CourseWorx Development Team
\ No newline at end of file