293 lines
9.7 KiB
JavaScript
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>
|
|
);
|
|
}
|