diff --git a/frontend/src/api/mockData.js b/frontend/src/api/mockData.js new file mode 100644 index 0000000..9d03a53 --- /dev/null +++ b/frontend/src/api/mockData.js @@ -0,0 +1,195 @@ +/** + * MOCK DATA FOR DEVELOPMENT + * + * This file provides mock data to test the frontend without a backend server. + * Remove this file when the actual backend API is implemented. + */ + +export const mockQuestions = [ + { + _id: '1', + questionText: 'What is the difference between var, let, and const in JavaScript?', + company: 'Google', + topic: 'JavaScript', + role: 'Frontend Developer', + difficulty: 'Medium', + upvotes: 42, + createdAt: '2024-10-15T10:30:00Z', + updatedAt: '2024-10-15T10:30:00Z' + }, + { + _id: '2', + questionText: 'Explain the concept of closures in JavaScript with an example.', + company: 'Meta', + topic: 'JavaScript', + role: 'Full Stack Developer', + difficulty: 'Hard', + upvotes: 38, + createdAt: '2024-10-14T14:20:00Z', + updatedAt: '2024-10-14T14:20:00Z' + }, + { + _id: '3', + questionText: 'What is React Virtual DOM and how does it work?', + company: 'Netflix', + topic: 'React', + role: 'Frontend Developer', + difficulty: 'Medium', + upvotes: 56, + createdAt: '2024-10-13T09:15:00Z', + updatedAt: '2024-10-13T09:15:00Z' + }, + { + _id: '4', + questionText: 'How do you implement authentication in a React application?', + company: 'Airbnb', + topic: 'React', + role: 'Frontend Developer', + difficulty: 'Hard', + upvotes: 73, + createdAt: '2024-10-12T16:45:00Z', + updatedAt: '2024-10-12T16:45:00Z' + }, + { + _id: '5', + questionText: 'What are React Hooks and why were they introduced?', + company: 'Uber', + topic: 'React', + role: 'Frontend Developer', + difficulty: 'Easy', + upvotes: 29, + createdAt: '2024-10-11T11:30:00Z', + updatedAt: '2024-10-11T11:30:00Z' + }, + { + _id: '6', + questionText: 'Explain the difference between SQL and NoSQL databases.', + company: 'Amazon', + topic: 'Database', + role: 'Backend Developer', + difficulty: 'Medium', + upvotes: 45, + createdAt: '2024-10-10T13:20:00Z', + updatedAt: '2024-10-10T13:20:00Z' + }, + { + _id: '7', + questionText: 'What is RESTful API design and what are its principles?', + company: 'Microsoft', + topic: 'API Design', + role: 'Backend Developer', + difficulty: 'Medium', + upvotes: 51, + createdAt: '2024-10-09T15:10:00Z', + updatedAt: '2024-10-09T15:10:00Z' + }, + { + _id: '8', + questionText: 'How do you optimize database queries for better performance?', + company: 'Oracle', + topic: 'Database', + role: 'Database Administrator', + difficulty: 'Hard', + upvotes: 67, + createdAt: '2024-10-08T12:00:00Z', + updatedAt: '2024-10-08T12:00:00Z' + } +]; + +export const mockCategories = { + companies: ['Google', 'Meta', 'Netflix', 'Airbnb', 'Uber', 'Amazon', 'Microsoft', 'Oracle'], + topics: ['JavaScript', 'React', 'Database', 'API Design', 'Node.js', 'Python', 'System Design'], + roles: ['Frontend Developer', 'Backend Developer', 'Full Stack Developer', 'Database Administrator', 'DevOps Engineer'] +}; + +/** + * Mock API functions that simulate server responses + */ +export const mockGetAllQuestions = (filters = {}) => { + return new Promise((resolve) => { + setTimeout(() => { + let filteredQuestions = [...mockQuestions]; + + // Filter by company + if (filters.company) { + filteredQuestions = filteredQuestions.filter(q => + q.company.toLowerCase().includes(filters.company.toLowerCase()) + ); + } + + // Filter by topic + if (filters.topic) { + filteredQuestions = filteredQuestions.filter(q => + q.topic.toLowerCase().includes(filters.topic.toLowerCase()) + ); + } + + // Filter by role + if (filters.role) { + filteredQuestions = filteredQuestions.filter(q => + q.role.toLowerCase().includes(filters.role.toLowerCase()) + ); + } + + // Filter by difficulty + if (filters.difficulty) { + filteredQuestions = filteredQuestions.filter(q => + q.difficulty === filters.difficulty + ); + } + + // Sort questions + if (filters.sort) { + switch (filters.sort) { + case 'latest': + filteredQuestions.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt)); + break; + case 'oldest': + filteredQuestions.sort((a, b) => new Date(a.createdAt) - new Date(b.createdAt)); + break; + case 'upvotes': + filteredQuestions.sort((a, b) => b.upvotes - a.upvotes); + break; + default: + // Default to latest + filteredQuestions.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt)); + } + } + + resolve({ + success: true, + questions: filteredQuestions, + total: filteredQuestions.length + }); + }, 500); // Simulate network delay + }); +}; + +export const mockGetCategories = () => { + return new Promise((resolve) => { + setTimeout(() => { + resolve({ + success: true, + ...mockCategories + }); + }, 300); + }); +}; + +export const mockSearchQuestions = (keyword) => { + return new Promise((resolve) => { + setTimeout(() => { + const searchResults = mockQuestions.filter(q => + q.questionText.toLowerCase().includes(keyword.toLowerCase()) || + q.company.toLowerCase().includes(keyword.toLowerCase()) || + q.topic.toLowerCase().includes(keyword.toLowerCase()) + ); + + resolve({ + success: true, + questions: searchResults, + total: searchResults.length + }); + }, 400); + }); +}; \ No newline at end of file diff --git a/frontend/src/api/questionApi.js b/frontend/src/api/questionApi.js index 09a4e3c..b99b848 100644 --- a/frontend/src/api/questionApi.js +++ b/frontend/src/api/questionApi.js @@ -25,6 +25,7 @@ */ import api from './axios'; +import { mockGetAllQuestions, mockGetCategories, mockSearchQuestions } from './mockData'; /** * TODO: IMPLEMENT GET ALL QUESTIONS @@ -59,9 +60,23 @@ import api from './axios'; */ export const getAllQuestions = async (filters = {}) => { - // TODO: Implement get all questions with filters - console.log('Get all questions API not implemented yet'); - throw new Error('Get all questions API not implemented'); + try { + const params = new URLSearchParams(); + + // Add all filter parameters to the URL + Object.keys(filters).forEach(key => { + if (filters[key]) { + params.append(key, filters[key]); + } + }); + + const response = await api.get(`/questions?${params.toString()}`); + return response.data; + } catch (error) { + console.error('Error fetching questions from API, falling back to mock data:', error); + // Fall back to mock data when backend is not available + return mockGetAllQuestions(filters); + } }; /** @@ -167,9 +182,14 @@ export const getQuestionUpvotes = async (id) => { */ export const searchQuestions = async (keyword) => { - // TODO: Implement search - console.log('Search questions API not implemented yet'); - throw new Error('Search questions API not implemented'); + try { + const response = await api.get(`/questions/search?q=${encodeURIComponent(keyword)}`); + return response.data; + } catch (error) { + console.error('Error searching questions from API, falling back to mock data:', error); + // Fall back to mock data when backend is not available + return mockSearchQuestions(keyword); + } }; /** @@ -187,7 +207,12 @@ export const searchQuestions = async (keyword) => { */ export const getCategories = async () => { - // TODO: Implement get categories - console.log('Get categories API not implemented yet'); - throw new Error('Get categories API not implemented'); + try { + const response = await api.get('/categories'); + return response.data; + } catch (error) { + console.error('Error fetching categories from API, falling back to mock data:', error); + // Fall back to mock data when backend is not available + return mockGetCategories(); + } }; diff --git a/frontend/src/components/QuestionCard.jsx b/frontend/src/components/QuestionCard.jsx index 7ccc4ec..a264c2e 100644 --- a/frontend/src/components/QuestionCard.jsx +++ b/frontend/src/components/QuestionCard.jsx @@ -73,9 +73,13 @@ const QuestionCard = ({ question, onUpvote, showActions = true }) => { return colors[difficulty] || 'bg-gray-100 text-gray-800'; }; - // TODO: Implement upvote handler + // Implement upvote handler const handleUpvote = (e) => { - + e.preventDefault(); // Prevent navigation when clicking upvote + e.stopPropagation(); // Prevent event bubbling + if (onUpvote) { + onUpvote(question._id); + } }; // TODO: Format relative time (e.g., "2 days ago") @@ -99,8 +103,10 @@ const QuestionCard = ({ question, onUpvote, showActions = true }) => { onClick={handleUpvote} className="flex items-center space-x-1 text-gray-600 hover:text-red-500 transition" > - {/* TODO: Add heart or arrow icon */} - + {/* Heart icon */} + {question.upvotes || 0} )} diff --git a/frontend/src/pages/Questions.jsx b/frontend/src/pages/Questions.jsx index f889835..bd8f96b 100644 --- a/frontend/src/pages/Questions.jsx +++ b/frontend/src/pages/Questions.jsx @@ -27,10 +27,10 @@ * - Pagination */ -import { useState, useEffect } from 'react'; -// TODO: Import API functions +import { useState, useEffect, useCallback } from 'react'; +// Import API functions import { getAllQuestions, getCategories, searchQuestions } from '../api/questionApi'; -// import QuestionCard from '../components/QuestionCard'; +import QuestionCard from '../components/QuestionCard'; // import Loading from '../components/Loading'; /** @@ -85,11 +85,26 @@ const Questions = () => { roles: [], }); - // TODO: Fetch questions on mount and when filters change - useEffect(() => { - // fetchQuestions(); + // Fetch questions function + const fetchQuestions = useCallback(async () => { + setLoading(true); + setError(''); + try { + const response = await getAllQuestions(filters); + setQuestions(response.questions || []); + } catch (err) { + setError('Failed to load questions. Please try again later.'); + console.error('Error fetching questions:', err); + } finally { + setLoading(false); + } }, [filters]); + // Fetch questions on mount and when filters change + useEffect(() => { + fetchQuestions(); + }, [fetchQuestions]); + //Fetch categories function for filters const fetchCategories = async () => { setCategoriesLoading(true); @@ -112,7 +127,36 @@ const Questions = () => { } } - // TODO: Fetch categories for filters + // Handle search functionality + const handleSearch = useCallback(async () => { + if (searchQuery.trim()) { + setLoading(true); + setError(''); + try { + const response = await searchQuestions(searchQuery); + setQuestions(response.questions || []); + } catch (err) { + setError('Failed to search questions. Please try again later.'); + console.error('Error searching questions:', err); + } finally { + setLoading(false); + } + } else { + // If search is empty, fetch all questions with current filters + fetchQuestions(); + } + }, [searchQuery, fetchQuestions]); + + // Debounce search queries + useEffect(() => { + const delayedSearch = setTimeout(() => { + handleSearch(); + }, 500); // Wait 500ms after user stops typing + + return () => clearTimeout(delayedSearch); + }, [searchQuery, handleSearch]); + + // Fetch categories for filters useEffect(() => { fetchCategories(); }, []); @@ -146,7 +190,11 @@ const Questions = () => { onChange={(e) => setFilters({ ...filters, company: e.target.value })} > - {/* TODO: Map categories.companies */} + {categories.companies.map((company) => ( + + ))} + + + {/* Error Message */} + {error && ( +
No questions found. Try adjusting your filters.
+No questions found. Try adjusting your filters or search terms.
Question cards will appear here once API is implemented
-