@ieltsrealtest/ui
Version:
Reusable UI components for IELTS Real Test platform, built with React and TypeScript.
114 lines (113 loc) • 8.43 kB
JavaScript
'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;