UNPKG

@ieltsrealtest/ui

Version:

Reusable UI components for IELTS Real Test platform, built with React and TypeScript.

114 lines (113 loc) 8.43 kB
'use client'; import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; import { useEffect, useState } from 'react'; import CommentCard from './commentcard'; import CommentInput from './commentInput'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faMessage } from '@fortawesome/free-solid-svg-icons'; const COMMENTS_PER_PAGE = 5; const Discussions = () => { const [discussions, setDiscussions] = useState([]); const [loading, setLoading] = useState(true); const [openThreads, setOpenThreads] = useState([]); const [activeReplyId, setActiveReplyId] = useState(null); const [currentPage, setCurrentPage] = useState(1); const [userId, setUserId] = useState(null); useEffect(() => { const fetchData = async () => { try { // Gọi API để lấy user ID const userRes = await fetch(`https://api.youready.net/ielts/user/api/auth/get_user_id/`, { method: "GET", credentials: "include", headers: { "Content-Type": "application/json", }, }); const userData = await userRes.json(); if (!userRes.ok) { // window.location.href = `https://api.youready.net/ielts`; return; } setUserId(userData.user_id); // console.log('User ID:', userId); } catch (error) { // console.error("Lỗi khi gọi API decode_token:", error); } }; fetchData(); }); const fetchDiscussions = async () => { try { const response = await fetch(`https://api.youready.net/ielts/comment/api/discussion/`, { credentials: "include", // Include cookies for authentication }); const json = await response.json(); setDiscussions(json.data); } catch (error) { // console.error('Fetch error:', error); } finally { setLoading(false); } }; useEffect(() => { fetchDiscussions(); }, []); const grouped = discussions.reduce((acc, comment) => { if (!acc[comment.thread_id]) acc[comment.thread_id] = []; acc[comment.thread_id].push(comment); return acc; }, {}); const allMainComments = discussions .filter(c => c.parent_id === null) .sort((a, b) => { if (a.pins && !b.pins) return -1; if (!a.pins && b.pins) return 1; return new Date(b.created_at).getTime() - new Date(a.created_at).getTime(); }); const totalPages = Math.ceil(allMainComments.length / COMMENTS_PER_PAGE); const currentMainThreadIds = allMainComments .slice((currentPage - 1) * COMMENTS_PER_PAGE, currentPage * COMMENTS_PER_PAGE) .map(c => c.thread_id); const paginatedGrouped = Object.fromEntries(Object.entries(grouped) .sort(([, a], [, b]) => { const mainA = a.find(c => c.parent_id === null); const mainB = b.find(c => c.parent_id === null); const timeA = mainA ? new Date(mainA.created_at).getTime() : 0; const timeB = mainB ? new Date(mainB.created_at).getTime() : 0; return timeB - timeA; }) .filter(([threadId]) => currentMainThreadIds.includes(threadId))); if (loading) return _jsx("p", { className: "ml-[100px]", children: "Loading discussions..." }); return (_jsxs("section", { className: "w-full max-w-4xl mx-auto px-4 sm:px-8", children: [_jsxs("h1", { className: "text-[#A11D33] font-bold text-[20px] mb-4", children: [_jsxs("label", { className: 'text-green-500', children: [_jsx(FontAwesomeIcon, { icon: faMessage }), " ", discussions.length] }), " Discussions"] }), userId && (_jsx(CommentInput, { userId: userId, onSuccess: fetchDiscussions, name: "" })), [...Object.entries(paginatedGrouped) .filter(([_, threadComments]) => threadComments.some(c => c.pins)), ...Object.entries(paginatedGrouped) .filter(([_, threadComments]) => !threadComments.some(c => c.pins)), ].map(([threadId, threadComments]) => { const sortedThread = [...threadComments].sort((a, b) => { if (a.pins && !b.pins) return -1; if (!a.pins && b.pins) return 1; return new Date(b.created_at).getTime() - new Date(a.created_at).getTime(); }); const mainComment = sortedThread.find(c => c.parent_id === null); const replies = sortedThread.filter(c => c.parent_id !== null); const isOpen = openThreads.includes(threadId); const visibleReplies = isOpen ? replies : replies.slice(0, 1); if (!mainComment) return null; return (_jsxs("div", { className: "relative my-[20px] pl-[10px]", children: [replies.length > 0 && (_jsx("div", { className: "absolute left-[30px] top-[24px] w-[1px] bg-gray-400 z-0", style: { height: `${visibleReplies.length * 107}px` } })), _jsx("div", { className: "relative z-10", children: _jsx(CommentCard, { id: mainComment._id, avatar: mainComment.avatar, name: mainComment.commenter, comment: mainComment.content, threadId: threadId, userId: userId ? userId : "", isReplying: activeReplyId === mainComment._id, onReplyClick: () => setActiveReplyId(activeReplyId === mainComment._id ? null : mainComment._id), onRefresh: fetchDiscussions, role: mainComment.role, replied_user: "", time: new Date(mainComment.created_at), likes: mainComment.likes, pin: mainComment.pins }) }), visibleReplies.map((reply // Chỉ render nếu userId có giá trị ) => (_jsxs("div", { className: "relative ml-[60px] mt-3 z-10", children: [_jsx("div", { className: "absolute left-[-40px] top-[15px] w-[20px] h-[5px] border-l border-b border-gray-400 rounded-bl-sm z-0" }), _jsx(CommentCard, { id: reply._id, avatar: reply.avatar, name: reply.commenter, comment: reply.content, threadId: threadId, userId: userId ? userId : "", isReplying: activeReplyId === reply._id, onReplyClick: () => setActiveReplyId(activeReplyId === reply._id ? null : reply._id), onRefresh: fetchDiscussions, role: reply.role, replied_user: reply.replied_user || "", time: new Date(reply.created_at), likes: reply.likes, pin: reply.pins })] }, reply._id))), replies.length > 1 && !isOpen && (_jsxs("div", { className: "ml-[70px] text-[16px] hover:underline cursor-pointer text-blue-600 mt-1", onClick: () => setOpenThreads(prev => [...prev, threadId]), children: ["View all ", replies.length, " responses"] })), replies.length > 1 && isOpen && (_jsx("div", { className: "ml-[70px] text-[16px] hover:underline cursor-pointer text-blue-600 mt-1", onClick: () => setOpenThreads(prev => prev.filter(id => id !== threadId)), children: "Hide responses" }))] }, threadId)); }), _jsxs("div", { className: "flex justify-center mt-8 gap-2", children: [_jsx("button", { onClick: () => setCurrentPage(1), disabled: currentPage === 1, className: "px-3 py-1 rounded cursor-pointer bg-gray-200 hover:bg-gray-300 disabled:opacity-50", children: "Begin" }), _jsx("button", { onClick: () => setCurrentPage(prev => Math.max(1, prev - 1)), disabled: currentPage === 1, className: "px-3 py-1 rounded cursor-pointer bg-gray-200 hover:bg-gray-300 disabled:opacity-50", children: "Prev" }), Array.from({ length: totalPages }, (_, i) => (_jsx("button", { onClick: () => setCurrentPage(i + 1), className: `px-3 cursor-pointer py-1 rounded ${currentPage === i + 1 ? 'bg-[#A11D33] text-white' : 'bg-gray-200 text-gray-700 hover:bg-gray-300'}`, children: i + 1 }, i))), _jsx("button", { onClick: () => setCurrentPage(prev => Math.min(totalPages, prev + 1)), disabled: currentPage === totalPages, className: "px-3 py-1 rounded cursor-pointer bg-gray-200 hover:bg-gray-300 disabled:opacity-50", children: "Next" }), _jsx("button", { onClick: () => setCurrentPage(totalPages), disabled: currentPage === totalPages, className: "px-3 py-1 rounded cursor-pointer bg-gray-200 hover:bg-gray-300 disabled:opacity-50", children: "End" })] })] })); }; export default Discussions;