diff --git a/backend/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/backend/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst index 7b9469e..8012ba6 100644 --- a/backend/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst +++ b/backend/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst @@ -1,11 +1,16 @@ C:\Users\deves\OneDrive\project_main\Freshers-Hub\backend\src\main\java\com\rit\portal\config\DataInitializer.java C:\Users\deves\OneDrive\project_main\Freshers-Hub\backend\src\main\java\com\rit\portal\config\WebConfig.java +C:\Users\deves\OneDrive\project_main\Freshers-Hub\backend\src\main\java\com\rit\portal\controller\BusRouteController.java C:\Users\deves\OneDrive\project_main\Freshers-Hub\backend\src\main\java\com\rit\portal\controller\CommunityQuestionController.java C:\Users\deves\OneDrive\project_main\Freshers-Hub\backend\src\main\java\com\rit\portal\controller\NotePyqController.java +C:\Users\deves\OneDrive\project_main\Freshers-Hub\backend\src\main\java\com\rit\portal\entity\BusRoute.java +C:\Users\deves\OneDrive\project_main\Freshers-Hub\backend\src\main\java\com\rit\portal\entity\BusStop.java C:\Users\deves\OneDrive\project_main\Freshers-Hub\backend\src\main\java\com\rit\portal\entity\CommunityAnswer.java C:\Users\deves\OneDrive\project_main\Freshers-Hub\backend\src\main\java\com\rit\portal\entity\CommunityQuestion.java C:\Users\deves\OneDrive\project_main\Freshers-Hub\backend\src\main\java\com\rit\portal\entity\NotePyq.java C:\Users\deves\OneDrive\project_main\Freshers-Hub\backend\src\main\java\com\rit\portal\PortalApplication.java +C:\Users\deves\OneDrive\project_main\Freshers-Hub\backend\src\main\java\com\rit\portal\repository\BusRouteRepository.java +C:\Users\deves\OneDrive\project_main\Freshers-Hub\backend\src\main\java\com\rit\portal\repository\BusStopRepository.java C:\Users\deves\OneDrive\project_main\Freshers-Hub\backend\src\main\java\com\rit\portal\repository\CommunityAnswerRepository.java C:\Users\deves\OneDrive\project_main\Freshers-Hub\backend\src\main\java\com\rit\portal\repository\CommunityQuestionRepository.java C:\Users\deves\OneDrive\project_main\Freshers-Hub\backend\src\main\java\com\rit\portal\repository\NotePyqRepository.java diff --git a/src/pages/Community/Community.tsx b/src/pages/Community/Community.tsx index e8b1eea..a4e087d 100644 --- a/src/pages/Community/Community.tsx +++ b/src/pages/Community/Community.tsx @@ -7,7 +7,7 @@ import { import SectionTitle from '@/components/SectionTitle/SectionTitle'; import { StaggerContainer, StaggerItem } from '@/components/AnimatedContainer/AnimatedContainer'; import AnimatedContainer from '@/components/AnimatedContainer/AnimatedContainer'; -import { QUESTIONS_DATA, CONFESSIONS_DATA } from '@/constants'; +import { CONFESSIONS_DATA } from '@/constants'; type Tab = 'qa' | 'confession'; @@ -74,13 +74,12 @@ export default function Community() { const [searchQuery, setSearchQuery] = useState(''); const [likedIds, setLikedIds] = useState>(new Set()); const [expandedQuestionIds, setExpandedQuestionIds] = useState>(new Set()); - // Server-side pagination + const [expandedBodyIds, setExpandedBodyIds] = useState>(new Set()); + const [sortFilter, setSortFilter] = useState<'recent' | 'liked' | 'answered' | 'unanswered'>('recent'); + // Client-side pagination state const [currentPage, setCurrentPage] = useState(0); - const [totalPages, setTotalPages] = useState(1); const [questions, setQuestions] = useState([]); const [loading, setLoading] = useState(false); - // Fallback static questions for when backend is unavailable - const [staticQuestions] = useState(QUESTIONS_DATA); const toggleAnswers = (id: string) => { setExpandedQuestionIds((prev) => { @@ -94,37 +93,35 @@ export default function Community() { }); }; - const fetchQuestions = async (page = 0) => { + const fetchQuestions = async () => { setLoading(true); try { - const response = await fetch(`http://localhost:8080/api/questions/paged?page=${page}&size=${PAGE_SIZE}`); + const response = await fetch(`http://localhost:8080/api/questions`); if (response.ok) { const data = await response.json(); - // Spring Page response: { content: [], totalPages, totalElements, number } - setQuestions(data.content || []); - setTotalPages(data.totalPages || 1); - setCurrentPage(data.number ?? page); + // Backend returns oldest-first, reverse to show newest first + setQuestions(Array.isArray(data) ? [...data].reverse() : []); } else { - console.error("Failed to fetch questions from backend: HTTP status", response.status); - // Fallback: slice static questions - const start = page * PAGE_SIZE; - setQuestions(staticQuestions.slice(start, start + PAGE_SIZE)); - setTotalPages(Math.ceil(staticQuestions.length / PAGE_SIZE)); - setCurrentPage(page); + setQuestions([]); } - } catch (error) { - console.error('Backend not available. Falling back to local static questions data.', error); - const start = page * PAGE_SIZE; - setQuestions(staticQuestions.slice(start, start + PAGE_SIZE)); - setTotalPages(Math.ceil(staticQuestions.length / PAGE_SIZE)); - setCurrentPage(page); + } catch { + // Backend unavailable — show empty state + setQuestions([]); } finally { setLoading(false); } }; useEffect(() => { - fetchQuestions(0); + const savedLikes = localStorage.getItem('qa-liked-ids'); + if (savedLikes) { + try { + setLikedIds(new Set(JSON.parse(savedLikes))); + } catch (e) { + console.error('Failed to parse liked IDs from localStorage', e); + } + } + fetchQuestions(); }, []); const getAnswersCount = (q: any) => { @@ -138,17 +135,37 @@ export default function Community() { return votesVal; }; - // Client-side filter applied on top of current page (for search within page) - const filteredQuestions = questions.filter((q) => { + // Client-side filter and sorting derived state + let processedQuestions = questions.filter((q) => { const titleVal = q.title || ""; const tagsVal = q.tags || []; return titleVal.toLowerCase().includes(searchQuery.toLowerCase()) || tagsVal.some((t: string) => t.toLowerCase().includes(searchQuery.toLowerCase())); }); + if (sortFilter === 'recent') { + processedQuestions.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()); + } else if (sortFilter === 'liked') { + processedQuestions.sort((a, b) => getVotesCount(b) - getVotesCount(a)); + } else if (sortFilter === 'answered') { + processedQuestions.sort((a, b) => getAnswersCount(b) - getAnswersCount(a)); + } else if (sortFilter === 'unanswered') { + processedQuestions = processedQuestions.filter(q => !q.isAnswered && getAnswersCount(q) === 0); + } + + const filteredQuestions = processedQuestions; + const totalPages = Math.max(1, Math.ceil(filteredQuestions.length / PAGE_SIZE)); + const paginatedQuestions = filteredQuestions.slice(currentPage * PAGE_SIZE, (currentPage + 1) * PAGE_SIZE); + + // Reset page when search or filter changes + useEffect(() => { + setCurrentPage(0); + }, [searchQuery, sortFilter]); + const handlePageChange = (newPage: number) => { setExpandedQuestionIds(new Set()); // collapse any open answers - fetchQuestions(newPage); + setExpandedBodyIds(new Set()); // collapse bodies too + setCurrentPage(newPage); // Scroll back to the top of the questions list smoothly document.getElementById('qa-questions-list')?.scrollIntoView({ behavior: 'smooth', block: 'start' }); }; @@ -160,23 +177,58 @@ export default function Community() { setTimeout(() => setConfessionPosted(false), 3000); }; - const toggleLike = async (id: string) => { + const handleLike = async (id: string) => { + let isLikedNow = false; setLikedIds((prev) => { const next = new Set(prev); - if (next.has(id)) next.delete(id); else next.add(id); + if (next.has(id)) { + next.delete(id); + isLikedNow = false; + } else { + next.add(id); + isLikedNow = true; + } + localStorage.setItem('qa-liked-ids', JSON.stringify(Array.from(next))); return next; }); + // Optimistically update count (+1 if liked, -1 if unliked) + setQuestions((prev) => + prev.map((q) => { + if (q.id.toString() === id) { + const votesVal = typeof q.upvotes === 'number' ? q.upvotes : (typeof q.votes === 'number' ? q.votes : 0); + return { ...q, upvotes: isLikedNow ? votesVal + 1 : Math.max(0, votesVal - 1) }; + } + return q; + }) + ); + try { - await fetch(`http://localhost:8080/api/questions/${id}/upvote`, { - method: 'POST' - }); - fetchQuestions(); + if (isLikedNow) { + await fetch(`http://localhost:8080/api/questions/${id}/upvote`, { + method: 'POST' + }); + fetchQuestions(); + } } catch (error) { console.log('Backend not available for upvote sync.', error); } }; + const handleCardClick = (id: string) => { + const idStr = id.toString(); + setExpandedQuestionIds((prev) => { + const next = new Set(prev); + if (next.has(idStr)) next.delete(idStr); else next.add(idStr); + return next; + }); + setExpandedBodyIds((prev) => { + const next = new Set(prev); + if (next.has(idStr)) next.delete(idStr); else next.add(idStr); + return next; + }); + }; + const handlePostQuestion = async () => { if (!questionText.trim()) return; @@ -232,7 +284,7 @@ export default function Community() { {/* Header */}
-

+

RIT Community

@@ -280,16 +332,15 @@ export default function Community() { transition={{ duration: 0.25 }} >

-
- {/* Ask Question Box */} -
-

Ask a Question

+
{/* Ask Question Box */} +
+

Ask a Question