Added topics field to FE
Browse files- backend/app/main.py +1 -1
- frontend/src/App.tsx +18 -4
- frontend/src/components/QuizGenerator.tsx +1 -1
- frontend/src/components/Topics.tsx +65 -0
backend/app/main.py
CHANGED
@@ -90,7 +90,7 @@ async def get_feedback(request: FeedbackRequest):
|
|
90 |
raise HTTPException(status_code=500, detail=str(e))
|
91 |
|
92 |
|
93 |
-
@app.
|
94 |
async def get_topics():
|
95 |
sources = get_all_unique_source_of_docs_in_collection_DUMB()
|
96 |
return {"sources": sources}
|
|
|
90 |
raise HTTPException(status_code=500, detail=str(e))
|
91 |
|
92 |
|
93 |
+
@app.get("/api/topics", response_model=TopicsResponse)
|
94 |
async def get_topics():
|
95 |
sources = get_all_unique_source_of_docs_in_collection_DUMB()
|
96 |
return {"sources": sources}
|
frontend/src/App.tsx
CHANGED
@@ -1,8 +1,9 @@
|
|
1 |
-
import { Container, CssBaseline, ThemeProvider, createTheme, Button, Box, Typography, Alert } from '@mui/material';
|
2 |
import Header from './components/Header';
|
3 |
import DocumentInput from './components/DocumentInput';
|
4 |
import QuizGenerator from './components/QuizGenerator';
|
5 |
import ProblemAnswer from './components/ProblemAnswer';
|
|
|
6 |
import { useState } from 'react';
|
7 |
import { Problem } from './types/Problem';
|
8 |
|
@@ -25,6 +26,8 @@ function App() {
|
|
25 |
const [problems, setProblems] = useState<ProblemWithFeedback[]>([]);
|
26 |
const [quizTopic, setQuizTopic] = useState<string>('');
|
27 |
const [error, setError] = useState<string | null>(null);
|
|
|
|
|
28 |
|
29 |
const handleProblemsGenerated = (newProblems: string[], query: string) => {
|
30 |
const problemObjects = newProblems.map(question => ({ question }));
|
@@ -40,9 +43,14 @@ function App() {
|
|
40 |
});
|
41 |
};
|
42 |
|
|
|
|
|
|
|
|
|
43 |
const handleSubmit = async () => {
|
44 |
try {
|
45 |
setError(null);
|
|
|
46 |
|
47 |
// Validate that all problems have answers
|
48 |
const unansweredProblems = problems.some(p => !p.userAnswer);
|
@@ -58,8 +66,9 @@ function App() {
|
|
58 |
},
|
59 |
body: JSON.stringify({
|
60 |
user_query: quizTopic,
|
|
|
61 |
problems: problems.map(p => p.question),
|
62 |
-
user_answers: problems.map(p => p.userAnswer as string)
|
63 |
})
|
64 |
});
|
65 |
|
@@ -78,6 +87,8 @@ function App() {
|
|
78 |
);
|
79 |
} catch (error) {
|
80 |
setError(error instanceof Error ? error.message : 'An error occurred');
|
|
|
|
|
81 |
}
|
82 |
};
|
83 |
|
@@ -87,6 +98,7 @@ function App() {
|
|
87 |
<Container maxWidth="md" sx={{ py: 4 }}>
|
88 |
<Header />
|
89 |
<DocumentInput />
|
|
|
90 |
<QuizGenerator onProblemsGenerated={handleProblemsGenerated} />
|
91 |
|
92 |
{error && (
|
@@ -116,15 +128,17 @@ function App() {
|
|
116 |
))}
|
117 |
|
118 |
{problems.length > 0 && (
|
119 |
-
<Box sx={{ mt: 4, display: 'flex', justifyContent: 'center' }}>
|
120 |
<Button
|
121 |
variant="contained"
|
122 |
color="primary"
|
123 |
size="large"
|
124 |
onClick={handleSubmit}
|
|
|
125 |
>
|
126 |
-
Submit for Feedback
|
127 |
</Button>
|
|
|
128 |
</Box>
|
129 |
)}
|
130 |
</Container>
|
|
|
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';
|
5 |
import ProblemAnswer from './components/ProblemAnswer';
|
6 |
+
import Topics from './components/Topics';
|
7 |
import { useState } from 'react';
|
8 |
import { Problem } from './types/Problem';
|
9 |
|
|
|
26 |
const [problems, setProblems] = useState<ProblemWithFeedback[]>([]);
|
27 |
const [quizTopic, setQuizTopic] = useState<string>('');
|
28 |
const [error, setError] = useState<string | null>(null);
|
29 |
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
30 |
+
const [selectedTopic, setSelectedTopic] = useState<string>('');
|
31 |
|
32 |
const handleProblemsGenerated = (newProblems: string[], query: string) => {
|
33 |
const problemObjects = newProblems.map(question => ({ question }));
|
|
|
43 |
});
|
44 |
};
|
45 |
|
46 |
+
const handleTopicChange = (topic: string) => {
|
47 |
+
setSelectedTopic(topic);
|
48 |
+
};
|
49 |
+
|
50 |
const handleSubmit = async () => {
|
51 |
try {
|
52 |
setError(null);
|
53 |
+
setIsSubmitting(true);
|
54 |
|
55 |
// Validate that all problems have answers
|
56 |
const unansweredProblems = problems.some(p => !p.userAnswer);
|
|
|
66 |
},
|
67 |
body: JSON.stringify({
|
68 |
user_query: quizTopic,
|
69 |
+
selected_topic: selectedTopic,
|
70 |
problems: problems.map(p => p.question),
|
71 |
+
user_answers: problems.map(p => p.userAnswer as string)
|
72 |
})
|
73 |
});
|
74 |
|
|
|
87 |
);
|
88 |
} catch (error) {
|
89 |
setError(error instanceof Error ? error.message : 'An error occurred');
|
90 |
+
} finally {
|
91 |
+
setIsSubmitting(false);
|
92 |
}
|
93 |
};
|
94 |
|
|
|
98 |
<Container maxWidth="md" sx={{ py: 4 }}>
|
99 |
<Header />
|
100 |
<DocumentInput />
|
101 |
+
<Topics onTopicChange={handleTopicChange} />
|
102 |
<QuizGenerator onProblemsGenerated={handleProblemsGenerated} />
|
103 |
|
104 |
{error && (
|
|
|
128 |
))}
|
129 |
|
130 |
{problems.length > 0 && (
|
131 |
+
<Box sx={{ mt: 4, display: 'flex', justifyContent: 'center', alignItems: 'center', gap: 2 }}>
|
132 |
<Button
|
133 |
variant="contained"
|
134 |
color="primary"
|
135 |
size="large"
|
136 |
onClick={handleSubmit}
|
137 |
+
disabled={isSubmitting}
|
138 |
>
|
139 |
+
{isSubmitting ? 'Submitting...' : 'Submit for Feedback'}
|
140 |
</Button>
|
141 |
+
{isSubmitting && <CircularProgress size={24} />}
|
142 |
</Box>
|
143 |
)}
|
144 |
</Container>
|
frontend/src/components/QuizGenerator.tsx
CHANGED
@@ -38,7 +38,7 @@ function QuizGenerator({ onProblemsGenerated }: QuizGeneratorProps) {
|
|
38 |
<Box sx={{ mb: 4, display: 'flex', gap: 2 }}>
|
39 |
<TextField
|
40 |
fullWidth
|
41 |
-
label="Quiz
|
42 |
value={query}
|
43 |
onChange={(e) => setQuery(e.target.value)}
|
44 |
disabled={isLoading}
|
|
|
38 |
<Box sx={{ mb: 4, display: 'flex', gap: 2 }}>
|
39 |
<TextField
|
40 |
fullWidth
|
41 |
+
label="Quiz focus?"
|
42 |
value={query}
|
43 |
onChange={(e) => setQuery(e.target.value)}
|
44 |
disabled={isLoading}
|
frontend/src/components/Topics.tsx
ADDED
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { FormControl, InputLabel, Select, MenuItem, SelectChangeEvent } from '@mui/material';
|
2 |
+
import { useEffect, useState } from 'react';
|
3 |
+
|
4 |
+
interface TopicsProps {
|
5 |
+
onTopicChange: (topic: string) => void;
|
6 |
+
}
|
7 |
+
|
8 |
+
interface TopicsResponse {
|
9 |
+
sources: string[];
|
10 |
+
}
|
11 |
+
|
12 |
+
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 |
+
}
|
30 |
+
|
31 |
+
setTopics(data.sources);
|
32 |
+
} catch (error) {
|
33 |
+
console.error('Error fetching topics:', error);
|
34 |
+
setError('Failed to load topics');
|
35 |
+
setTopics([]); // Ensure topics is at least an empty array
|
36 |
+
}
|
37 |
+
};
|
38 |
+
|
39 |
+
fetchTopics();
|
40 |
+
}, []);
|
41 |
+
|
42 |
+
const handleChange = (event: SelectChangeEvent) => {
|
43 |
+
const topic = event.target.value;
|
44 |
+
setSelectedTopic(topic);
|
45 |
+
onTopicChange(topic);
|
46 |
+
};
|
47 |
+
|
48 |
+
return (
|
49 |
+
<FormControl fullWidth sx={{ mb: 2 }}>
|
50 |
+
<InputLabel>Topics</InputLabel>
|
51 |
+
<Select
|
52 |
+
value={selectedTopic}
|
53 |
+
label="Topics"
|
54 |
+
onChange={handleChange}
|
55 |
+
error={!!error}
|
56 |
+
>
|
57 |
+
{Array.isArray(topics) && topics.map((topic) => (
|
58 |
+
<MenuItem key={topic} value={topic}>
|
59 |
+
{topic}
|
60 |
+
</MenuItem>
|
61 |
+
))}
|
62 |
+
</Select>
|
63 |
+
</FormControl>
|
64 |
+
);
|
65 |
+
}
|