Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 124 additions & 17 deletions app/practice/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
import { useState, useEffect, useRef } from 'react';
import Link from 'next/link';
import { allQuestions } from '@/lib/questionsData';
import { saveResponse, deleteResponse } from '@/lib/responseStorage';
import { saveEvaluation } from '@/lib/evaluationStorage';
import { saveResponse, deleteResponse, clearAllResponses } from '@/lib/responseStorage';
import { saveEvaluation, getAllEvaluations, SelfEvaluation } from '@/lib/evaluationStorage';

interface Question {
id: string;
Expand All @@ -26,14 +26,25 @@ export default function PracticePage() {
const [questionsInRound, setQuestionsInRound] = useState(0);
const [extraTime, setExtraTime] = useState(0);
const [showEvaluation, setShowEvaluation] = useState(false);
const [evaluation, setEvaluation] = useState({
confidence: 3,
effectiveness: 3,
knowledge: 3,
const [evaluation, setEvaluation] = useState<{
confidence: number | null;
effectiveness: number | null;
knowledge: number | null;
}>({
confidence: null,
effectiveness: null,
knowledge: null,
});
const [previousEvaluations, setPreviousEvaluations] = useState<SelfEvaluation[]>([]);
const [showPreviousEvaluations, setShowPreviousEvaluations] = useState(false);

const skipQuestionRef = useRef<(() => void) | null>(null);

// Load previous evaluations on mount
useEffect(() => {
setPreviousEvaluations(getAllEvaluations());
}, []);

useEffect(() => {
let interval: NodeJS.Timeout | null = null;

Expand Down Expand Up @@ -148,7 +159,21 @@ export default function PracticePage() {
};

const handleSubmitEvaluation = () => {
saveEvaluation(evaluation);
// Validate that all evaluations are filled
if (evaluation.confidence === null || evaluation.effectiveness === null || evaluation.knowledge === null) {
alert('Please rate all three categories before submitting.');
return;
}

saveEvaluation({
confidence: evaluation.confidence,
effectiveness: evaluation.effectiveness,
knowledge: evaluation.knowledge,
});

// Reload evaluations to show the new one
setPreviousEvaluations(getAllEvaluations());

// Reset for new round
setShowEvaluation(false);
setQuestionsInRound(0);
Expand All @@ -157,6 +182,11 @@ export default function PracticePage() {
setCurrentQuestion(null);
setTimer(TIMER_DURATION);
setIsActive(false);
setEvaluation({
confidence: null,
effectiveness: null,
knowledge: null,
});
};

const handleStartNewRound = () => {
Expand All @@ -166,6 +196,14 @@ export default function PracticePage() {
getRandomQuestion();
};

const handleClearAllResponses = () => {
if (window.confirm('Are you sure you want to delete all your recorded answers? This action cannot be undone.')) {
clearAllResponses();
setResponse('');
alert('All responses have been cleared.');
}
};

return (
<div className="min-h-screen bg-gradient-to-br from-slate-50 to-slate-100 dark:from-slate-900 dark:to-slate-800">
<div className="container mx-auto px-4 py-8 max-w-4xl">
Expand Down Expand Up @@ -194,7 +232,7 @@ export default function PracticePage() {
<div className="space-y-6">
<div>
<label className="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-2">
Confidence (1-5)
Confidence (1-5) {evaluation.confidence === null && <span className="text-red-600 dark:text-red-400">*</span>}
</label>
<div className="flex gap-2 justify-center">
{[1, 2, 3, 4, 5].map((val) => (
Expand All @@ -215,7 +253,7 @@ export default function PracticePage() {

<div>
<label className="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-2">
Effectiveness (1-5)
Effectiveness (1-5) {evaluation.effectiveness === null && <span className="text-red-600 dark:text-red-400">*</span>}
</label>
<div className="flex gap-2 justify-center">
{[1, 2, 3, 4, 5].map((val) => (
Expand All @@ -236,7 +274,7 @@ export default function PracticePage() {

<div>
<label className="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-2">
Knowledge (1-5)
Knowledge (1-5) {evaluation.knowledge === null && <span className="text-red-600 dark:text-red-400">*</span>}
</label>
<div className="flex gap-2 justify-center">
{[1, 2, 3, 4, 5].map((val) => (
Expand All @@ -262,6 +300,11 @@ export default function PracticePage() {
>
Submit Evaluation & Start New Round
</button>
{(evaluation.confidence === null || evaluation.effectiveness === null || evaluation.knowledge === null) && (
<p className="text-sm text-red-600 dark:text-red-400 text-center">
* Please rate all three categories before submitting
</p>
)}
</div>
) : !currentQuestion ? (
<div className="text-center space-y-8">
Expand All @@ -271,13 +314,77 @@ export default function PracticePage() {
<p className="text-md text-slate-500 dark:text-slate-400">
Finish questions early? Your extra time rolls over to the next question!
</p>
<button
onClick={handleStartNewRound}
disabled={questions.length === 0}
className="px-8 py-4 bg-green-600 hover:bg-green-700 text-white font-semibold rounded-xl shadow-lg hover:shadow-xl transition-all duration-300 text-lg disabled:opacity-50 disabled:cursor-not-allowed"
>
Start Practicing
</button>

<div className="flex flex-col sm:flex-row gap-4 justify-center items-center">
<button
onClick={handleStartNewRound}
disabled={questions.length === 0}
className="px-8 py-4 bg-green-600 hover:bg-green-700 text-white font-semibold rounded-xl shadow-lg hover:shadow-xl transition-all duration-300 text-lg disabled:opacity-50 disabled:cursor-not-allowed"
>
Start Practicing
</button>

<button
onClick={handleClearAllResponses}
className="px-6 py-3 bg-red-600 hover:bg-red-700 text-white font-semibold rounded-xl shadow-lg hover:shadow-xl transition-all duration-300"
>
Clear All Responses
</button>
</div>

{previousEvaluations.length > 0 && (
<div className="mt-8">
<button
onClick={() => setShowPreviousEvaluations(!showPreviousEvaluations)}
className="text-blue-600 dark:text-blue-400 hover:underline font-medium"
>
{showPreviousEvaluations ? '▼ Hide' : '▶'} Previous Evaluations ({previousEvaluations.length})
</button>

{showPreviousEvaluations && (
<div className="mt-4 space-y-4 max-w-3xl mx-auto">
{previousEvaluations.slice().reverse().map((evalItem) => (
<div
key={evalItem.timestamp}
className="bg-white dark:bg-slate-800 rounded-lg shadow-md p-6 text-left"
>
<div className="flex justify-between items-center mb-4">
<h3 className="text-lg font-semibold text-slate-900 dark:text-slate-100">
Round {evalItem.roundNumber}
</h3>
<span className="text-sm text-slate-500 dark:text-slate-400">
{new Date(evalItem.timestamp).toLocaleDateString()} {new Date(evalItem.timestamp).toLocaleTimeString()}
</span>
</div>
<div className="grid grid-cols-3 gap-4">
<div>
<p className="text-sm text-slate-600 dark:text-slate-400 mb-1">Confidence</p>
<div className="flex items-center">
<span className="text-2xl font-bold text-blue-600 dark:text-blue-400">{evalItem.confidence}</span>
<span className="text-sm text-slate-500 dark:text-slate-500 ml-1">/5</span>
</div>
</div>
<div>
<p className="text-sm text-slate-600 dark:text-slate-400 mb-1">Effectiveness</p>
<div className="flex items-center">
<span className="text-2xl font-bold text-green-600 dark:text-green-400">{evalItem.effectiveness}</span>
<span className="text-sm text-slate-500 dark:text-slate-500 ml-1">/5</span>
</div>
</div>
<div>
<p className="text-sm text-slate-600 dark:text-slate-400 mb-1">Knowledge</p>
<div className="flex items-center">
<span className="text-2xl font-bold text-purple-600 dark:text-purple-400">{evalItem.knowledge}</span>
<span className="text-sm text-slate-500 dark:text-slate-500 ml-1">/5</span>
</div>
</div>
</div>
</div>
))}
</div>
)}
</div>
)}
</div>
) : (
<div className="space-y-6">
Expand Down