import { useEffect, useState } from 'react' import { Button, Badge, Card, Col, Container, Form, Modal, Row } from 'react-bootstrap' import { Link, useParams } from 'react-router-dom' import { createThread, getForum, listAllForums, listThreadsByForum } from '../api/client' import PortalTopicRow from '../components/PortalTopicRow' import { useAuth } from '../context/AuthContext' import { useTranslation } from 'react-i18next' export default function ForumView() { const { id } = useParams() const { token } = useAuth() const [forum, setForum] = useState(null) const [children, setChildren] = useState([]) const [threads, setThreads] = useState([]) const [error, setError] = useState('') const [loading, setLoading] = useState(true) const [showModal, setShowModal] = useState(false) const [title, setTitle] = useState('') const [body, setBody] = useState('') const [saving, setSaving] = useState(false) const { t } = useTranslation() const renderChildRows = (nodes) => nodes.map((node) => (
{node.name}
{node.description || ''}
{node.threads_count ?? 0}
{node.views_count ?? 0}
{node.last_post_at ? (
{t('thread.by')}{' '} {node.last_post_user_id ? ( {node.last_post_user_name || t('thread.anonymous')} ) : ( {node.last_post_user_name || t('thread.anonymous')} )} {node.last_post_at.slice(0, 10)}
) : ( {t('thread.no_replies')} )}
)) const getParentId = (node) => { if (!node.parent) return null if (typeof node.parent === 'string') { return node.parent.split('/').pop() } return node.parent.id ?? null } const buildForumTree = (allForums) => { const map = new Map() const roots = [] allForums.forEach((item) => { map.set(String(item.id), { ...item, children: [] }) }) allForums.forEach((item) => { const parentId = getParentId(item) const node = map.get(String(item.id)) if (parentId && map.has(String(parentId))) { map.get(String(parentId)).children.push(node) } else { roots.push(node) } }) const aggregateNodes = (node) => { if (!node.children?.length) { return { threads: node.threads_count ?? 0, views: node.views_count ?? 0, posts: node.posts_count ?? 0, last: node.last_post_at ? { at: node.last_post_at, node } : null, } } let threads = node.threads_count ?? 0 let views = node.views_count ?? 0 let posts = node.posts_count ?? 0 let last = node.last_post_at ? { at: node.last_post_at, node } : null node.children.forEach((child) => { const agg = aggregateNodes(child) threads += agg.threads views += agg.views posts += agg.posts if (agg.last && (!last || agg.last.at > last.at)) { last = agg.last } }) node.threads_count = threads node.views_count = views node.posts_count = posts if (last) { const source = last.node node.last_post_at = source.last_post_at node.last_post_user_id = source.last_post_user_id node.last_post_user_name = source.last_post_user_name node.last_post_user_rank_color = source.last_post_user_rank_color node.last_post_user_group_color = source.last_post_user_group_color } return { threads, views, posts, last } } roots.forEach((root) => aggregateNodes(root)) return map } useEffect(() => { let active = true const loadData = async () => { setLoading(true) setError('') try { const forumData = await getForum(id) if (!active) return setForum(forumData) const allForums = await listAllForums() if (!active) return const treeMap = buildForumTree(allForums) const currentNode = treeMap.get(String(forumData.id)) setChildren(currentNode?.children ?? []) if (forumData.type === 'forum') { const threadData = await listThreadsByForum(id) if (!active) return setThreads(threadData) } else { setThreads([]) } } catch (err) { if (active) setError(err.message) } finally { if (active) setLoading(false) } } loadData() return () => { active = false } }, [id]) const handleSubmit = async (event) => { event.preventDefault() setSaving(true) setError('') try { await createThread({ title, body, forumId: id }) setTitle('') setBody('') const updated = await listThreadsByForum(id) setThreads(updated) setShowModal(false) } catch (err) { setError(err.message) } finally { setSaving(false) } } return ( {loading &&

{t('forum.loading')}

} {error &&

{error}

} {forum && ( <> {forum.type !== 'forum' && (
{forum.name}
{t('portal.topic')} {t('thread.views')} {t('thread.last_post')}
{children.length > 0 ? ( renderChildRows(children) ) : (
{t('forum.empty_children')}
)}
)} {forum.type === 'forum' && ( <>