AeThex-Connect/src/frontend/mockup/ForumChannel.jsx

293 lines
9.7 KiB
JavaScript

import React, { useState } from 'react';
const mockPosts = [
{
id: 1,
title: 'How to set up AeThex Passport for your game?',
author: { name: 'Trevor', avatar: 'T', color: '#ff0000' },
content: 'I want to integrate Passport authentication into my Unity game. What are the steps?',
tags: ['question', 'passport', 'unity'],
replies: 12,
reactions: { '👍': 24, '❤️': 5 },
pinned: true,
createdAt: '2 hours ago',
lastActivity: '15 minutes ago',
},
{
id: 2,
title: '[Tutorial] Complete Nexus SDK Integration Guide',
author: { name: 'Sarah', avatar: 'S', color: '#ffa500' },
content: 'Step-by-step guide for integrating the Nexus SDK into your application...',
tags: ['tutorial', 'nexus', 'sdk'],
replies: 45,
reactions: { '🔥': 89, '👍': 67, '❤️': 23 },
pinned: true,
createdAt: '1 day ago',
lastActivity: '30 minutes ago',
},
{
id: 3,
title: 'Bug: Voice chat not working on Linux',
author: { name: 'DevUser_456', avatar: 'D' },
content: 'Getting an error when trying to join voice channels on Ubuntu 22.04...',
tags: ['bug', 'linux', 'voice'],
replies: 8,
reactions: { '👍': 12 },
pinned: false,
createdAt: '5 hours ago',
lastActivity: '1 hour ago',
},
{
id: 4,
title: 'Show off your AeThex-powered projects!',
author: { name: 'Anderson', avatar: 'A', color: '#5865f2' },
content: 'Share what you\'ve built with AeThex tools and get feedback from the community.',
tags: ['showcase', 'community'],
replies: 156,
reactions: { '🚀': 234, '❤️': 145, '👏': 89 },
pinned: false,
createdAt: '3 days ago',
lastActivity: '10 minutes ago',
},
];
const tags = [
{ id: 'all', label: 'All Posts', color: '#5865f2' },
{ id: 'question', label: 'Question', color: '#3ba55d' },
{ id: 'tutorial', label: 'Tutorial', color: '#faa61a' },
{ id: 'bug', label: 'Bug Report', color: '#ed4245' },
{ id: 'showcase', label: 'Showcase', color: '#9b59b6' },
{ id: 'discussion', label: 'Discussion', color: '#747f8d' },
];
export default function ForumChannel({ channel, onClose }) {
const [activeTag, setActiveTag] = useState('all');
const [sortBy, setSortBy] = useState('activity');
const [search, setSearch] = useState('');
const [selectedPost, setSelectedPost] = useState(null);
const channelData = channel || {
name: 'help-forum',
description: 'Get help with AeThex products and services',
guidelines: 'Be respectful, search before posting, use appropriate tags.',
};
const filteredPosts = mockPosts.filter(post => {
const matchesTag = activeTag === 'all' || post.tags.includes(activeTag);
const matchesSearch = !search ||
post.title.toLowerCase().includes(search.toLowerCase()) ||
post.content.toLowerCase().includes(search.toLowerCase());
return matchesTag && matchesSearch;
});
const sortedPosts = [...filteredPosts].sort((a, b) => {
if (a.pinned && !b.pinned) return -1;
if (!a.pinned && b.pinned) return 1;
return 0;
});
if (selectedPost) {
return (
<ForumPost
post={selectedPost}
onBack={() => setSelectedPost(null)}
/>
);
}
return (
<div className="forum-channel">
<div className="forum-header">
<div className="forum-title">
<span className="forum-icon">💬</span>
<h2>{channelData.name}</h2>
</div>
<p className="forum-description">{channelData.description}</p>
</div>
<div className="forum-toolbar">
<div className="forum-search">
<span className="search-icon">🔍</span>
<input
type="text"
placeholder="Search posts..."
value={search}
onChange={(e) => setSearch(e.target.value)}
/>
</div>
<div className="forum-filters">
<select
value={sortBy}
onChange={(e) => setSortBy(e.target.value)}
className="sort-select"
>
<option value="activity">Recent Activity</option>
<option value="newest">Newest</option>
<option value="oldest">Oldest</option>
<option value="popular">Most Popular</option>
</select>
</div>
<button className="new-post-btn">
+ New Post
</button>
</div>
<div className="forum-tags">
{tags.map(tag => (
<button
key={tag.id}
className={`tag-btn ${activeTag === tag.id ? 'active' : ''}`}
style={activeTag === tag.id ? { background: tag.color } : undefined}
onClick={() => setActiveTag(tag.id)}
>
{tag.label}
</button>
))}
</div>
<div className="forum-posts">
{sortedPosts.map(post => (
<div
key={post.id}
className={`forum-post-card ${post.pinned ? 'pinned' : ''}`}
onClick={() => setSelectedPost(post)}
>
{post.pinned && <span className="pinned-badge">📌 Pinned</span>}
<div className="post-main">
<div
className="post-avatar"
style={post.author.color ? { background: post.author.color } : undefined}
>
{post.author.avatar}
</div>
<div className="post-content">
<h3 className="post-title">{post.title}</h3>
<p className="post-preview">{post.content}</p>
<div className="post-tags">
{post.tags.map(tagId => {
const tag = tags.find(t => t.id === tagId);
return tag ? (
<span
key={tagId}
className="post-tag"
style={{ background: tag.color }}
>
{tag.label}
</span>
) : null;
})}
</div>
</div>
</div>
<div className="post-meta">
<div className="post-stats">
<span className="reply-count">💬 {post.replies}</span>
{Object.entries(post.reactions).map(([emoji, count]) => (
<span key={emoji} className="reaction-count">{emoji} {count}</span>
))}
</div>
<div className="post-time">
<span className="author-name">{post.author.name}</span>
<span></span>
<span>Last activity {post.lastActivity}</span>
</div>
</div>
</div>
))}
</div>
{filteredPosts.length === 0 && (
<div className="forum-empty">
<span className="empty-icon">📭</span>
<p>No posts found</p>
</div>
)}
</div>
);
}
function ForumPost({ post, onBack }) {
const [replyContent, setReplyContent] = useState('');
const replies = [
{ id: 1, author: { name: 'Marcus', avatar: 'M' }, content: 'Have you checked the documentation? There\'s a great guide at docs.aethex.com/passport', time: '1 hour ago', reactions: { '👍': 5 } },
{ id: 2, author: { name: 'Sarah', avatar: 'S', color: '#ffa500' }, content: 'I wrote a tutorial for this! Check out post #2 in this forum.', time: '45 minutes ago', reactions: { '❤️': 3 } },
];
return (
<div className="forum-post-detail">
<button className="back-btn" onClick={onBack}>
Back to posts
</button>
<div className="post-header">
<h1>{post.title}</h1>
<div className="post-author-info">
<div
className="author-avatar"
style={post.author.color ? { background: post.author.color } : undefined}
>
{post.author.avatar}
</div>
<span className="author-name">{post.author.name}</span>
<span className="post-date">Posted {post.createdAt}</span>
</div>
</div>
<div className="post-body">
<p>{post.content}</p>
</div>
<div className="post-reactions">
{Object.entries(post.reactions).map(([emoji, count]) => (
<button key={emoji} className="reaction-btn">
{emoji} {count}
</button>
))}
<button className="reaction-btn add">+</button>
</div>
<div className="post-replies">
<h3>Replies ({replies.length})</h3>
{replies.map(reply => (
<div key={reply.id} className="reply-card">
<div
className="reply-avatar"
style={reply.author.color ? { background: reply.author.color } : undefined}
>
{reply.author.avatar}
</div>
<div className="reply-content">
<div className="reply-header">
<span className="reply-author">{reply.author.name}</span>
<span className="reply-time">{reply.time}</span>
</div>
<p>{reply.content}</p>
<div className="reply-actions">
{Object.entries(reply.reactions).map(([emoji, count]) => (
<button key={emoji} className="reaction-btn small">
{emoji} {count}
</button>
))}
<button className="reply-btn">Reply</button>
</div>
</div>
</div>
))}
</div>
<div className="reply-input">
<textarea
placeholder="Write a reply..."
value={replyContent}
onChange={(e) => setReplyContent(e.target.value)}
/>
<button className="send-reply-btn" disabled={!replyContent.trim()}>
Send Reply
</button>
</div>
</div>
);
}