Colors keep their role: headers/banners darken to readable contrast (same hue), lessons uses the teal token, packages use indigo; +XP pop, streak milestone card, lazy confetti

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-08 16:26:07 +03:30
parent dc1fb15a55
commit 54bd865835
7 changed files with 66 additions and 12 deletions

View File

@@ -1,6 +1,5 @@
import { useEffect, useRef, useState } from 'react';
import { AnimatePresence, motion } from 'motion/react';
import confetti from 'canvas-confetti';
import { Brain, Check, ChevronLeft, Coins, Flag, Heart, Infinity as InfinityIcon, Star, X } from 'lucide-react';
import { api, ApiError, type AnswerResult, type Hearts, type Question, type ReportReason } from '@/api';
import { useAppData } from '@/state/AppData';
@@ -165,7 +164,7 @@ export function QuizScreen({ lessonId, nodeId }: { lessonId?: number; nodeId?: s
: await api.lesson.complete(lessonId, payload, completeKey.current);
setPhase({ kind: 'result', result });
sounds.finish();
if (result.stars >= 2) void confetti({ particleCount: 90, spread: 70, origin: { y: 0.35 } });
if (result.stars >= 2) void import('canvas-confetti').then(({ default: confetti }) => confetti({ particleCount: 90, spread: 70, origin: { y: 0.35 } }));
void refreshHome();
void refreshSummary();
} catch (error) {
@@ -283,6 +282,17 @@ export function QuizScreen({ lessonId, nodeId }: { lessonId?: number; nodeId?: s
!answer ? 'border-line bg-white text-indigo' : answer.correct ? 'border-green-200 bg-green-50 text-green-700' : 'border-purple-200 bg-purple-50 text-[#5B3FA0]'
}`}>
{bubble}
{answer?.correct && answer.xpEarned > 0 && (
<motion.span
key={question.id}
initial={{ opacity: 0, y: 8, scale: 0.8 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
transition={{ type: 'spring', stiffness: 400, damping: 14 }}
className="mr-2 inline-block rounded-full bg-[#FFF3C4] px-2 py-0.5 text-[11px] font-black text-[#8A5A00]"
>
+{toFa(answer.xpEarned)} ⭐
</motion.span>
)}
</div>
</div>