|
import { Container, CssBaseline, ThemeProvider, createTheme, Button, Box, Typography, Alert, CircularProgress, Grid } from '@mui/material'; |
|
import Header from './components/Header'; |
|
import DocumentInput from './components/DocumentInput'; |
|
import QuizGenerator from './components/QuizGenerator'; |
|
import ProblemAnswer from './components/ProblemAnswer'; |
|
import Topics from './components/Topics'; |
|
import { useState } from 'react'; |
|
import { Problem } from './types/Problem'; |
|
import './styles/global.css'; |
|
|
|
const theme = createTheme({ |
|
palette: { |
|
mode: 'dark', |
|
primary: { |
|
main: '#90caf9', |
|
}, |
|
background: { |
|
default: '#1a1a1a', |
|
paper: '#242424', |
|
}, |
|
text: { |
|
primary: '#ffffff', |
|
secondary: 'rgba(255, 255, 255, 0.7)', |
|
}, |
|
}, |
|
components: { |
|
MuiCssBaseline: { |
|
styleOverrides: { |
|
body: { |
|
backgroundImage: ` |
|
linear-gradient(rgba(255, 255, 255, 0.05) 1px, transparent 1px), |
|
linear-gradient(90deg, rgba(255, 255, 255, 0.05) 1px, transparent 1px) |
|
`, |
|
backgroundSize: '20px 20px', |
|
backgroundPosition: '-1px -1px', |
|
}, |
|
}, |
|
}, |
|
MuiPaper: { |
|
styleOverrides: { |
|
root: { |
|
backgroundImage: 'none', |
|
}, |
|
}, |
|
}, |
|
}, |
|
}); |
|
|
|
interface ProblemWithFeedback extends Problem { |
|
feedback?: string; |
|
} |
|
|
|
function App() { |
|
const [problems, setProblems] = useState<ProblemWithFeedback[]>([]); |
|
const [quizTopic, setQuizTopic] = useState<string>(''); |
|
const [error, setError] = useState<string | null>(null); |
|
const [isSubmitting, setIsSubmitting] = useState(false); |
|
const [selectedTopic, setSelectedTopic] = useState<string>(''); |
|
|
|
const handleProblemsGenerated = (newProblems: string[], query: string) => { |
|
const problemObjects = newProblems.map(question => ({ question })); |
|
setProblems(problemObjects); |
|
setQuizTopic(query); |
|
}; |
|
|
|
const handleAnswerChange = (index: number, answer: string) => { |
|
setProblems(prevProblems => { |
|
const newProblems = [...prevProblems]; |
|
newProblems[index] = { ...newProblems[index], userAnswer: answer }; |
|
return newProblems; |
|
}); |
|
}; |
|
|
|
const handleTopicChange = (topic: string) => { |
|
setSelectedTopic(topic); |
|
}; |
|
|
|
const handleSubmit = async () => { |
|
try { |
|
setError(null); |
|
setIsSubmitting(true); |
|
|
|
|
|
const unansweredProblems = problems.some(p => !p.userAnswer); |
|
if (unansweredProblems) { |
|
setError('Please answer all questions before submitting'); |
|
return; |
|
} |
|
|
|
const response = await fetch('/api/feedback', { |
|
method: 'POST', |
|
headers: { |
|
'Content-Type': 'application/json', |
|
}, |
|
body: JSON.stringify({ |
|
user_query: quizTopic, |
|
selected_topic: selectedTopic, |
|
problems: problems.map(p => p.question), |
|
user_answers: problems.map(p => p.userAnswer as string) |
|
}) |
|
}); |
|
|
|
if (!response.ok) { |
|
const errorData = await response.json().catch(() => null); |
|
throw new Error(errorData?.detail || 'Failed to submit answers'); |
|
} |
|
|
|
const data = await response.json(); |
|
|
|
setProblems(prevProblems => |
|
prevProblems.map((problem, index) => ({ |
|
...problem, |
|
feedback: data.feedback[index] |
|
})) |
|
); |
|
} catch (error) { |
|
setError(error instanceof Error ? error.message : 'An error occurred'); |
|
} finally { |
|
setIsSubmitting(false); |
|
} |
|
}; |
|
|
|
return ( |
|
<ThemeProvider theme={theme}> |
|
<CssBaseline /> |
|
<Container |
|
maxWidth="md" |
|
sx={{ |
|
py: 4, |
|
'& .MuiPaper-root': { |
|
backgroundColor: 'background.paper', |
|
backdropFilter: 'blur(10px)', |
|
borderRadius: 2, |
|
boxShadow: '0 4px 6px rgba(0, 0, 0, 0.1)', |
|
}, |
|
}} |
|
> |
|
<Header /> |
|
<Grid container spacing={2} sx={{ mb: 2 }}> |
|
<Grid item xs={12} md={4}> |
|
<Topics onTopicChange={handleTopicChange} /> |
|
</Grid> |
|
<Grid item xs={12} md={8}> |
|
<QuizGenerator onProblemsGenerated={handleProblemsGenerated} /> |
|
</Grid> |
|
</Grid> |
|
<DocumentInput /> |
|
|
|
{problems.map((problem, index) => ( |
|
<Box key={index} sx={{ mb: 4 }}> |
|
<ProblemAnswer |
|
problem={problem} |
|
index={index} |
|
onAnswerChange={handleAnswerChange} |
|
/> |
|
{problem.feedback && ( |
|
<Box sx={{ mt: 2, pl: 2, borderLeft: 3, borderColor: 'primary.main' }}> |
|
<Typography variant="h6" color="primary" gutterBottom> |
|
Feedback: |
|
</Typography> |
|
<Typography> |
|
{problem.feedback} |
|
</Typography> |
|
</Box> |
|
)} |
|
</Box> |
|
))} |
|
|
|
{error && ( |
|
<Alert severity="error" sx={{ mt: 2, mb: 2 }}> |
|
{error} |
|
</Alert> |
|
)} |
|
|
|
{problems.length > 0 && ( |
|
<Box sx={{ mt: 4, display: 'flex', justifyContent: 'center', alignItems: 'center', gap: 2 }}> |
|
<Button |
|
variant="contained" |
|
color="primary" |
|
size="large" |
|
onClick={handleSubmit} |
|
disabled={isSubmitting} |
|
> |
|
{isSubmitting ? 'Submitting...' : 'Submit for Feedback'} |
|
</Button> |
|
{isSubmitting && <CircularProgress size={24} />} |
|
</Box> |
|
)} |
|
</Container> |
|
</ThemeProvider> |
|
); |
|
} |
|
|
|
export default App; |