import React, { useState, useEffect } from 'react'; import { participantAPI } from '../../../services/api.service'; const ForumScriptDemo = ({ lessonData, eventLessonId }) => { const interactiveData = lessonData?.interactiveData || {}; const forumPost = interactiveData.forumPost || {}; const initialComments = interactiveData.initialComments || []; const freeHints = interactiveData.freeHints || []; const timeLimit = interactiveData.timeLimit || 900000; // 15 min default const [comments, setComments] = useState(initialComments); const [authorName, setAuthorName] = useState(''); const [commentText, setCommentText] = useState(''); const [loading, setLoading] = useState(false); const [remainingTime, setRemainingTime] = useState(null); const [timerStarted, setTimerStarted] = useState(false); const [currentHint, setCurrentHint] = useState(null); const [progress, setProgress] = useState({ discovered: 0, total: 9, remaining: 9 }); // Start timer on mount useEffect(() => { const startTimer = async () => { try { const response = await participantAPI.executeLessonAction( eventLessonId, 'start-timer', { stepId: 'forum-demo' } ); setTimerStarted(true); setRemainingTime(timeLimit); } catch (error) { console.error('Failed to start timer:', error); } }; startTimer(); }, [eventLessonId, timeLimit]); // Timer countdown useEffect(() => { if (remainingTime === null || remainingTime <= 0) return; const interval = setInterval(() => { setRemainingTime(prev => { if (prev <= 1000) { clearInterval(interval); return 0; } return prev - 1000; }); }, 1000); return () => clearInterval(interval); }, [remainingTime]); const formatTime = (ms) => { if (ms === null) return '--:--'; const minutes = Math.floor(ms / 60000); const seconds = Math.floor((ms % 60000) / 1000); return `${minutes}:${seconds.toString().padStart(2, '0')}`; }; const addComment = async () => { if (!commentText.trim()) return; setLoading(true); try { const response = await participantAPI.executeLessonAction( eventLessonId, 'add-comment', { author: authorName || 'Anonym', content: commentText, stepId: 'forum-demo' } ); const newComment = response.data.data; setComments([...comments, newComment]); setCommentText(''); // Update progress if (newComment.progress) { setProgress(newComment.progress); } // Update remaining time from server if (newComment.remainingTime !== undefined) { setRemainingTime(newComment.remainingTime); } // Scroll to bottom to show new comment setTimeout(() => { const commentList = document.getElementById('comment-list'); if (commentList) { commentList.scrollTop = commentList.scrollHeight; } }, 100); } catch (error) { console.error('Failed to add comment:', error); } finally { setLoading(false); } }; const requestHint = async () => { try { const response = await participantAPI.executeLessonAction( eventLessonId, 'get-hint', { stepId: 'forum-demo' } ); setCurrentHint(response.data.data); } catch (error) { console.error('Failed to get hint:', error); } }; const reloadForum = () => { setComments(initialComments); setAuthorName(''); setCommentText(''); }; const formatTimestamp = (timestamp) => { if (!timestamp) return 'gerade eben'; const date = new Date(timestamp); return date.toLocaleString('de-DE', { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }); }; const timeExpired = remainingTime === 0; const progressPercent = (progress.discovered / progress.total) * 100; return (
{/* Educational Warning */}
⚠️ Nur zu Lehrzwecken
Dieses Forum demonstriert Stored-XSS-Schwachstellen. Es werden keine tatsächlichen Skripte ausgeführt - sie werden sicher als Text mit klaren Warnungen angezeigt.

💬 Anfälliges Forum Demo

{/* Progress and Timer Bar */}
{/* Progress Tracker */}
🎯 Fortschritt
{progress.discovered} / {progress.total}
Varianten entdeckt
{/* Progress bar */}
{/* Timer */}
⏱️ Verbleibende Zeit
{formatTime(remainingTime)}
{timeExpired && (
⚠️ Keine Punkte mehr verfügbar
)} {!timeExpired && (
Punkte verdienbar
)}
{/* Free Hints */}
💡 Hinweise (kostenlos)
{/* Hint Request Button */}
{currentHint && !currentHint.noMoreHints && (
Hinweis #{currentHint.hintsUsed}
{currentHint.hint}
Abgezogene Punkte: {currentHint.totalPointsDeducted}
)} {currentHint && currentHint.noMoreHints && (
Keine weiteren Hinweise verfügbar
)}
{/* Forum Post */}
A
{forumPost.title}
Gepostet von {forumPost.author} • {formatTimestamp(forumPost.timestamp)}
{forumPost.content}
{/* Comments List */}
Kommentare ({comments.length})
{comments.map((comment, idx) => (
{comment.author?.charAt(0).toUpperCase() || '?'}
{comment.author}
{formatTimestamp(comment.timestamp)}
{/* Comment Content - safely displayed */}
{comment.content}
{/* Injection Warning */} {comment.hasInjection && (
⚠️ XSS ERKANNT: {comment.injectionType}
{comment.injectionDescription}
Bereinigt: {comment.sanitizedContent}
{comment.isNewDiscovery && (
🎉 Neue Variante entdeckt! +{timeExpired ? '0' : '10'} Punkte
)}
)}
))}
{/* Add Comment Form */}
Kommentar hinzufügen
setAuthorName(e.target.value)} placeholder="Ihr Name (optional)" style={{ width: '100%', padding: '0.5rem', border: '1px solid #d1d5db', borderRadius: '0.375rem', fontSize: '0.875rem' }} />