From 45b81d9acbacd6c667b43b3c9ab935097571f61f Mon Sep 17 00:00:00 2001 From: Lesia Soloviova Date: Tue, 13 Jan 2026 22:26:34 +0200 Subject: [PATCH 1/2] feat(quiz): replace emojis with lucide-react icons --- frontend/components/quiz/CountdownTimer.tsx | 3 +- frontend/components/quiz/QuizCard.tsx | 17 +++--- frontend/components/quiz/QuizContainer.tsx | 12 ++--- frontend/components/quiz/QuizProgress.tsx | 3 +- frontend/components/quiz/QuizQuestion.tsx | 7 +-- frontend/components/quiz/QuizResult.tsx | 57 +++++++++++---------- 6 files changed, 54 insertions(+), 45 deletions(-) diff --git a/frontend/components/quiz/CountdownTimer.tsx b/frontend/components/quiz/CountdownTimer.tsx index 01bb05c2..e4fb3fb5 100644 --- a/frontend/components/quiz/CountdownTimer.tsx +++ b/frontend/components/quiz/CountdownTimer.tsx @@ -3,6 +3,7 @@ import { useEffect, useState } from 'react'; import { useTranslations } from 'next-intl'; import { cn } from '@/lib/utils'; +import { AlertTriangle } from 'lucide-react'; interface CountdownTimerProps { timeLimitSeconds: number; @@ -105,7 +106,7 @@ export function CountdownTimer({

{percentage <= 10 ? ( <> - {t('almostDone')} + {t('almostDone')} ) : ( <> diff --git a/frontend/components/quiz/QuizCard.tsx b/frontend/components/quiz/QuizCard.tsx index 2bdd556f..882e2bad 100644 --- a/frontend/components/quiz/QuizCard.tsx +++ b/frontend/components/quiz/QuizCard.tsx @@ -3,6 +3,7 @@ import { useTranslations } from 'next-intl'; import { Link } from '@/i18n/routing'; import { Badge } from '@/components/ui/badge'; +import { FileText, Clock } from 'lucide-react'; interface QuizCardProps { quiz: { @@ -44,12 +45,16 @@ export function QuizCard({ quiz, userProgress }: QuizCardProps) { {quiz.description}

)} -
- 📝 {quiz.questionsCount} {t('questions')} - - ⏱️ {Math.floor((quiz.timeLimitSeconds ?? quiz.questionsCount * 30) / 60)} {t('min')} - -
+
+ + + {quiz.questionsCount} {t('questions')} + + + + {Math.floor((quiz.timeLimitSeconds ?? quiz.questionsCount * 30) / 60)} {t('min')} + +
{userProgress && (
diff --git a/frontend/components/quiz/QuizContainer.tsx b/frontend/components/quiz/QuizContainer.tsx index e00896a3..9bf55be1 100644 --- a/frontend/components/quiz/QuizContainer.tsx +++ b/frontend/components/quiz/QuizContainer.tsx @@ -17,6 +17,7 @@ import { savePendingQuizResult } from '@/lib/quiz/guest-quiz'; import type { QuizQuestionClient } from '@/db/queries/quiz'; import { ConfirmModal } from '@/components/ui/confirm-modal'; import { Button } from '@/components/ui/button'; +import { FileText, Ban, AlertTriangle, Clock } from 'lucide-react'; interface Answer { questionId: string; @@ -380,8 +381,8 @@ const confirmQuit = () => {
-
- 📝 +
+

{tRules('general.title')}

@@ -391,7 +392,7 @@ const confirmQuit = () => {

- 🚫 +

{tRules('forbidden.title')}

    @@ -404,7 +405,7 @@ const confirmQuit = () => {
- ⚠️ +

{tRules('control.title')}

@@ -412,9 +413,8 @@ const confirmQuit = () => {

-
- ⏱️ +

{tRules('time.title')}

diff --git a/frontend/components/quiz/QuizProgress.tsx b/frontend/components/quiz/QuizProgress.tsx index f46ed9fe..694e2382 100644 --- a/frontend/components/quiz/QuizProgress.tsx +++ b/frontend/components/quiz/QuizProgress.tsx @@ -2,6 +2,7 @@ import { useTranslations } from 'next-intl'; import { cn } from '@/lib/utils'; +import { Check, X } from 'lucide-react'; interface Answer { questionId: string; @@ -90,7 +91,7 @@ export function QuizProgress({ current, total, answers }: QuizProgressProps) { > {isAnswered ? ( - {isCorrect ? '✓' : '✗'} + {isCorrect ? : } ) : ( {answer.answerText} {showCorrect && ( - ✓ {t('correct')} + {t('correct')} )} {showIncorrect && ( - ✗ {t('incorrect')} + {t('incorrect')} )} @@ -99,7 +100,7 @@ export function QuizQuestion({ )} >

-
💡
+

{t('recommendation.title')} diff --git a/frontend/components/quiz/QuizResult.tsx b/frontend/components/quiz/QuizResult.tsx index bc3ae888..b432cfea 100644 --- a/frontend/components/quiz/QuizResult.tsx +++ b/frontend/components/quiz/QuizResult.tsx @@ -3,6 +3,7 @@ import { useLocale, useTranslations } from 'next-intl'; import { cn } from '@/lib/utils'; import { Button } from '@/components/ui/button'; +import { Clock, BookOpen, TrendingUp, Trophy, AlertTriangle } from 'lucide-react'; interface QuizResultProps { score: number; @@ -35,49 +36,49 @@ export function QuizResult({ const t = useTranslations('quiz.result'); const getMotivationalMessage = () => { if (isIncomplete && answeredCount > 0) { - return { - emoji: '⏱️', - title: 'Час вийшов', - message: `Ви відповіли на ${answeredCount} з ${total} питань. Результат не зараховано.`, - color: 'text-orange-600 dark:text-orange-400', - }; - } - + return { + icon: , + title: 'Час вийшов', + message: `Ви відповіли на ${answeredCount} з ${total} питань. Результат не зараховано.`, + color: 'text-orange-600 dark:text-orange-400', + }; + } + if (score === 0 && answeredCount === 0) { - return { - emoji: '⏱️', - title: t('timeUp.title'), - message: t('timeUp.message'), - color: 'text-gray-600 dark:text-gray-400', - }; - } + return { + icon: , + title: t('timeUp.title'), + message: t('timeUp.message'), + color: 'text-gray-600 dark:text-gray-400', + }; + } - if (score === 0 && answeredCount > 0) { - return { - emoji: '📚', - title: t('allWrong.title'), - message: t('allWrong.message'), - color: 'text-red-600 dark:text-red-400', - }; - } + if (score === 0 && answeredCount > 0) { + return { + icon: , + title: t('allWrong.title'), + message: t('allWrong.message'), + color: 'text-red-600 dark:text-red-400', + }; + } if (percentage < 50) { return { - emoji: '📚', + icon: , title: t('needPractice.title'), message: t('needPractice.message'), color: 'text-red-600 dark:text-red-400', }; } else if (percentage < 80) { return { - emoji: '💪', + icon: , title: t('goodJob.title'), message: t('goodJob.message'), color: 'text-orange-600 dark:text-orange-400', }; } else { return { - emoji: '🎉', + icon: , title: t('excellent.title'), message: t('excellent.message'), color: 'text-green-600 dark:text-green-400', @@ -89,7 +90,7 @@ export function QuizResult({ return (
-
{motivation.emoji}
+
{motivation.icon}
{!isIncomplete && ( <>
@@ -124,7 +125,7 @@ export function QuizResult({ {violationsCount >= 3 && (

- {t('violations', { count: violationsCount })} + {t('violations', { count: violationsCount })}

)} From c18bbaa3bf3fddfefa6777433ac9ea1933b78e30 Mon Sep 17 00:00:00 2001 From: Lesia Soloviova Date: Tue, 13 Jan 2026 22:54:28 +0200 Subject: [PATCH 2/2] feat(quiz): localize incomplete quiz result --- frontend/components/quiz/QuizResult.tsx | 5 ++--- frontend/messages/en.json | 4 ++++ frontend/messages/pl.json | 4 ++++ frontend/messages/uk.json | 4 ++++ 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/frontend/components/quiz/QuizResult.tsx b/frontend/components/quiz/QuizResult.tsx index b432cfea..06be09a1 100644 --- a/frontend/components/quiz/QuizResult.tsx +++ b/frontend/components/quiz/QuizResult.tsx @@ -38,12 +38,11 @@ export function QuizResult({ if (isIncomplete && answeredCount > 0) { return { icon: , - title: 'Час вийшов', - message: `Ви відповіли на ${answeredCount} з ${total} питань. Результат не зараховано.`, + title: t('incomplete.title'), + message: t('incomplete.message', { answeredCount, total }), color: 'text-orange-600 dark:text-orange-400', }; } - if (score === 0 && answeredCount === 0) { return { icon: , diff --git a/frontend/messages/en.json b/frontend/messages/en.json index cd8cb5f0..0560cc1a 100644 --- a/frontend/messages/en.json +++ b/frontend/messages/en.json @@ -130,6 +130,10 @@ "title": "Excellent Work!", "message": "You have mastered the material well" }, + "incomplete": { + "title": "Time's up", + "message": "You answered {answeredCount} out of {total} questions. Result not counted." + }, "violations": "Quiz completed with rule violations ({count} violations). Result not counted towards leaderboard.", "pointsAwarded": "+{points} points added to rating", "noPointsAwarded": "No points awarded (result not improved)", diff --git a/frontend/messages/pl.json b/frontend/messages/pl.json index fec2827b..b6c136ad 100644 --- a/frontend/messages/pl.json +++ b/frontend/messages/pl.json @@ -130,6 +130,10 @@ "title": "Doskonała Praca!", "message": "Dobrze opanowałeś materiał" }, + "incomplete": { + "title": "Czas minął", + "message": "Odpowiedzi udzielono na {answeredCount} z {total} pytań. Wynik nie został zaliczony." + }, "violations": "Quiz ukończony z naruszeniami zasad ({count} naruszeń). Wynik nie zaliczony do rankingu.", "pointsAwarded": "+{points} punktów dodanych do oceny", "noPointsAwarded": "Nie przyznano punktów (wynik nie poprawiony)", diff --git a/frontend/messages/uk.json b/frontend/messages/uk.json index 44613053..48ebaca5 100644 --- a/frontend/messages/uk.json +++ b/frontend/messages/uk.json @@ -130,6 +130,10 @@ "title": "Чудова робота!", "message": "Ви добре засвоїли матеріал" }, + "incomplete": { + "title": "Час вийшов", + "message": "Ви відповіли на {answeredCount} з {total} питань. Результат не зараховано." + }, "violations": "Квіз завершено з порушеннями правил ({count} порушень). Результат не зараховано до рейтингу.", "pointsAwarded": "+{points} балів додано до рейтингу", "noPointsAwarded": "Бали не нараховано (результат не покращено)",