1
0
mirror of https://github.com/kamranahmedse/developer-roadmap.git synced 2025-09-10 01:00:42 +02:00
This commit is contained in:
Arik Chakma
2025-07-03 02:14:29 +06:00
parent 920d0512f6
commit e7e1c1c8d5
2 changed files with 58 additions and 54 deletions

View File

@@ -1,21 +1,15 @@
import { import {
CheckCircle,
PartyPopper, PartyPopper,
RefreshCcw,
RotateCcw, RotateCcw,
XCircle, XCircle,
SkipForward, SkipForward,
CheckCircle2Icon, CheckCircle2Icon,
PlusIcon, PlusIcon,
CheckCircleIcon,
XCircleIcon,
SkipForwardIcon,
CheckIcon,
XIcon,
} from 'lucide-react'; } from 'lucide-react';
import type { QuestionState } from './AIQuizContent'; import type { QuestionState } from './AIQuizContent';
import { getPercentage } from '../../lib/number'; import { getPercentage } from '../../lib/number';
import { cn } from '../../lib/classname'; import { cn } from '../../lib/classname';
import { QuizStateButton } from './AIQuizStripe';
type AIQuizResultsProps = { type AIQuizResultsProps = {
questionStates: Record<number, QuestionState>; questionStates: Record<number, QuestionState>;
@@ -61,27 +55,15 @@ export function AIQuizResults(props: AIQuizResultsProps) {
<div className="mb-6 grid w-full grid-cols-5 gap-2"> <div className="mb-6 grid w-full grid-cols-5 gap-2">
{states.map((state, quizIndex) => { {states.map((state, quizIndex) => {
const { status } = state;
const isCorrect = status === 'correct';
const isIncorrect = status === 'incorrect';
const isSkipped = status === 'skipped';
return ( return (
<button <QuizStateButton
key={quizIndex} key={quizIndex}
onClick={() => onReview?.(quizIndex)} state={state}
className={cn( quizIndex={quizIndex}
'flex aspect-square flex-col items-center justify-center rounded-xl border border-gray-200 p-2 hover:opacity-80', isActive={true}
isCorrect && 'bg-green-700 text-white', onReview={onReview}
isIncorrect && 'bg-red-700 text-white', className="p-2"
isSkipped && 'bg-gray-700 text-white', />
)}
>
{isCorrect && <CheckIcon className="h-6 w-6" />}
{isIncorrect && <XIcon className="h-6 w-6" />}
{isSkipped && <SkipForwardIcon className="h-6 w-6" />}
</button>
); );
})} })}
</div> </div>

View File

@@ -2,6 +2,7 @@ import { cn } from '../../lib/classname';
import { import {
ArrowRightIcon, ArrowRightIcon,
CheckIcon, CheckIcon,
CircleAlertIcon,
SkipForwardIcon, SkipForwardIcon,
XIcon, XIcon,
} from 'lucide-react'; } from 'lucide-react';
@@ -14,48 +15,69 @@ type AIQuizStripeProps = {
onComplete?: () => void; onComplete?: () => void;
}; };
type QuizStateButtonProps = {
state: QuestionState;
quizIndex: number;
isActive: boolean;
onReview?: (questionIndex: number) => void;
className?: string;
};
export function QuizStateButton(props: QuizStateButtonProps) {
const { state, quizIndex, isActive, onReview, className } = props;
const { status } = state;
const isCorrect = status === 'correct';
const isIncorrect = status === 'incorrect';
const isSkipped = status === 'skipped';
const isCanBeImproved = status === 'can_be_improved';
return (
<button
key={quizIndex}
onClick={() => onReview?.(quizIndex)}
className={cn(
'flex aspect-square flex-col items-center justify-center rounded-xl border p-1 hover:opacity-80',
isCorrect && 'border-green-700 bg-green-700 text-white',
isIncorrect && 'border-red-700 bg-red-700 text-white',
isSkipped && 'border-gray-700 bg-gray-700 text-white',
isCanBeImproved && 'border-yellow-700 bg-yellow-700 text-white',
!isActive && 'opacity-50',
className,
)}
>
{isCorrect && <CheckIcon className="h-6 w-6" />}
{isIncorrect && <XIcon className="h-6 w-6" />}
{isSkipped && <SkipForwardIcon className="h-6 w-6" />}
{isCanBeImproved && <CircleAlertIcon className="h-6 w-6" />}
</button>
);
}
export function AIQuizStripe(props: AIQuizStripeProps) { export function AIQuizStripe(props: AIQuizStripeProps) {
const { activeQuestionIndex, questionStates, onReview, onComplete } = props; const { activeQuestionIndex, questionStates, onReview, onComplete } = props;
const states = Object.values(questionStates); const states = Object.values(questionStates);
return ( return (
<div className="fixed right-0 bottom-0 w-[calc(100vw-255px)] border-t border-gray-200 bg-white p-3"> <div className="fixed right-0 bottom-0 w-[calc(100vw-255px)] border-t border-gray-200 bg-white p-3">
<div className="flex items-center justify-between gap-2"> <div className="flex items-center justify-between gap-2">
<div className="flex w-full gap-2"> <div className="flex w-full gap-2">
{states.map((state, quizIndex) => { {states.map((state, quizIndex) => (
const { status } = state; <QuizStateButton
key={quizIndex}
const isActive = quizIndex === activeQuestionIndex; state={state}
const isCorrect = status === 'correct'; quizIndex={quizIndex}
const isIncorrect = status === 'incorrect'; isActive={quizIndex === activeQuestionIndex}
const isSkipped = status === 'skipped'; onReview={onReview}
/>
return ( ))}
<button
key={quizIndex}
onClick={() => onReview?.(quizIndex)}
className={cn(
'flex aspect-square flex-col items-center justify-center rounded-xl border p-1 hover:opacity-80',
isCorrect && 'border-green-700 bg-green-700 text-white',
isIncorrect && 'border-red-700 bg-red-700 text-white',
isSkipped && 'border-gray-700 bg-gray-700 text-white',
!isActive && 'opacity-50',
)}
>
{isCorrect && <CheckIcon className="h-6 w-6" />}
{isIncorrect && <XIcon className="h-6 w-6" />}
{isSkipped && <SkipForwardIcon className="h-6 w-6" />}
</button>
);
})}
</div> </div>
<button <button
className="flex shrink-0 items-center gap-2 rounded-xl bg-black px-4 py-2 text-white hover:bg-gray-900 disabled:opacity-70" className="flex shrink-0 items-center gap-2 rounded-xl bg-black px-4 py-2 text-white hover:bg-gray-900 disabled:opacity-70"
onClick={onComplete} onClick={onComplete}
> >
Complete Quiz <ArrowRightIcon className="h-4 w-4" /> Show Results <ArrowRightIcon className="h-4 w-4" />
</button> </button>
</div> </div>
</div> </div>