fix errors

This commit is contained in:
2026-06-16 13:58:33 +03:00
parent da596fd2d8
commit 45f4c143c2
3 changed files with 351 additions and 40 deletions
+38 -17
View File
@@ -19,7 +19,7 @@ const App = () => {
const [currentAnswerResult, setCurrentAnswerResult] = useState(null); const [currentAnswerResult, setCurrentAnswerResult] = useState(null);
const [otherPlayersResults, setOtherPlayersResults] = useState([]); const [otherPlayersResults, setOtherPlayersResults] = useState([]);
// Новые состояния для лобби //состояния для лобби
const [showLobby, setShowLobby] = useState(true); const [showLobby, setShowLobby] = useState(true);
const [currentPlayer, setCurrentPlayer] = useState(null); const [currentPlayer, setCurrentPlayer] = useState(null);
const [players, setPlayers] = useState([]); const [players, setPlayers] = useState([]);
@@ -381,22 +381,43 @@ const App = () => {
if (quizCompleted) { if (quizCompleted) {
const score = calculateScore(); const score = calculateScore();
// Формируем массив всех игроков для таблицы лидеров
const allPlayersScores = [ const allPlayersScores = [
{ ...currentPlayer, isCurrent: true }, {
...players.filter(p => p.id !== currentPlayer.id) ...currentPlayer,
].sort((a, b) => b.score - a.score); isCurrent: true,
totalAnswers: userAnswers.filter(a => a && !a.timeExpired).length,
correctAnswers: userAnswers.filter(a => {
if (!a || a.timeExpired) return false;
if (a.id) {
const question = quizData.questions[userAnswers.indexOf(a)];
return question.answers.find(ans => ans.id === a.id)?.isCorrect;
}
return false;
}).length
},
...players
.filter(p => p.id !== currentPlayer.id)
.map(p => ({
...p,
isCurrent: false,
totalAnswers: 0, // В реальном приложении здесь были бы данные
correctAnswers: 0
}))
].sort((a, b) => (b.score || 0) - (a.score || 0));
return ( return (
<QuizResults <QuizResults
score={score} score={score}
totalQuestions={quizData.questions.length} totalQuestions={quizData.questions.length}
userAnswers={userAnswers} userAnswers={userAnswers}
quizData={quizData} quizData={quizData}
onRestart={handleRestart} onRestart={handleRestart}
timeExpiredQuestions={timeExpiredQuestions} timeExpiredQuestions={timeExpiredQuestions}
allPlayers={allPlayersScores} allPlayers={allPlayersScores}
currentPlayer={currentPlayer} currentPlayer={currentPlayer}
/> />
); );
} }
@@ -466,11 +487,11 @@ const App = () => {
)} )}
<div className="navigation-buttons"> <div className="navigation-buttons">
{hasPrevious && !isTimeExpired && ( {/*hasPrevious && !isTimeExpired && (
<button onClick={handlePrevious} className="nav-button prev"> <button onClick={handlePrevious} className="nav-button prev">
← Назад ← Назад
</button> </button>
)} )*/}
</div> </div>
</div> </div>
); );
+228
View File
@@ -130,4 +130,232 @@
.score-number { .score-number {
font-size: 36px; font-size: 36px;
} }
}
.leaderboard-section {
margin: 30px 0;
padding: 20px;
background: #f8f9fa;
border-radius: 15px;
}
.leaderboard-section h3 {
color: #333;
margin-bottom: 20px;
text-align: center;
font-size: 1.3rem;
}
.leaderboard {
display: flex;
flex-direction: column;
gap: 10px;
}
.leaderboard-item {
display: flex;
align-items: center;
padding: 15px 20px;
background: white;
border-radius: 12px;
transition: all 0.3s ease;
border: 2px solid transparent;
gap: 15px;
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
}
.leaderboard-item:hover {
transform: translateX(5px);
box-shadow: 0 4px 15px rgba(0,0,0,0.1);
}
.leaderboard-item.current-player {
border-color: #667eea;
background: linear-gradient(90deg, #e8eaf6 0%, #ffffff 100%);
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.2);
}
.leaderboard-item.first {
background: linear-gradient(90deg, #fff3cd 0%, #ffffff 100%);
border-color: #ffc107;
}
.leaderboard-item.second {
background: linear-gradient(90deg, #e8e8e8 0%, #ffffff 100%);
border-color: #c0c0c0;
}
.leaderboard-item.third {
background: linear-gradient(90deg, #ffe0b0 0%, #ffffff 100%);
border-color: #cd7f32;
}
.leaderboard-rank {
min-width: 50px;
font-size: 1.5rem;
text-align: center;
}
.rank-number {
display: inline-block;
}
.leaderboard-avatar {
font-size: 2.5rem;
width: 50px;
text-align: center;
}
.leaderboard-info {
flex: 1;
min-width: 0;
}
.leaderboard-name {
font-weight: bold;
color: #333;
font-size: 1.1rem;
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
}
.host-badge {
background: #ff9800;
color: white;
padding: 2px 8px;
border-radius: 12px;
font-size: 0.7rem;
font-weight: normal;
}
.you-badge {
background: #667eea;
color: white;
padding: 2px 8px;
border-radius: 12px;
font-size: 0.7rem;
font-weight: normal;
}
.leaderboard-stats {
display: flex;
gap: 15px;
margin-top: 4px;
flex-wrap: wrap;
}
.stat-item {
font-size: 0.8rem;
color: #666;
}
.leaderboard-score {
text-align: right;
min-width: 80px;
}
.score-value {
display: block;
font-size: 1.8rem;
font-weight: bold;
color: #333;
line-height: 1;
}
.leaderboard-item.first .score-value {
color: #ffc107;
}
.leaderboard-item.second .score-value {
color: #c0c0c0;
}
.leaderboard-item.third .score-value {
color: #cd7f32;
}
.leaderboard-item.current-player .score-value {
color: #667eea;
}
.score-label {
font-size: 0.7rem;
color: #999;
display: block;
margin-top: 2px;
}
/* Точки за ответ */
.points-earned {
margin-top: 5px;
color: #4caf50;
font-weight: 500;
}
.points-earned strong {
color: #333;
}
/* Адаптивность для таблицы лидеров */
@media (max-width: 768px) {
.leaderboard-item {
flex-wrap: wrap;
padding: 12px 15px;
}
.leaderboard-rank {
min-width: 35px;
font-size: 1.2rem;
}
.leaderboard-avatar {
font-size: 2rem;
width: 40px;
}
.leaderboard-info {
width: 100%;
order: 3;
margin-top: 5px;
}
.leaderboard-score {
margin-left: auto;
min-width: 60px;
}
.score-value {
font-size: 1.4rem;
}
.leaderboard-stats {
font-size: 0.7rem;
gap: 10px;
}
}
/* Анимация появления */
.leaderboard-item {
animation: slideInLeft 0.5s ease forwards;
opacity: 0;
}
.leaderboard-item:nth-child(1) { animation-delay: 0.1s; }
.leaderboard-item:nth-child(2) { animation-delay: 0.2s; }
.leaderboard-item:nth-child(3) { animation-delay: 0.3s; }
.leaderboard-item:nth-child(4) { animation-delay: 0.4s; }
.leaderboard-item:nth-child(5) { animation-delay: 0.5s; }
.leaderboard-item:nth-child(6) { animation-delay: 0.6s; }
@keyframes slideInLeft {
from {
opacity: 0;
transform: translateX(-20px);
}
to {
opacity: 1;
transform: translateX(0);
}
} }
+85 -23
View File
@@ -1,7 +1,16 @@
import React from 'react'; import React from 'react';
import './QuizResults.css'; import './QuizResults.css';
const QuizResults = ({ score, totalQuestions, userAnswers, quizData, onRestart, timeExpiredQuestions }) => { const QuizResults = ({
score,
totalQuestions,
userAnswers,
quizData,
onRestart,
timeExpiredQuestions,
allPlayers = [], // Добавляем allPlayers с значением по умолчанию
currentPlayer = null
}) => {
const percentage = (score / totalQuestions) * 100; const percentage = (score / totalQuestions) * 100;
let resultMessage = ""; let resultMessage = "";
@@ -42,7 +51,7 @@ const QuizResults = ({ score, totalQuestions, userAnswers, quizData, onRestart,
}; };
const getAnswerStatus = (question, answer, index) => { const getAnswerStatus = (question, answer, index) => {
if (timeExpiredQuestions.includes(index)) { if (timeExpiredQuestions?.includes(index)) {
return "expired"; return "expired";
} }
if (question.type === "single" && answer && !answer.timeExpired) { if (question.type === "single" && answer && !answer.timeExpired) {
@@ -55,11 +64,28 @@ const QuizResults = ({ score, totalQuestions, userAnswers, quizData, onRestart,
return "unanswered"; return "unanswered";
}; };
// Функция для получения эмодзи медали
const getMedalEmoji = (index) => {
if (index === 0) return "🥇";
if (index === 1) return "🥈";
if (index === 2) return "🥉";
return `#${index + 1}`;
};
// Функция для получения класса места
const getRankClass = (index) => {
if (index === 0) return "first";
if (index === 1) return "second";
if (index === 2) return "third";
return "other";
};
return ( return (
<div className="results-container"> <div className="results-container">
<div className="results-card"> <div className="results-card">
<h2>Ваши результаты</h2> <h2>🏆 Результаты викторины</h2>
{/* Основной результат */}
<div className={`score-circle ${resultClass}`}> <div className={`score-circle ${resultClass}`}>
<div className="score-number">{score}</div> <div className="score-number">{score}</div>
<div className="score-total">/{totalQuestions}</div> <div className="score-total">/{totalQuestions}</div>
@@ -68,17 +94,62 @@ const QuizResults = ({ score, totalQuestions, userAnswers, quizData, onRestart,
<div className="result-message"> <div className="result-message">
<p>{resultMessage}</p> <p>{resultMessage}</p>
<p className="percentage">Правильных ответов: {Math.round(percentage)}%</p> <p className="percentage">Правильных ответов: {Math.round(percentage)}%</p>
{timeExpiredQuestions.length > 0 && ( {timeExpiredQuestions?.length > 0 && (
<p className="time-warning"> <p className="time-warning">
На {timeExpiredQuestions.length} вопрос{(timeExpiredQuestions.length > 1 ? 'ах' : 'е')} время истекло На {timeExpiredQuestions.length} вопрос{(timeExpiredQuestions.length > 1 ? 'ах' : 'е')} время истекло
</p> </p>
)} )}
{currentPlayer && (
<p className="total-points">
🎯 Всего очков: <strong>{currentPlayer.score || 0}</strong>
</p>
)}
</div> </div>
{/* Таблица лидеров */}
{allPlayers && allPlayers.length > 0 && (
<div className="leaderboard-section">
<h3>👑 Таблица лидеров</h3>
<div className="leaderboard">
{allPlayers.map((player, index) => (
<div
key={player.id || index}
className={`leaderboard-item ${getRankClass(index)} ${player.isCurrent ? 'current-player' : ''}`}
>
<div className="leaderboard-rank">
<span className="rank-number">{getMedalEmoji(index)}</span>
</div>
<div className="leaderboard-avatar">{player.avatar || "👤"}</div>
<div className="leaderboard-info">
<div className="leaderboard-name">
{player.name || "Игрок"}
{player.isHost && <span className="host-badge">👑 Хост</span>}
{player.isCurrent && <span className="you-badge">Вы</span>}
</div>
<div className="leaderboard-stats">
<span className="stat-item">
{player.correctAnswers || 0} правильных
</span>
<span className="stat-item">
📝 {player.totalAnswers || 0} ответов
</span>
</div>
</div>
<div className="leaderboard-score">
<span className="score-value">{player.score || 0}</span>
<span className="score-label">очков</span>
</div>
</div>
))}
</div>
</div>
)}
{/* Обзор ответов */}
<div className="answers-review"> <div className="answers-review">
<h3>Обзор ответов:</h3> <h3>📋 Обзор ваших ответов:</h3>
{quizData.questions.map((question, index) => { {quizData && quizData.questions && quizData.questions.map((question, index) => {
const userAnswer = userAnswers[index]; const userAnswer = userAnswers?.[index];
const status = getAnswerStatus(question, userAnswer, index); const status = getAnswerStatus(question, userAnswer, index);
return ( return (
@@ -94,7 +165,7 @@ const QuizResults = ({ score, totalQuestions, userAnswers, quizData, onRestart,
<div className="user-answer"> <div className="user-answer">
<strong>Ваш ответ:</strong> {getUserAnswerText(question, userAnswer, index)} <strong>Ваш ответ:</strong> {getUserAnswerText(question, userAnswer, index)}
</div> </div>
{status !== "expired" && status !== "pending" && ( {status !== "expired" && status !== "pending" && status !== "unanswered" && (
<div className="correct-answer"> <div className="correct-answer">
<strong>Правильный ответ:</strong> {getCorrectAnswerText(question, index)} <strong>Правильный ответ:</strong> {getCorrectAnswerText(question, index)}
</div> </div>
@@ -104,25 +175,16 @@ const QuizResults = ({ score, totalQuestions, userAnswers, quizData, onRestart,
<strong> Время на ответ:</strong> {userAnswer.timeSpent} сек <strong> Время на ответ:</strong> {userAnswer.timeSpent} сек
</div> </div>
)} )}
{userAnswer?.pointsEarned > 0 && (
<div className="points-earned">
<strong>🎯 Очков получено:</strong> +{userAnswer.pointsEarned}
</div>
)}
</div> </div>
</div> </div>
); );
})} })}
</div> </div>
<div className="leaderboard">
<h3>🏆 Итоговая таблица лидеров</h3>
{allPlayers.map((player, index) => (
<div key={player.id} className={`leaderboard-item ${player.isCurrent ? 'current' : ''}`}>
<div className="leaderboard-rank">{index + 1}</div>
<div className="leaderboard-avatar">{player.avatar}</div>
<div className="leaderboard-name">
{player.name}
{player.isCurrent && <span className="you-badge">(Вы)</span>}
</div>
<div className="leaderboard-score">{player.score} очков</div>
</div>
))}
</div>
<button onClick={onRestart} className="restart-button"> <button onClick={onRestart} className="restart-button">
Пройти викторину снова 🔄 Пройти викторину снова 🔄