import React, { useState, useEffect } from 'react'; import { useAppSelector } from '../store/hooks'; import { useNavigate } from 'react-router-dom'; import { Play, BookOpen, Download, ExternalLink, CheckCircle, Clock, Users, Award, Video, FileText, Headphones, Globe, ChevronDown, ChevronUp, Plus, Edit, Trash2, Settings, Save, X, Upload, Eye, EyeOff, Search, Filter } from 'lucide-react'; import toast from 'react-hot-toast'; interface TrainingCourse { id: number; title: string; description: string; level: string; category: string; totalVideos: number; totalDuration: number; isActive: boolean; createdAt: string; vendor: { firstName: string; lastName: string; company?: string; }; videos?: TrainingVideo[]; // Added for expanded view } interface TrainingVideo { id: number; title: string; description: string; videoType: 'youtube' | 'manual_upload'; youtubeUrl?: string; videoFile?: string; duration: number; requiredForCompletion: boolean; sortOrder: number; } interface CourseProgress { courseId: number; status: 'not_started' | 'in_progress' | 'completed'; progressPercentage: number; completedVideos: number; totalVideos: number; lastActivity?: string; } const Training: React.FC = () => { const { user } = useAppSelector((state) => state.auth); const navigate = useNavigate(); const [courses, setCourses] = useState([]); const [courseProgress, setCourseProgress] = useState([]); const [selectedCourse, setSelectedCourse] = useState(null); const [loading, setLoading] = useState(true); const [search, setSearch] = useState(''); const [selectedCategory, setSelectedCategory] = useState('all'); const [selectedLevel, setSelectedLevel] = useState('all'); const [expandedCourse, setExpandedCourse] = useState(null); // Modal states const [isCreateCourseModalOpen, setIsCreateCourseModalOpen] = useState(false); const [isAddVideoModalOpen, setIsAddVideoModalOpen] = useState(false); const [isEditCourseModalOpen, setIsEditCourseModalOpen] = useState(false); const [isVideoPreviewModalOpen, setIsVideoPreviewModalOpen] = useState(false); const [selectedVideo, setSelectedVideo] = useState(null); // Form states const [courseForm, setCourseForm] = useState({ title: '', description: '', level: 'Beginner', category: '', completionCriteria: 'all_videos', completionPercentage: 100 }); const [videoForm, setVideoForm] = useState({ title: '', description: '', videoType: 'youtube' as 'youtube' | 'manual_upload', youtubeUrl: '', requiredForCompletion: true }); useEffect(() => { console.log('Training component - User state:', user); console.log('Training component - User role:', user?.role); console.log('Training component - User roles:', user?.roles); console.log('Training component - Is authenticated:', !!localStorage.getItem('accessToken')); if (!user) { console.log('No user found, checking if we need to redirect to login'); // If no user but we have a token, we might need to fetch user data const token = localStorage.getItem('accessToken'); if (token) { console.log('Token found but no user, might need to fetch user data'); // You might want to dispatch an action to fetch user data here } else { console.log('No token, redirecting to login'); navigate('/login'); return; } } // Check if user has the right role for vendor access const isVendor = user?.role === 'channel_partner_admin' || user?.roles?.some(r => r.name === 'channel_partner_admin'); console.log('Is vendor:', isVendor); if (isVendor) { console.log('Fetching vendor courses...'); fetchVendorCourses(); } else { console.log('Fetching available courses for reseller...'); fetchAvailableCourses(); } }, [user, navigate]); const fetchVendorCourses = async () => { try { setLoading(true); const token = localStorage.getItem('accessToken'); console.log('Fetching vendor courses with token:', token ? 'Token exists' : 'No token'); if (!token) { console.error('No access token found'); toast.error('Authentication required. Please log in.'); return; } const response = await fetch(`${process.env.REACT_APP_API_URL || 'http://localhost:5000/api'}/training-new/courses`, { headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' } }); console.log('API response status:', response.status); console.log('API response headers:', response.headers); if (response.ok) { const data = await response.json(); console.log('API response data:', data); console.log('Courses received:', data.data.courses); console.log('First course videos:', data.data.courses[0]?.videos); setCourses(data.data.courses || []); } else { const errorData = await response.json().catch(() => ({ message: 'Unknown error' })); console.error('API error response:', errorData); toast.error(errorData.message || 'Failed to fetch courses'); } } catch (error) { console.error('Error fetching courses:', error); toast.error('Failed to fetch courses'); } finally { setLoading(false); } }; const fetchAvailableCourses = async () => { try { setLoading(true); const token = localStorage.getItem('accessToken'); console.log('Fetching available courses with token:', token ? 'Token exists' : 'No token'); if (!token) { console.error('No access token found'); toast.error('Authentication required. Please log in.'); return; } const response = await fetch(`${process.env.REACT_APP_API_URL || 'http://localhost:5000/api'}/training-new/courses`, { headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' } }); console.log('API response status:', response.status); console.log('API response headers:', response.headers); if (response.ok) { const data = await response.json(); console.log('API response data:', data); console.log('Courses received:', data.data.courses); console.log('First course videos:', data.data.courses[0]?.videos); setCourses(data.data.courses || []); // Fetch progress for each course const progressPromises = data.data.courses.map((course: TrainingCourse) => fetchCourseProgress(course.id) ); const progressResults = await Promise.all(progressPromises); setCourseProgress(progressResults.filter(Boolean)); } else { const errorData = await response.json().catch(() => ({ message: 'Unknown error' })); console.error('API error response:', errorData); toast.error(errorData.message || 'Failed to fetch courses'); } } catch (error) { console.error('Error fetching courses:', error); toast.error('Failed to fetch courses'); } finally { setLoading(false); } }; const fetchCourseProgress = async (courseId: number): Promise => { try { const token = localStorage.getItem('accessToken'); const response = await fetch(`${process.env.REACT_APP_API_URL || 'http://localhost:5000/api'}/training-new/courses/${courseId}/progress`, { headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' } }); if (response.ok) { const data = await response.json(); return data.data.userProgress || null; } } catch (error) { console.error('Error fetching course progress:', error); } return null; }; const handleCreateCourse = async (e: React.FormEvent) => { e.preventDefault(); try { const token = localStorage.getItem('accessToken'); const response = await fetch(`${process.env.REACT_APP_API_URL || 'http://localhost:5000/api'}/training-new/courses`, { method: 'POST', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }, body: JSON.stringify(courseForm) }); if (response.ok) { const data = await response.json(); toast.success('Course created successfully'); setIsCreateCourseModalOpen(false); setCourseForm({ title: '', description: '', level: 'Beginner', category: '', completionCriteria: 'all_videos', completionPercentage: 100 }); fetchVendorCourses(); } else { const errorData = await response.json(); toast.error(errorData.message || 'Failed to create course'); } } catch (error) { console.error('Error creating course:', error); toast.error('Failed to create course'); } }; const handleAddVideo = async (e: React.FormEvent) => { e.preventDefault(); if (!selectedCourse) return; try { const token = localStorage.getItem('accessToken'); const formData = new FormData(); // Handle form data properly for different types Object.keys(videoForm).forEach(key => { const value = videoForm[key as keyof typeof videoForm]; if (typeof value === 'string') { formData.append(key, value); } else if (typeof value === 'boolean') { formData.append(key, value.toString()); } }); const response = await fetch(`${process.env.REACT_APP_API_URL || 'http://localhost:5000/api'}/training-new/courses/${selectedCourse.id}/videos`, { method: 'POST', headers: { 'Authorization': `Bearer ${token}` }, body: formData }); if (response.ok) { const data = await response.json(); toast.success('Video added successfully'); setIsAddVideoModalOpen(false); setVideoForm({ title: '', description: '', videoType: 'youtube', youtubeUrl: '', requiredForCompletion: true }); fetchVendorCourses(); } else { const errorData = await response.json(); toast.error(errorData.message || 'Failed to add video'); } } catch (error) { console.error('Error adding video:', error); toast.error('Failed to add video'); } }; const getLevelColor = (level: string) => { switch (level) { case 'Beginner': return 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200'; case 'Intermediate': return 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200'; case 'Advanced': return 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200'; default: return 'bg-gray-100 text-gray-800 dark:bg-gray-900 dark:text-gray-200'; } }; const getStatusColor = (status: string) => { switch (status) { case 'completed': return 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200'; case 'in_progress': return 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200'; case 'not_started': return 'bg-gray-100 text-gray-800 dark:bg-gray-900 dark:text-gray-200'; default: return 'bg-gray-100 text-gray-800 dark:bg-gray-900 dark:text-gray-200'; } }; const getStatusIcon = (status: string) => { switch (status) { case 'completed': return ; case 'in_progress': return ; default: return ; } }; const filteredCourses = courses.filter(course => { const matchesSearch = course.title.toLowerCase().includes(search.toLowerCase()) || course.description.toLowerCase().includes(search.toLowerCase()); const matchesCategory = selectedCategory === 'all' || course.category === selectedCategory; const matchesLevel = selectedLevel === 'all' || course.level === selectedLevel; return matchesSearch && matchesCategory && matchesLevel; }); const uniqueCategories = Array.from(new Set(courses.map(course => course.category).filter(Boolean))); const uniqueLevels = ['Beginner', 'Intermediate', 'Advanced']; if (loading) { return (
); } return (
{/* Header */}

Training Center

Master the Cloudtopiaa platform and maximize your success

{/* Action Bar for Vendors */} {user?.role === 'channel_partner_admin' && (
{courses.length} course{courses.length !== 1 ? 's' : ''} created
)} {/* Search and Filters */}
e.preventDefault()} className="flex-1">
setSearch(e.target.value)} className="w-full pl-10 pr-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:outline-none focus:ring-2 focus:ring-primary-500" />
{/* How Training Works Section */}

