import React, { useState, useEffect, useCallback } from 'react'; import { useNavigate } from 'react-router-dom'; import { api } from '../services/api'; import { AcademicCapIcon, DocumentTextIcon, CheckCircleIcon, ClockIcon, ArrowRightIcon, PencilIcon, XMarkIcon, CheckIcon, PlusIcon, TrashIcon } from '@heroicons/react/24/outline'; interface TutorialTask { _id: string; content: string; weekNumber: number; translationBrief?: string; imageUrl?: string; imageAlt?: string; } interface TutorialWeek { weekNumber: number; translationBrief?: string; tasks: TutorialTask[]; } interface UserSubmission { _id: string; transcreation: string; status: string; score: number; groupNumber?: number; isOwner?: boolean; userId?: { _id: string; username: string; }; voteCounts: { '1': number; '2': number; '3': number; }; } const TutorialTasks: React.FC = () => { const [selectedWeek, setSelectedWeek] = useState(() => { const savedWeek = localStorage.getItem('selectedTutorialWeek'); return savedWeek ? parseInt(savedWeek) : 1; }); const [tutorialTasks, setTutorialTasks] = useState([]); const [tutorialWeek, setTutorialWeek] = useState(null); const [userSubmissions, setUserSubmissions] = useState<{[key: string]: UserSubmission[]}>({}); const [loading, setLoading] = useState(true); const [submitting, setSubmitting] = useState<{[key: string]: boolean}>({}); const [translationText, setTranslationText] = useState<{[key: string]: string}>({}); const [selectedGroups, setSelectedGroups] = useState<{[key: string]: number}>({}); const [expandedSections, setExpandedSections] = useState<{[key: string]: boolean}>({}); const [editingTask, setEditingTask] = useState(null); const [editingBrief, setEditingBrief] = useState<{[key: number]: boolean}>({}); const [addingTask, setAddingTask] = useState(false); const [editForm, setEditForm] = useState<{ content: string; translationBrief: string; imageUrl: string; imageAlt: string; }>({ content: '', translationBrief: '', imageUrl: '', imageAlt: '' }); const [selectedFile, setSelectedFile] = useState(null); const [uploading, setUploading] = useState(false); const [saving, setSaving] = useState(false); const navigate = useNavigate(); const weeks = [1, 2, 3, 4, 5, 6]; const handleFileUpload = async (file: File): Promise => { try { setUploading(true); // Convert file to data URL for display return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = () => { const dataUrl = reader.result as string; console.log('File uploaded:', file.name, 'Size:', file.size); console.log('Generated data URL:', dataUrl.substring(0, 50) + '...'); resolve(dataUrl); }; reader.onerror = () => { console.error('Error reading file:', reader.error); reject(reader.error); }; reader.readAsDataURL(file); }); } catch (error) { console.error('Error uploading file:', error); throw error; } finally { setUploading(false); } }; const handleFileChange = (event: React.ChangeEvent) => { const file = event.target.files?.[0]; if (file) { setSelectedFile(file); } }; const toggleExpanded = (taskId: string) => { setExpandedSections(prev => ({ ...prev, [taskId]: !prev[taskId] })); }; const fetchUserSubmissions = useCallback(async (tasks: TutorialTask[]) => { try { const token = localStorage.getItem('token'); const user = localStorage.getItem('user'); if (!token || !user) { return; } const response = await api.get('/submissions/my-submissions'); if (response.data && response.data.submissions) { const data = response.data; const groupedSubmissions: {[key: string]: UserSubmission[]} = {}; // Initialize all tasks with empty arrays tasks.forEach(task => { groupedSubmissions[task._id] = []; }); // Then populate with actual submissions if (data.submissions && Array.isArray(data.submissions)) { data.submissions.forEach((submission: any) => { // Extract the actual ID from sourceTextId (could be string or object) const sourceTextId = submission.sourceTextId?._id || submission.sourceTextId; if (sourceTextId && groupedSubmissions[sourceTextId]) { groupedSubmissions[sourceTextId].push(submission); } }); } setUserSubmissions(groupedSubmissions); } } catch (error) { console.error('Error fetching user submissions:', error); } }, []); const fetchTutorialTasks = useCallback(async (showLoading = true) => { try { if (showLoading) { setLoading(true); } const token = localStorage.getItem('token'); const user = localStorage.getItem('user'); if (!token || !user) { navigate('/login'); return; } const response = await api.get(`/search/tutorial-tasks/${selectedWeek}`); if (response.data) { const tasks = response.data; console.log('Fetched tasks:', tasks); console.log('Tasks with images:', tasks.filter((task: TutorialTask) => task.imageUrl)); // Debug: Log each task's fields tasks.forEach((task: any, index: number) => { console.log(`Task ${index} fields:`, { _id: task._id, content: task.content, imageUrl: task.imageUrl, imageAlt: task.imageAlt, translationBrief: task.translationBrief, weekNumber: task.weekNumber, category: task.category }); console.log(`Task ${index} imageUrl:`, task.imageUrl); console.log(`Task ${index} translationBrief:`, task.translationBrief); }); setTutorialTasks(tasks); // Use translation brief from tasks or localStorage let translationBrief = null; if (tasks.length > 0) { translationBrief = tasks[0].translationBrief; console.log('Translation brief from task:', translationBrief); } else { // Check localStorage for brief if no tasks exist const briefKey = `translationBrief_week_${selectedWeek}`; translationBrief = localStorage.getItem(briefKey); console.log('Translation brief from localStorage:', translationBrief); console.log('localStorage key:', briefKey); } console.log('Final translation brief:', translationBrief); const tutorialWeekData: TutorialWeek = { weekNumber: selectedWeek, translationBrief: translationBrief, tasks: tasks }; setTutorialWeek(tutorialWeekData); await fetchUserSubmissions(tasks); } else { console.error('Failed to fetch tutorial tasks'); } } catch (error) { console.error('Error fetching tutorial tasks:', error); } finally { if (showLoading) { setLoading(false); } } }, [selectedWeek, fetchUserSubmissions, navigate]); useEffect(() => { const user = localStorage.getItem('user'); if (!user) { navigate('/login'); return; } fetchTutorialTasks(); }, [fetchTutorialTasks, navigate]); // Refresh submissions when user changes (after login/logout) useEffect(() => { const user = localStorage.getItem('user'); if (user && tutorialTasks.length > 0) { fetchUserSubmissions(tutorialTasks); } }, [tutorialTasks, fetchUserSubmissions]); const handleSubmitTranslation = async (taskId: string) => { if (!translationText[taskId]?.trim()) { return; } if (!selectedGroups[taskId]) { return; } try { setSubmitting({ ...submitting, [taskId]: true }); const user = JSON.parse(localStorage.getItem('user') || '{}'); const response = await api.post('/submissions', { sourceTextId: taskId, transcreation: translationText[taskId], groupNumber: selectedGroups[taskId], culturalAdaptations: [], username: user.name || 'Unknown' }); if (response.status >= 200 && response.status < 300) { const result = response.data; console.log('Submission created successfully:', result); setTranslationText({ ...translationText, [taskId]: '' }); setSelectedGroups({ ...selectedGroups, [taskId]: 0 }); await fetchUserSubmissions(tutorialTasks); } else { console.error('Failed to submit translation:', response.data); } } catch (error) { console.error('Error submitting translation:', error); } finally { setSubmitting({ ...submitting, [taskId]: false }); } }; const [editingSubmission, setEditingSubmission] = useState<{id: string, text: string} | null>(null); const [editSubmissionText, setEditSubmissionText] = useState(''); const handleEditSubmission = async (submissionId: string, currentText: string) => { setEditingSubmission({ id: submissionId, text: currentText }); setEditSubmissionText(currentText); }; const saveEditedSubmission = async () => { if (!editingSubmission || !editSubmissionText.trim()) return; try { const response = await api.put(`/submissions/${editingSubmission.id}`, { transcreation: editSubmissionText }); if (response.status >= 200 && response.status < 300) { setEditingSubmission(null); setEditSubmissionText(''); await fetchUserSubmissions(tutorialTasks); } else { console.error('Failed to update translation:', response.data); } } catch (error) { console.error('Error updating translation:', error); } }; const cancelEditSubmission = () => { setEditingSubmission(null); setEditSubmissionText(''); }; const handleDeleteSubmission = async (submissionId: string) => { try { const response = await api.delete(`/submissions/${submissionId}`); if (response.status === 200) { await fetchUserSubmissions(tutorialTasks); } else { } } catch (error) { console.error('Error deleting submission:', error); } }; const getStatusIcon = (status: string) => { switch (status) { case 'approved': return ; case 'pending': return ; default: return ; } }; const startEditing = (task: TutorialTask) => { setEditingTask(task._id); setEditForm({ content: task.content, translationBrief: task.translationBrief || '', imageUrl: task.imageUrl || '', imageAlt: task.imageAlt || '' }); }; const startEditingBrief = () => { setEditingBrief(prev => ({ ...prev, [selectedWeek]: true })); setEditForm({ content: '', translationBrief: tutorialWeek?.translationBrief || '', imageUrl: '', imageAlt: '' }); }; const startAddingBrief = () => { setEditingBrief(prev => ({ ...prev, [selectedWeek]: true })); setEditForm({ content: '', translationBrief: '', imageUrl: '', imageAlt: '' }); }; const removeBrief = async () => { try { setSaving(true); const token = localStorage.getItem('token'); const user = JSON.parse(localStorage.getItem('user') || '{}'); // Check if user is admin if (user.role !== 'admin') { return; } const response = await api.put(`/auth/admin/tutorial-brief/${selectedWeek}`, { translationBrief: '', weekNumber: selectedWeek }); if (response.status >= 200 && response.status < 300) { await fetchTutorialTasks(); } else { console.error('Failed to remove translation brief:', response.data); } } catch (error) { console.error('Failed to remove translation brief:', error); } finally { setSaving(false); } }; const cancelEditing = () => { setEditingTask(null); setEditingBrief(prev => ({ ...prev, [selectedWeek]: false })); setEditForm({ content: '', translationBrief: '', imageUrl: '', imageAlt: '' }); setSelectedFile(null); }; const saveTask = async () => { if (!editingTask) return; try { setSaving(true); const token = localStorage.getItem('token'); const user = JSON.parse(localStorage.getItem('user') || '{}'); // Check if user is admin if (user.role !== 'admin') { return; } const updateData = { ...editForm, weekNumber: selectedWeek }; console.log('Saving task with data:', updateData); const response = await api.put(`/auth/admin/tutorial-tasks/${editingTask}`, updateData); if (response.status >= 200 && response.status < 300) { await fetchTutorialTasks(false); setEditingTask(null); } else { console.error('Failed to update tutorial task:', response.data); } } catch (error) { console.error('Failed to update tutorial task:', error); } finally { setSaving(false); } }; const saveBrief = async () => { try { setSaving(true); const user = JSON.parse(localStorage.getItem('user') || '{}'); // Check if user is admin if (user.role !== 'admin') { return; } console.log('Saving brief for week:', selectedWeek); console.log('Brief content:', editForm.translationBrief); // Save brief by creating or updating the first task of the week if (tutorialTasks.length > 0) { const firstTask = tutorialTasks[0]; console.log('Updating first task with brief:', firstTask._id); const response = await api.put(`/auth/admin/tutorial-tasks/${firstTask._id}`, { ...firstTask, translationBrief: editForm.translationBrief, weekNumber: selectedWeek }); if (response.status >= 200 && response.status < 300) { console.log('Brief saved successfully'); await fetchTutorialTasks(false); setEditingBrief(prev => ({ ...prev, [selectedWeek]: false })); } else { console.error('Failed to save brief:', response.data); } } else { // If no tasks exist, save the brief to localStorage console.log('No tasks available to save brief to - saving to localStorage'); const briefKey = `translationBrief_week_${selectedWeek}`; localStorage.setItem(briefKey, editForm.translationBrief); setEditingBrief(prev => ({ ...prev, [selectedWeek]: false })); } } catch (error) { console.error('Failed to update translation brief:', error); } finally { setSaving(false); } }; const startAddingTask = () => { setAddingTask(true); setEditForm({ content: '', translationBrief: '', imageUrl: '', imageAlt: '' }); }; const cancelAddingTask = () => { setAddingTask(false); setEditForm({ content: '', translationBrief: '', imageUrl: '', imageAlt: '' }); setSelectedFile(null); }; const saveNewTask = async () => { try { setSaving(true); const user = JSON.parse(localStorage.getItem('user') || '{}'); // Check if user is admin if (user.role !== 'admin') { return; } if (!editForm.content.trim()) { return; } console.log('Saving new task for week:', selectedWeek); console.log('Task content:', editForm.content); console.log('Image URL:', editForm.imageUrl); console.log('Image Alt:', editForm.imageAlt); const taskData = { title: `Week ${selectedWeek} Tutorial Task`, content: editForm.content, sourceLanguage: 'English', weekNumber: selectedWeek, category: 'tutorial', imageUrl: editForm.imageUrl || null, imageAlt: editForm.imageAlt || null }; console.log('Task data being sent:', JSON.stringify(taskData, null, 2)); console.log('Sending task data:', taskData); const response = await api.post('/auth/admin/tutorial-tasks', taskData); console.log('Task save response:', response.data); if (response.status >= 200 && response.status < 300) { console.log('Task saved successfully'); console.log('Saved task response:', response.data); console.log('Saved task response keys:', Object.keys(response.data || {})); console.log('Saved task response.task:', response.data?.task); console.log('Saved task response.task.imageUrl:', response.data?.task?.imageUrl); console.log('Saved task response.task.translationBrief:', response.data?.task?.translationBrief); await fetchTutorialTasks(false); setAddingTask(false); } else { console.error('Failed to add tutorial task:', response.data); } } catch (error) { console.error('Failed to add tutorial task:', error); } finally { setSaving(false); } }; const deleteTask = async (taskId: string) => { try { const token = localStorage.getItem('token'); const user = JSON.parse(localStorage.getItem('user') || '{}'); // Check if user is admin if (user.role !== 'admin') { return; } const response = await api.delete(`/auth/admin/tutorial-tasks/${taskId}`); if (response.status >= 200 && response.status < 300) { await fetchTutorialTasks(false); } else { console.error('Failed to delete tutorial task:', response.data); } } catch (error) { console.error('Failed to delete tutorial task:', error); } }; // Remove intrusive loading screen - just show content with loading state return (
{/* Header */}

Tutorial Tasks

Complete weekly tutorial tasks with your group to practice collaborative translation skills.

{/* Week Selector */}
{weeks.map((week) => ( ))}
{/* Translation Brief - Shown once at the top */} {tutorialWeek && tutorialWeek.translationBrief ? (

Translation Brief

{JSON.parse(localStorage.getItem('user') || '{}').role === 'admin' && (
{editingBrief[selectedWeek] ? ( <> ) : ( <> )}
)}
{editingBrief[selectedWeek] ? (