diff --git a/backend/routes/questions.py b/backend/routes/questions.py
index df16d5f..3bc1481 100644
--- a/backend/routes/questions.py
+++ b/backend/routes/questions.py
@@ -7,8 +7,65 @@ bp = Blueprint('questions', __name__, url_prefix='/api/questions')
@bp.route('', methods=['GET'])
def list_questions():
- """Get all questions"""
- questions = Question.query.order_by(Question.created_at.desc()).all()
+ """Get all questions with optional filtering and sorting
+
+ Query parameters:
+ - search: Search in question content and answer (case-insensitive)
+ - category: Filter by category name (exact match, or 'none' for uncategorized)
+ - type: Filter by question type (text, image, youtube_audio)
+ - sort_by: Field to sort by (created_at, category, type, question_content, answer)
+ - sort_order: Sort direction (asc, desc) - default: desc
+ """
+ query = Question.query
+
+ # Search filter
+ search = request.args.get('search', '').strip()
+ if search:
+ search_pattern = f'%{search}%'
+ query = query.filter(
+ db.or_(
+ Question.question_content.ilike(search_pattern),
+ Question.answer.ilike(search_pattern)
+ )
+ )
+
+ # Category filter
+ category = request.args.get('category', '').strip()
+ if category:
+ if category.lower() == 'none':
+ query = query.filter(Question.category.is_(None))
+ else:
+ query = query.filter(Question.category == category)
+
+ # Type filter
+ question_type = request.args.get('type', '').strip()
+ if question_type:
+ try:
+ query = query.filter(Question.type == QuestionType(question_type))
+ except ValueError:
+ pass # Invalid type, ignore filter
+
+ # Sorting
+ sort_by = request.args.get('sort_by', 'created_at').strip()
+ sort_order = request.args.get('sort_order', 'desc').strip().lower()
+
+ # Map sort_by to column
+ sort_columns = {
+ 'created_at': Question.created_at,
+ 'category': Question.category,
+ 'type': Question.type,
+ 'question_content': Question.question_content,
+ 'answer': Question.answer,
+ }
+
+ sort_column = sort_columns.get(sort_by, Question.created_at)
+
+ if sort_order == 'asc':
+ query = query.order_by(sort_column.asc())
+ else:
+ query = query.order_by(sort_column.desc())
+
+ questions = query.all()
return jsonify([q.to_dict(include_answer=True) for q in questions]), 200
diff --git a/frontend/frontend/src/components/questionbank/QuestionBankView.jsx b/frontend/frontend/src/components/questionbank/QuestionBankView.jsx
index bcc6475..0508820 100644
--- a/frontend/frontend/src/components/questionbank/QuestionBankView.jsx
+++ b/frontend/frontend/src/components/questionbank/QuestionBankView.jsx
@@ -29,14 +29,29 @@ export default function QuestionBankView() {
const [downloadJob, setDownloadJob] = useState(null);
const [downloadProgress, setDownloadProgress] = useState(0);
+ // Filter and sort state
+ const [searchTerm, setSearchTerm] = useState("");
+ const [filterCategory, setFilterCategory] = useState("");
+ const [filterType, setFilterType] = useState("");
+ const [sortBy, setSortBy] = useState("created_at");
+ const [sortOrder, setSortOrder] = useState("desc");
+
useEffect(() => {
loadQuestions();
loadCategories();
- }, []);
+ }, [searchTerm, filterCategory, filterType, sortBy, sortOrder]);
const loadQuestions = async () => {
try {
- const response = await questionsAPI.getAll();
+ const params = {
+ sort_by: sortBy,
+ sort_order: sortOrder,
+ };
+ if (searchTerm) params.search = searchTerm;
+ if (filterCategory) params.category = filterCategory;
+ if (filterType) params.type = filterType;
+
+ const response = await questionsAPI.getAll(params);
setQuestions(response.data);
} catch (error) {
console.error("Error loading questions:", error);
@@ -295,6 +310,28 @@ export default function QuestionBankView() {
}
};
+ const handleSort = (column) => {
+ if (sortBy === column) {
+ setSortOrder(sortOrder === "asc" ? "desc" : "asc");
+ } else {
+ setSortBy(column);
+ setSortOrder("asc");
+ }
+ };
+
+ const SortIndicator = ({ column }) => {
+ if (sortBy !== column) return ↕;
+ return {sortOrder === "asc" ? "↑" : "↓"};
+ };
+
+ const sortableHeaderStyle = {
+ padding: "0.75rem",
+ textAlign: "left",
+ borderBottom: "2px solid #ddd",
+ cursor: "pointer",
+ userSelect: "none",
+ };
+
return (
<>