Skip to content

Commit

Permalink
Merge pull request #50 from CS3219-AY2425S1/question-service-enhancem…
Browse files Browse the repository at this point in the history
…ents

Question Service Enhancements
  • Loading branch information
Chen1x authored Nov 14, 2024
2 parents 713805b + 55eceaa commit 26662cb
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 33 deletions.
1 change: 1 addition & 0 deletions backend/question-backend/controllers/questionTest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// to create PR only
22 changes: 2 additions & 20 deletions frontend/src/components/questions/QuestionTable.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@

import * as React from 'react';
import { useEffect, useState } from 'react';
import { useState } from 'react';
import { Paper, Table, TableBody, TableCell, TableContainer, TableHead, TablePagination, TableRow, Button } from '@mui/material';
import ErrorMessage from './ErrorMessageDialog'
import QuestionDialog from './QuestionDialog';
import questionService from '../../services/question-service';
import useAuth from '../../hooks/useAuth';

const columns = [
Expand All @@ -15,7 +14,7 @@ const columns = [
{ id: 'status', label: 'Status', minWidth: 100 }
];

export default function QuestionTable({ mountTrigger }) {
export default function QuestionTable({ questions }) {
const { cookies } = useAuth();
const [page, setPage] = React.useState(0);
const [rowsPerPage, setRowsPerPage] = React.useState(10);
Expand All @@ -29,29 +28,12 @@ export default function QuestionTable({ mountTrigger }) {
setPage(0);
};

const [questions, setQuestions] = useState([]);
const [loading, setLoading] = useState(true); // Loading state
const [open, setOpen] = useState(false);
const [errorOpen, setErrorOpen] = React.useState(false); // State to control error dialog visibility
const [errorMessage, setErrorMessage] = React.useState(''); // State to store error message
const [selectedQuestion, setSelectedQuestion] = useState(null);

// Fetch questions from backend when component mounts
useEffect(() => {
const fetchQuestions = async () => {
try {
const response = await questionService.getAllQuestions(cookies);
setQuestions(response);
} catch (error) {
setErrorMessage(error.message); // Set error message
setErrorOpen(true); // Open error dialog
} finally {
setLoading(false);
}
};
fetchQuestions(); // Trigger the fetch
}, [mountTrigger]);

const handleQuestionClick = (question) => {
setSelectedQuestion(question);
setOpen(true);
Expand Down
62 changes: 54 additions & 8 deletions frontend/src/pages/Questions.jsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,80 @@
import { useState } from "react";
import React, { useEffect, useState } from "react";

import GeneralNavbar from "../components/navbar/GeneralNavbar";
import QuestionTable from '../components/questions/QuestionTable';
import AddQuestionButton from '../components/questions/AddQuestionButtons';
import RefreshTableButton from '../components/questions/refreshTableButton';
import useAuth from "../hooks/useAuth";
import { topics } from '../assets/topics';
import questionService from '../services/question-service';
import ErrorMessage from '../components/questions/ErrorMessageDialog'

import '../styles/questions.css';
import 'react-toastify/dist/ReactToastify.css';

const Questions = () => {
const { privilege } = useAuth();
const { privilege, cookies } = useAuth();
const [refresh, setRefresh] = useState(true);
const toggle = () => setRefresh(!refresh);
const [difficultyFilter, setDifficultyFilter] = useState('');
const [topicFilter, setTopicFilter] = useState('');
const [questions, setQuestions] = useState([]);
const [errorOpen, setErrorOpen] = React.useState(false);
const [errorMessage, setErrorMessage] = React.useState('');

useEffect(() => {
const fetchFilteredQuestions = async () => {
try {
const response = await questionService.filterQuestions(topicFilter, difficultyFilter, cookies);
setQuestions(response);
} catch (error) {
setErrorMessage(error.message); // Set error message
setErrorOpen(true); // Open error dialog
}
}
fetchFilteredQuestions();
}, [refresh, topicFilter, difficultyFilter]);

const handleErrorClose = () => {
setErrorOpen(false); // Close the error dialog
};

return (
<div>
<GeneralNavbar />
<div className="questions-container">
<h1>Questions</h1>
<p className="description">View all the questions stored in database.</p>
<div className="question-table-container">
<div className="table-buttons">
<RefreshTableButton trigger={toggle}/>
<div className="admin-button">
{privilege ? <AddQuestionButton /> : null}
{/* Filter options */}
<div className="filters">
<select onChange={(e) => setDifficultyFilter(e.target.value)}>
<option value="">All Difficulties</option>
<option value="Easy">Easy</option>
<option value="Medium">Medium</option>
<option value="Hard">Hard</option>
</select>

<select onChange={(e) => setTopicFilter(e.target.value)}>
<option value="">All Topics</option>
{topics.map((topic, index) => (
<option key={index} value={topic}>{topic}</option>
))}
</select>
<div className="table-buttons">
<RefreshTableButton trigger={toggle}/>
<div className="admin-button">
{privilege ? <AddQuestionButton /> : null}
</div>
</div>
</div>
<QuestionTable mountTrigger={refresh} />
<div className="question-table-container">
<QuestionTable questions={questions} />
</div>
<ErrorMessage
open={errorOpen}
handleClose={handleErrorClose}
errorMessage={errorMessage}
/>
</div>
</div>
);
Expand Down
13 changes: 11 additions & 2 deletions frontend/src/services/question-service.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,18 @@ const getQuestionByTopicAndDifficulty = async (topic, difficulty, roomId, cookie
};

// Filter questions by specific category (topic / difficulty)
const filterQuestions = async (category, filter) => {
const filterQuestions = async (topicFilter, difficultyFilter, cookies) => {
try {
const response = await axios.get(`${BASE_URL}?${category}=${filter}`);
let query = [];
if (topicFilter) query.push(`topic=${topicFilter}`);
if (difficultyFilter) query.push(`difficulty=${difficultyFilter}`);
const queryString = query.length ? `?${query.join('&')}` : '';
const response = await axios.get(`${BASE_URL}${queryString}`, {
headers: {
Authorization: `Bearer ${cookies.token}`
},
withCredentials: true
});
return response.data;
} catch (error) {
console.error('Error filtering questions:', error);
Expand Down
32 changes: 29 additions & 3 deletions frontend/src/styles/questions.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,28 @@
.filters select {
font-size: 16px;
font-family: 'Poppins', sans-serif;
font-weight: 600;
color: #1C1678;
padding: 8px 12px;
border: 2px solid #1C1678;
border-radius: 8px;
background-color: #F7F7F7;
margin-right: 10px; /* Spacing between filters */
outline: none;
transition: border-color 0.3s ease;
}

.filters select:focus {
border-color: #41AFFF; /* Add a subtle border change on focus */
}

.filters {
display: flex;
align-items: center;
gap: 10px; /* Consistent spacing between dropdowns */
margin-top: 20px;
}

h1 {
color: #1C1678;
font-family: Poppins;
Expand Down Expand Up @@ -32,9 +57,10 @@ h1 {

.table-buttons {
display: flex;
position: absolute;
top: -90px; /* Adjust as needed */
right: 0;
justify-content: flex-end; /* Align buttons to the right */
gap: 10px; /* Space between buttons */
margin-top: 20px;
margin-bottom: 20px;
}

.admin-button {
Expand Down

0 comments on commit 26662cb

Please sign in to comment.