mirror of
https://github.com/pezkuwichain/pwap.git
synced 2026-07-07 08:27:26 +00:00
feat(telegram): redesign all sections with production-quality UI
- Rewrite AnnouncementsSection with shadcn/ui Card components - Rewrite ForumSection with proper thread/reply UI - Rewrite APKSection with version history and download features - All sections now use consistent design patterns - Turkish localization for all UI text - Bottom navigation instead of sidebar - Remove obsolete sub-components (AnnouncementCard, ThreadCard, etc.) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,91 +1,405 @@
|
||||
import { useState } from 'react';
|
||||
import { MessageCircle, Plus, RefreshCw, Search } from 'lucide-react';
|
||||
import { ThreadCard, ForumThread } from './ThreadCard';
|
||||
import { ThreadView, ForumReply } from './ThreadView';
|
||||
import { useTelegram } from '../../hooks/useTelegram';
|
||||
import { usePezkuwi } from '@/contexts/PezkuwiContext';
|
||||
import { useWallet } from '@/contexts/WalletContext';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Skeleton } from '@/components/ui/skeleton';
|
||||
import {
|
||||
MessageCircle, Plus, RefreshCw, Search, Pin, Clock, User,
|
||||
Eye, ChevronRight, ArrowLeft, Send, ThumbsUp, MessageSquare, Hash
|
||||
} from 'lucide-react';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
// Mock data - will be replaced with API calls
|
||||
export interface ForumThread {
|
||||
id: string;
|
||||
title: string;
|
||||
content: string;
|
||||
author: string;
|
||||
authorAddress?: string;
|
||||
createdAt: Date;
|
||||
replyCount: number;
|
||||
viewCount: number;
|
||||
lastReplyAt?: Date;
|
||||
lastReplyAuthor?: string;
|
||||
isPinned?: boolean;
|
||||
tags?: string[];
|
||||
}
|
||||
|
||||
export interface ForumReply {
|
||||
id: string;
|
||||
content: string;
|
||||
author: string;
|
||||
authorAddress?: string;
|
||||
createdAt: Date;
|
||||
likes: number;
|
||||
userLiked?: boolean;
|
||||
}
|
||||
|
||||
// Mock data
|
||||
const mockThreads: ForumThread[] = [
|
||||
{
|
||||
id: '1',
|
||||
title: 'Welcome to Pezkuwi Forum!',
|
||||
content: 'This is the official community forum for Pezkuwi citizens. Feel free to discuss anything related to our digital state, governance, development, and more.\n\nPlease be respectful and follow our community guidelines.',
|
||||
title: 'Pezkuwi Forum\'a Hoş Geldiniz!',
|
||||
content: 'Bu, Pezkuwi vatandaşları için resmi topluluk forumudur. Dijital devletimiz, yönetişim, geliştirme ve daha fazlası hakkında serbestçe tartışabilirsiniz.\n\nLütfen saygılı olun ve topluluk kurallarımıza uyun.',
|
||||
author: 'Admin',
|
||||
createdAt: new Date(Date.now() - 1000 * 60 * 60 * 24 * 7),
|
||||
replyCount: 45,
|
||||
viewCount: 1234,
|
||||
isPinned: true,
|
||||
tags: ['announcement', 'rules'],
|
||||
tags: ['duyuru', 'kurallar'],
|
||||
lastReplyAt: new Date(Date.now() - 1000 * 60 * 30),
|
||||
lastReplyAuthor: 'NewCitizen',
|
||||
lastReplyAuthor: 'YeniVatandaş',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
title: 'How to stake HEZ and earn rewards?',
|
||||
content: 'Hi everyone! I just got my first HEZ tokens and want to start staking. Can someone explain the process step by step? What is the minimum amount required?',
|
||||
author: 'CryptoNewbie',
|
||||
title: 'HEZ nasıl stake edilir ve ödül kazanılır?',
|
||||
content: 'Herkese merhaba! İlk HEZ tokenlarımı aldım ve stake etmeye başlamak istiyorum. Biri adım adım süreci açıklayabilir mi? Minimum miktar ne kadar?',
|
||||
author: 'KriptoYeni',
|
||||
createdAt: new Date(Date.now() - 1000 * 60 * 60 * 5),
|
||||
replyCount: 12,
|
||||
viewCount: 256,
|
||||
tags: ['staking', 'help'],
|
||||
tags: ['staking', 'yardım'],
|
||||
lastReplyAt: new Date(Date.now() - 1000 * 60 * 60 * 2),
|
||||
lastReplyAuthor: 'StakingPro',
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
title: 'Proposal: Add support for Kurdish language in the app',
|
||||
content: 'As a Kurdish digital state, I think it would be great to have full Kurdish language support (Kurmanci and Sorani) in all our applications.\n\nWhat do you think?',
|
||||
author: 'KurdishDev',
|
||||
title: 'Öneri: Uygulamaya Kürtçe dil desteği eklenmeli',
|
||||
content: 'Kürt dijital devleti olarak, tüm uygulamalarımızda tam Kürtçe dil desteği (Kurmancî ve Soranî) olması gerektiğini düşünüyorum.\n\nNe düşünüyorsunuz?',
|
||||
author: 'KürtGeliştirici',
|
||||
createdAt: new Date(Date.now() - 1000 * 60 * 60 * 24 * 2),
|
||||
replyCount: 28,
|
||||
viewCount: 567,
|
||||
tags: ['proposal', 'localization'],
|
||||
tags: ['öneri', 'yerelleştirme'],
|
||||
lastReplyAt: new Date(Date.now() - 1000 * 60 * 60 * 4),
|
||||
lastReplyAuthor: 'LanguageExpert',
|
||||
lastReplyAuthor: 'DilUzmanı',
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
title: 'Bug report: Wallet balance not updating',
|
||||
content: 'After making a transfer, my wallet balance doesn\'t update immediately. I have to refresh the page multiple times. Is anyone else experiencing this issue?',
|
||||
author: 'TechUser',
|
||||
title: 'Hata: Cüzdan bakiyesi güncellenmiyor',
|
||||
content: 'Transfer yaptıktan sonra cüzdan bakiyem hemen güncellenmiyor. Sayfayı birkaç kez yenilemem gerekiyor. Bu sorunu yaşayan başka var mı?',
|
||||
author: 'TeknikKullanıcı',
|
||||
createdAt: new Date(Date.now() - 1000 * 60 * 60 * 12),
|
||||
replyCount: 8,
|
||||
viewCount: 89,
|
||||
tags: ['bug', 'wallet'],
|
||||
tags: ['hata', 'cüzdan'],
|
||||
lastReplyAt: new Date(Date.now() - 1000 * 60 * 60 * 6),
|
||||
lastReplyAuthor: 'DevTeam',
|
||||
lastReplyAuthor: 'GeliştiriciEkibi',
|
||||
},
|
||||
];
|
||||
|
||||
const mockReplies: ForumReply[] = [
|
||||
{
|
||||
id: '1',
|
||||
content: 'Great to be here! Looking forward to participating in the community.',
|
||||
author: 'NewCitizen',
|
||||
content: 'Burada olmak harika! Topluluğa katılmak için sabırsızlanıyorum.',
|
||||
author: 'YeniVatandaş',
|
||||
createdAt: new Date(Date.now() - 1000 * 60 * 30),
|
||||
likes: 5,
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
content: 'Welcome! Make sure to check out the staking guide in the docs section.',
|
||||
author: 'Helper',
|
||||
content: 'Hoş geldiniz! Dokümantasyon bölümündeki staking rehberini kontrol etmeyi unutmayın.',
|
||||
author: 'Yardımcı',
|
||||
createdAt: new Date(Date.now() - 1000 * 60 * 60),
|
||||
likes: 12,
|
||||
},
|
||||
];
|
||||
|
||||
export function Forum() {
|
||||
function ThreadCard({ thread, onClick }: { thread: ForumThread; onClick: () => void }) {
|
||||
const { hapticSelection } = useTelegram();
|
||||
|
||||
const handleClick = () => {
|
||||
hapticSelection();
|
||||
onClick();
|
||||
};
|
||||
|
||||
const formatDate = (date: Date) => {
|
||||
const now = new Date();
|
||||
const diff = now.getTime() - date.getTime();
|
||||
const hours = Math.floor(diff / (1000 * 60 * 60));
|
||||
const days = Math.floor(hours / 24);
|
||||
|
||||
if (hours < 1) return 'Az önce';
|
||||
if (hours < 24) return `${hours}s önce`;
|
||||
if (days < 7) return `${days}g önce`;
|
||||
return date.toLocaleDateString('tr-TR', { day: 'numeric', month: 'short' });
|
||||
};
|
||||
|
||||
return (
|
||||
<Card
|
||||
className={cn(
|
||||
"bg-gray-900 border-gray-800 cursor-pointer hover:border-gray-700 transition-all",
|
||||
thread.isPinned && "border-l-4 border-l-blue-500"
|
||||
)}
|
||||
onClick={handleClick}
|
||||
>
|
||||
<CardContent className="p-4">
|
||||
<div className="flex items-start justify-between gap-3">
|
||||
<div className="flex-1 min-w-0">
|
||||
{/* Title with pin badge */}
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
{thread.isPinned && (
|
||||
<Badge className="bg-blue-500/20 text-blue-500 border-blue-500/30 text-xs">
|
||||
<Pin className="w-3 h-3 mr-1" />
|
||||
Sabit
|
||||
</Badge>
|
||||
)}
|
||||
<h3 className="font-medium text-white truncate">{thread.title}</h3>
|
||||
</div>
|
||||
|
||||
{/* Preview */}
|
||||
<p className="text-gray-400 text-sm mb-3 line-clamp-2">
|
||||
{thread.content.length > 100 ? thread.content.slice(0, 100) + '...' : thread.content}
|
||||
</p>
|
||||
|
||||
{/* Tags */}
|
||||
{thread.tags && thread.tags.length > 0 && (
|
||||
<div className="flex flex-wrap gap-1.5 mb-3">
|
||||
{thread.tags.slice(0, 3).map(tag => (
|
||||
<div
|
||||
key={tag}
|
||||
className="flex items-center gap-1 text-xs bg-gray-800 text-gray-300 px-2 py-1 rounded-md"
|
||||
>
|
||||
<Hash className="w-3 h-3" />
|
||||
{tag}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Meta info */}
|
||||
<div className="flex items-center gap-4 text-xs text-gray-500">
|
||||
<div className="flex items-center gap-1">
|
||||
<User className="w-3 h-3" />
|
||||
<span>{thread.author}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<Clock className="w-3 h-3" />
|
||||
<span>{formatDate(thread.createdAt)}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<MessageCircle className="w-3 h-3" />
|
||||
<span>{thread.replyCount}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<Eye className="w-3 h-3" />
|
||||
<span>{thread.viewCount}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ChevronRight className="w-5 h-5 text-gray-500 flex-shrink-0" />
|
||||
</div>
|
||||
|
||||
{/* Last reply info */}
|
||||
{thread.lastReplyAt && thread.lastReplyAuthor && (
|
||||
<div className="mt-3 pt-3 border-t border-gray-800 text-xs text-gray-500">
|
||||
Son yanıt: <span className="text-gray-400">{thread.lastReplyAuthor}</span>{' '}
|
||||
· {formatDate(thread.lastReplyAt)}
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
function ThreadView({
|
||||
thread,
|
||||
replies,
|
||||
onBack,
|
||||
onReply,
|
||||
onLikeReply,
|
||||
isConnected
|
||||
}: {
|
||||
thread: ForumThread;
|
||||
replies: ForumReply[];
|
||||
onBack: () => void;
|
||||
onReply: (content: string) => void;
|
||||
onLikeReply: (replyId: string) => void;
|
||||
isConnected: boolean;
|
||||
}) {
|
||||
const { hapticImpact } = useTelegram();
|
||||
const [replyContent, setReplyContent] = useState('');
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
const formatDate = (date: Date) => {
|
||||
return date.toLocaleDateString('tr-TR', {
|
||||
day: 'numeric',
|
||||
month: 'short',
|
||||
year: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
});
|
||||
};
|
||||
|
||||
const handleSubmitReply = async () => {
|
||||
if (!replyContent.trim() || isSubmitting) return;
|
||||
|
||||
setIsSubmitting(true);
|
||||
hapticImpact('medium');
|
||||
|
||||
try {
|
||||
await onReply(replyContent);
|
||||
setReplyContent('');
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full bg-gray-950">
|
||||
{/* Header */}
|
||||
<div className="flex items-center gap-3 p-4 border-b border-gray-800">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={onBack}
|
||||
className="h-8 w-8"
|
||||
>
|
||||
<ArrowLeft className="w-4 h-4 text-gray-400" />
|
||||
</Button>
|
||||
<h2 className="text-white font-semibold truncate flex-1">{thread.title}</h2>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="flex-1 overflow-y-auto">
|
||||
{/* Original post */}
|
||||
<div className="p-4 border-b border-gray-800">
|
||||
<Card className="bg-gray-900 border-gray-800">
|
||||
<CardContent className="p-4">
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<div className="w-10 h-10 rounded-full bg-gradient-to-br from-blue-500 to-cyan-600 flex items-center justify-center">
|
||||
<User className="w-5 h-5 text-white" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-white font-medium text-sm">{thread.author}</p>
|
||||
<div className="flex items-center gap-1 text-xs text-gray-400">
|
||||
<Clock className="w-3 h-3" />
|
||||
<span>{formatDate(thread.createdAt)}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Tags */}
|
||||
{thread.tags && thread.tags.length > 0 && (
|
||||
<div className="flex flex-wrap gap-1.5 mb-3">
|
||||
{thread.tags.map(tag => (
|
||||
<Badge key={tag} variant="outline" className="text-gray-400 border-gray-700 text-xs">
|
||||
<Hash className="w-3 h-3 mr-1" />
|
||||
{tag}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<p className="text-gray-300 text-sm leading-relaxed whitespace-pre-wrap">
|
||||
{thread.content}
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* Replies */}
|
||||
<div className="p-4">
|
||||
<div className="flex items-center gap-2 mb-4">
|
||||
<MessageSquare className="w-4 h-4 text-blue-500" />
|
||||
<span className="text-white font-medium text-sm">
|
||||
{replies.length} Yanıt
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
{replies.map(reply => (
|
||||
<Card key={reply.id} className="bg-gray-900 border-gray-800">
|
||||
<CardContent className="p-3">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-8 h-8 rounded-full bg-gray-800 flex items-center justify-center">
|
||||
<User className="w-4 h-4 text-gray-400" />
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-sm font-medium text-white">{reply.author}</span>
|
||||
<span className="text-xs text-gray-500 ml-2">
|
||||
{formatDate(reply.createdAt)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => onLikeReply(reply.id)}
|
||||
className={cn(
|
||||
"flex items-center gap-1 px-2 py-1 rounded-md text-xs transition-all",
|
||||
reply.userLiked
|
||||
? "bg-green-500/20 text-green-500"
|
||||
: "bg-gray-800 text-gray-500 hover:text-green-500"
|
||||
)}
|
||||
>
|
||||
<ThumbsUp className={cn("w-3 h-3", reply.userLiked && "fill-current")} />
|
||||
<span>{reply.likes}</span>
|
||||
</button>
|
||||
</div>
|
||||
<p className="text-gray-300 text-sm whitespace-pre-wrap">{reply.content}</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
|
||||
{replies.length === 0 && (
|
||||
<div className="text-center text-gray-500 py-8">
|
||||
<MessageCircle className="w-10 h-10 mx-auto mb-2 opacity-50" />
|
||||
<p>Henüz yanıt yok. İlk yanıtı siz yazın!</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Reply input */}
|
||||
{isConnected ? (
|
||||
<div className="p-4 border-t border-gray-800 bg-gray-900">
|
||||
<div className="flex gap-2">
|
||||
<Input
|
||||
value={replyContent}
|
||||
onChange={(e) => setReplyContent(e.target.value)}
|
||||
placeholder="Yanıtınızı yazın..."
|
||||
className="flex-1 bg-gray-800 border-gray-700 text-white placeholder:text-gray-500"
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
e.preventDefault();
|
||||
handleSubmitReply();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
onClick={handleSubmitReply}
|
||||
disabled={!replyContent.trim() || isSubmitting}
|
||||
className="bg-blue-600 hover:bg-blue-700"
|
||||
>
|
||||
<Send className="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="p-4 border-t border-gray-800 bg-gray-900 text-center text-gray-500 text-sm">
|
||||
Yanıt yazmak için cüzdanınızı bağlayın
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function ForumSection() {
|
||||
const { hapticNotification, showAlert } = useTelegram();
|
||||
const { selectedAccount } = usePezkuwi();
|
||||
const { isConnected } = useWallet();
|
||||
|
||||
const [threads, setThreads] = useState<ForumThread[]>(mockThreads);
|
||||
const [selectedThread, setSelectedThread] = useState<ForumThread | null>(null);
|
||||
const [replies, setReplies] = useState<ForumReply[]>(mockReplies);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
|
||||
const isConnected = !!selectedAccount;
|
||||
|
||||
const handleRefresh = async () => {
|
||||
setIsLoading(true);
|
||||
hapticNotification('success');
|
||||
@@ -96,11 +410,10 @@ export function Forum() {
|
||||
|
||||
const handleCreateThread = () => {
|
||||
if (!isConnected) {
|
||||
showAlert('Please connect your wallet to create a thread');
|
||||
showAlert('Konu oluşturmak için cüzdanınızı bağlayın');
|
||||
return;
|
||||
}
|
||||
// TODO: Implement thread creation modal
|
||||
showAlert('Thread creation coming soon!');
|
||||
showAlert('Konu oluşturma özelliği yakında!');
|
||||
};
|
||||
|
||||
const handleReply = async (content: string) => {
|
||||
@@ -109,7 +422,7 @@ export function Forum() {
|
||||
const newReply: ForumReply = {
|
||||
id: String(Date.now()),
|
||||
content,
|
||||
author: selectedAccount?.meta?.name || 'Anonymous',
|
||||
author: selectedAccount?.meta?.name || 'Anonim',
|
||||
createdAt: new Date(),
|
||||
likes: 0,
|
||||
};
|
||||
@@ -155,57 +468,81 @@ export function Forum() {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full">
|
||||
<div className="flex flex-col h-full overflow-y-auto bg-gray-950">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between p-4 border-b border-gray-800">
|
||||
<div className="flex items-center gap-2">
|
||||
<MessageCircle className="w-5 h-5 text-green-500" />
|
||||
<h2 className="text-lg font-semibold text-white">Forum</h2>
|
||||
<div className="p-4 pb-0">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<h2 className="text-white font-semibold text-lg flex items-center gap-2">
|
||||
<MessageCircle className="w-5 h-5 text-blue-500" />
|
||||
Forum
|
||||
</h2>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={handleRefresh}
|
||||
disabled={isLoading}
|
||||
className="h-8 w-8"
|
||||
>
|
||||
<RefreshCw className={cn("w-4 h-4 text-gray-400", isLoading && "animate-spin")} />
|
||||
</Button>
|
||||
<Button
|
||||
size="icon"
|
||||
onClick={handleCreateThread}
|
||||
className="h-8 w-8 bg-blue-600 hover:bg-blue-700"
|
||||
>
|
||||
<Plus className="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
onClick={handleRefresh}
|
||||
disabled={isLoading}
|
||||
className="p-2 rounded-lg bg-gray-800 hover:bg-gray-700 transition-colors disabled:opacity-50"
|
||||
>
|
||||
<RefreshCw className={`w-4 h-4 text-gray-400 ${isLoading ? 'animate-spin' : ''}`} />
|
||||
</button>
|
||||
<button
|
||||
onClick={handleCreateThread}
|
||||
className="p-2 rounded-lg bg-green-600 hover:bg-green-700 transition-colors"
|
||||
>
|
||||
<Plus className="w-4 h-4 text-white" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Search */}
|
||||
<div className="p-4 border-b border-gray-800">
|
||||
<div className="relative">
|
||||
{/* Search */}
|
||||
<div className="relative mb-4">
|
||||
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-gray-500" />
|
||||
<input
|
||||
<Input
|
||||
type="text"
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
placeholder="Search threads..."
|
||||
className="w-full bg-gray-800 border border-gray-700 rounded-lg pl-9 pr-3 py-2 text-white text-sm placeholder-gray-500 focus:outline-none focus:border-green-500"
|
||||
placeholder="Konularda ara..."
|
||||
className="pl-9 bg-gray-900 border-gray-800 text-white placeholder:text-gray-500"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Stats */}
|
||||
<div className="flex items-center gap-4 mb-4">
|
||||
<div className="flex items-center gap-2 px-3 py-1.5 bg-gray-900 rounded-lg">
|
||||
<MessageSquare className="w-4 h-4 text-blue-500" />
|
||||
<span className="text-gray-300 text-sm">{threads.length} Konu</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 px-3 py-1.5 bg-gray-900 rounded-lg">
|
||||
<Pin className="w-4 h-4 text-blue-500" />
|
||||
<span className="text-gray-300 text-sm">
|
||||
{threads.filter(t => t.isPinned).length} Sabitlenmiş
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="flex-1 overflow-y-auto p-4 space-y-3">
|
||||
{sortedThreads.length === 0 ? (
|
||||
<div className="flex flex-col items-center justify-center h-full text-gray-500">
|
||||
{/* Thread List */}
|
||||
<div className="flex-1 p-4 pt-0 space-y-3">
|
||||
{isLoading ? (
|
||||
<>
|
||||
{[...Array(4)].map((_, i) => (
|
||||
<Skeleton key={i} className="h-32 bg-gray-800" />
|
||||
))}
|
||||
</>
|
||||
) : sortedThreads.length === 0 ? (
|
||||
<div className="flex flex-col items-center justify-center h-64 text-gray-500">
|
||||
<MessageCircle className="w-12 h-12 mb-3 opacity-50" />
|
||||
<p>{searchQuery ? 'No threads found' : 'No threads yet'}</p>
|
||||
<p>{searchQuery ? 'Konu bulunamadı' : 'Henüz konu yok'}</p>
|
||||
{!searchQuery && (
|
||||
<button
|
||||
<Button
|
||||
variant="link"
|
||||
onClick={handleCreateThread}
|
||||
className="mt-4 text-green-500 hover:text-green-400"
|
||||
className="mt-2 text-blue-500"
|
||||
>
|
||||
Create the first thread
|
||||
</button>
|
||||
İlk konuyu oluştur
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
@@ -222,4 +559,4 @@ export function Forum() {
|
||||
);
|
||||
}
|
||||
|
||||
export default Forum;
|
||||
export default ForumSection;
|
||||
|
||||
Reference in New Issue
Block a user