How Training Works

1

Choose Your Path

Select from beginner, intermediate, or advanced training modules based on your experience level.

2

Watch & Learn

Access high-quality video tutorials, webinars, and interactive content designed for resellers.

3

Get Certified

Complete assessments and earn certifications to demonstrate your expertise to customers.

{/* Training Courses */}

Training Modules

{filteredCourses.length > 0 ? (
{filteredCourses.map((course) => { const progress = courseProgress.find(p => p.courseId === course.id); const status = progress ? progress.status : 'not_started'; const progressPercentage = progress ? progress.progressPercentage : 0; const isExpanded = expandedCourse === course.id; return (

{course.title}

{course.level} {user?.role !== 'channel_partner_admin' && (
{getStatusIcon(status)} {status.replace('_', ' ').replace(/\b\w/g, l => l.toUpperCase())}
)}

{course.description}

{Math.round(course.totalDuration / 60)} hours
{course.totalVideos} materials
{user?.role !== 'channel_partner_admin' && progress && (
{progressPercentage}% complete
)}
{user?.role !== 'channel_partner_admin' && progress && (
Progress {progressPercentage}%
)}
{user?.role === 'channel_partner_admin' && ( <> )}
{/* Expanded Content */} {isExpanded && (

Course Content

{/* Debug info */}

Debug Info:

Course ID: {course.id}

Videos count: {course.videos?.length || 0}

Videos data: {JSON.stringify(course.videos, null, 2)}

{/* Video List */}
{course.videos && course.videos.length > 0 ? ( course.videos.map((video) => (
{/* Video Thumbnail/Preview */}
{video.videoType === 'youtube' && video.youtubeUrl ? (
{video.title} { // Fallback to medium quality if maxresdefault fails const target = e.target as HTMLImageElement; const videoId = video.youtubeUrl?.split('v=')[1] || ''; target.src = `https://img.youtube.com/vi/${videoId}/mqdefault.jpg`; }} /> {/* Play Button Overlay */}
{ setSelectedVideo(video); setIsVideoPreviewModalOpen(true); }} >
) : video.videoFile ? (
{/* Play Button Overlay */}
{ setSelectedVideo(video); setIsVideoPreviewModalOpen(true); }} >
) : (
)} {/* Video Duration Badge */} {video.duration && (
{Math.floor(video.duration / 60)}:{(video.duration % 60).toString().padStart(2, '0')}
)} {/* Required Badge */} {video.requiredForCompletion && (
Required
)}
{/* Video Info */}
{video.title}
{video.description && (

{video.description}

)} {/* Video Type Badge */}
{video.videoType === 'youtube' ? ( <> YouTube ) : ( <> Uploaded )} {/* Play Button */}
)) ) : (
)}
)}
); })}
) : (

{search || selectedCategory !== 'all' || selectedLevel !== 'all' ? 'No courses found' : 'No courses available yet'}

{search || selectedCategory !== 'all' || selectedLevel !== 'all' ? 'Try adjusting your search or filters' : user?.role === 'channel_partner_admin' ? 'Create your first training course to get started' : 'Check back later for new training courses'}

{user?.role === 'channel_partner_admin' && ( )}
)}
{/* Create Course Modal */} {isCreateCourseModalOpen && (

Create New Course

setCourseForm({ ...courseForm, title: e.target.value })} className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:outline-none focus:ring-2 focus:ring-primary-500" required />