Visual update
Browse files
frontend/src/App.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
import { Container, CssBaseline, ThemeProvider, createTheme, Button, Box, Typography, Alert, CircularProgress } from '@mui/material';
|
| 2 |
import Header from './components/Header';
|
| 3 |
import DocumentInput from './components/DocumentInput';
|
| 4 |
import QuizGenerator from './components/QuizGenerator';
|
|
@@ -98,8 +98,14 @@ function App() {
|
|
| 98 |
<Container maxWidth="md" sx={{ py: 4 }}>
|
| 99 |
<Header />
|
| 100 |
<DocumentInput />
|
| 101 |
-
<
|
| 102 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 103 |
|
| 104 |
{error && (
|
| 105 |
<Alert severity="error" sx={{ mt: 2, mb: 2 }}>
|
|
|
|
| 1 |
+
import { Container, CssBaseline, ThemeProvider, createTheme, Button, Box, Typography, Alert, CircularProgress, Grid } from '@mui/material';
|
| 2 |
import Header from './components/Header';
|
| 3 |
import DocumentInput from './components/DocumentInput';
|
| 4 |
import QuizGenerator from './components/QuizGenerator';
|
|
|
|
| 98 |
<Container maxWidth="md" sx={{ py: 4 }}>
|
| 99 |
<Header />
|
| 100 |
<DocumentInput />
|
| 101 |
+
<Grid container spacing={2} sx={{ mb: 2 }}>
|
| 102 |
+
<Grid item xs={12} md={4}>
|
| 103 |
+
<Topics onTopicChange={handleTopicChange} />
|
| 104 |
+
</Grid>
|
| 105 |
+
<Grid item xs={12} md={8}>
|
| 106 |
+
<QuizGenerator onProblemsGenerated={handleProblemsGenerated} />
|
| 107 |
+
</Grid>
|
| 108 |
+
</Grid>
|
| 109 |
|
| 110 |
{error && (
|
| 111 |
<Alert severity="error" sx={{ mt: 2, mb: 2 }}>
|
frontend/src/components/Header.tsx
CHANGED
|
@@ -4,7 +4,7 @@ function Header() {
|
|
| 4 |
return (
|
| 5 |
<Box sx={{ mb: 4 }}>
|
| 6 |
<Typography variant="h2" component="h1" align="center">
|
| 7 |
-
|
| 8 |
</Typography>
|
| 9 |
</Box>
|
| 10 |
);
|
|
|
|
| 4 |
return (
|
| 5 |
<Box sx={{ mb: 4 }}>
|
| 6 |
<Typography variant="h2" component="h1" align="center">
|
| 7 |
+
Simplifi
|
| 8 |
</Typography>
|
| 9 |
</Box>
|
| 10 |
);
|
frontend/src/components/Topics.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
import { FormControl, InputLabel, Select, MenuItem, SelectChangeEvent } from '@mui/material';
|
| 2 |
import { useEffect, useState } from 'react';
|
| 3 |
|
| 4 |
interface TopicsProps {
|
|
@@ -13,17 +13,18 @@ export default function Topics({ onTopicChange }: TopicsProps) {
|
|
| 13 |
const [topics, setTopics] = useState<string[]>([]);
|
| 14 |
const [selectedTopic, setSelectedTopic] = useState('');
|
| 15 |
const [error, setError] = useState<string | null>(null);
|
|
|
|
| 16 |
|
| 17 |
useEffect(() => {
|
| 18 |
const fetchTopics = async () => {
|
| 19 |
try {
|
|
|
|
| 20 |
const response = await fetch('/api/topics');
|
| 21 |
if (!response.ok) {
|
| 22 |
throw new Error('Failed to fetch topics');
|
| 23 |
}
|
| 24 |
const data: TopicsResponse = await response.json();
|
| 25 |
|
| 26 |
-
// Validate that we received an array of sources
|
| 27 |
if (!data.sources || !Array.isArray(data.sources)) {
|
| 28 |
throw new Error('Invalid topics data received');
|
| 29 |
}
|
|
@@ -32,7 +33,9 @@ export default function Topics({ onTopicChange }: TopicsProps) {
|
|
| 32 |
} catch (error) {
|
| 33 |
console.error('Error fetching topics:', error);
|
| 34 |
setError('Failed to load topics');
|
| 35 |
-
setTopics([]);
|
|
|
|
|
|
|
| 36 |
}
|
| 37 |
};
|
| 38 |
|
|
@@ -47,18 +50,28 @@ export default function Topics({ onTopicChange }: TopicsProps) {
|
|
| 47 |
|
| 48 |
return (
|
| 49 |
<FormControl fullWidth sx={{ mb: 2 }}>
|
| 50 |
-
<InputLabel>
|
| 51 |
<Select
|
| 52 |
value={selectedTopic}
|
| 53 |
-
label="
|
| 54 |
onChange={handleChange}
|
| 55 |
error={!!error}
|
|
|
|
| 56 |
>
|
| 57 |
-
{
|
| 58 |
-
<MenuItem
|
| 59 |
-
{
|
|
|
|
|
|
|
|
|
|
| 60 |
</MenuItem>
|
| 61 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 62 |
</Select>
|
| 63 |
</FormControl>
|
| 64 |
);
|
|
|
|
| 1 |
+
import { FormControl, InputLabel, Select, MenuItem, SelectChangeEvent, CircularProgress, Box } from '@mui/material';
|
| 2 |
import { useEffect, useState } from 'react';
|
| 3 |
|
| 4 |
interface TopicsProps {
|
|
|
|
| 13 |
const [topics, setTopics] = useState<string[]>([]);
|
| 14 |
const [selectedTopic, setSelectedTopic] = useState('');
|
| 15 |
const [error, setError] = useState<string | null>(null);
|
| 16 |
+
const [isLoading, setIsLoading] = useState(true);
|
| 17 |
|
| 18 |
useEffect(() => {
|
| 19 |
const fetchTopics = async () => {
|
| 20 |
try {
|
| 21 |
+
setIsLoading(true);
|
| 22 |
const response = await fetch('/api/topics');
|
| 23 |
if (!response.ok) {
|
| 24 |
throw new Error('Failed to fetch topics');
|
| 25 |
}
|
| 26 |
const data: TopicsResponse = await response.json();
|
| 27 |
|
|
|
|
| 28 |
if (!data.sources || !Array.isArray(data.sources)) {
|
| 29 |
throw new Error('Invalid topics data received');
|
| 30 |
}
|
|
|
|
| 33 |
} catch (error) {
|
| 34 |
console.error('Error fetching topics:', error);
|
| 35 |
setError('Failed to load topics');
|
| 36 |
+
setTopics([]);
|
| 37 |
+
} finally {
|
| 38 |
+
setIsLoading(false);
|
| 39 |
}
|
| 40 |
};
|
| 41 |
|
|
|
|
| 50 |
|
| 51 |
return (
|
| 52 |
<FormControl fullWidth sx={{ mb: 2 }}>
|
| 53 |
+
<InputLabel>Topic</InputLabel>
|
| 54 |
<Select
|
| 55 |
value={selectedTopic}
|
| 56 |
+
label="Topic"
|
| 57 |
onChange={handleChange}
|
| 58 |
error={!!error}
|
| 59 |
+
disabled={isLoading}
|
| 60 |
>
|
| 61 |
+
{isLoading ? (
|
| 62 |
+
<MenuItem value="" disabled>
|
| 63 |
+
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
| 64 |
+
<CircularProgress size={20} />
|
| 65 |
+
Loading topics...
|
| 66 |
+
</Box>
|
| 67 |
</MenuItem>
|
| 68 |
+
) : (
|
| 69 |
+
Array.isArray(topics) && topics.map((topic) => (
|
| 70 |
+
<MenuItem key={topic} value={topic}>
|
| 71 |
+
{topic}
|
| 72 |
+
</MenuItem>
|
| 73 |
+
))
|
| 74 |
+
)}
|
| 75 |
</Select>
|
| 76 |
</FormControl>
|
| 77 |
);
|