| 1 | import React from 'react'
|
|---|
| 2 | import { useNavigate } from 'react-router-dom'
|
|---|
| 3 | import { Shield, Users, BookOpen, AlertTriangle, Settings, ChevronRight } from 'lucide-react'
|
|---|
| 4 | import { useAuthStore } from '../../store/authStore'
|
|---|
| 5 | import { useStoryStore } from '../../store/storyStore'
|
|---|
| 6 | import { useNotificationStore } from '../../store/notificationStore'
|
|---|
| 7 | import { PlatformStats } from '../../components/admin/PlatformStats'
|
|---|
| 8 | import { Button } from '../../components/ui/Button'
|
|---|
| 9 | import { RoleBadge } from '../../components/ui/Badge'
|
|---|
| 10 | import { Avatar } from '../../components/ui/Avatar'
|
|---|
| 11 |
|
|---|
| 12 | function timeAgo(str: string): string {
|
|---|
| 13 | const diff = Date.now() - new Date(str).getTime()
|
|---|
| 14 | const m = Math.floor(diff / 60000)
|
|---|
| 15 | if (m < 1) return 'just now'
|
|---|
| 16 | if (m < 60) return `${m}m ago`
|
|---|
| 17 | const h = Math.floor(m / 60)
|
|---|
| 18 | if (h < 24) return `${h}h ago`
|
|---|
| 19 | return `${Math.floor(h / 24)}d ago`
|
|---|
| 20 | }
|
|---|
| 21 |
|
|---|
| 22 | export const AdminDashboard: React.FC = () => {
|
|---|
| 23 | const navigate = useNavigate()
|
|---|
| 24 | const { allUsers } = useAuthStore()
|
|---|
| 25 | const { stories } = useStoryStore()
|
|---|
| 26 | const { notifications } = useNotificationStore()
|
|---|
| 27 |
|
|---|
| 28 | const recentUsers = [...allUsers].sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime()).slice(0, 5)
|
|---|
| 29 | const recentStories = [...stories].sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime()).slice(0, 5)
|
|---|
| 30 |
|
|---|
| 31 | const adminLinks = [
|
|---|
| 32 | { to: '/admin/users', icon: <Users size={20} className="text-blue-400" />, label: 'Manage Users', desc: `${allUsers.length} registered users`, color: 'bg-blue-500/10 border-blue-500/20' },
|
|---|
| 33 | { to: '/admin/content', icon: <BookOpen size={20} className="text-violet-400" />, label: 'Content Moderation', desc: `${stories.length} total stories`, color: 'bg-violet-500/10 border-violet-500/20' },
|
|---|
| 34 | { to: '/admin/genres', icon: <Settings size={20} className="text-amber-400" />, label: 'Manage Genres', desc: 'Add or remove genres', color: 'bg-amber-500/10 border-amber-500/20' },
|
|---|
| 35 | ]
|
|---|
| 36 |
|
|---|
| 37 | return (
|
|---|
| 38 | <div className="max-w-7xl mx-auto px-4 py-8">
|
|---|
| 39 | {/* Header */}
|
|---|
| 40 | <div className="flex items-center gap-3 mb-8">
|
|---|
| 41 | <div className="w-10 h-10 rounded-xl bg-rose-500/20 flex items-center justify-center">
|
|---|
| 42 | <Shield size={20} className="text-rose-400" />
|
|---|
| 43 | </div>
|
|---|
| 44 | <div>
|
|---|
| 45 | <h1 className="font-serif text-3xl font-bold text-white">Admin Dashboard</h1>
|
|---|
| 46 | <p className="text-slate-400 text-sm mt-0.5">Platform management and moderation</p>
|
|---|
| 47 | </div>
|
|---|
| 48 | </div>
|
|---|
| 49 |
|
|---|
| 50 | {/* Platform stats */}
|
|---|
| 51 | <div className="mb-8">
|
|---|
| 52 | <h2 className="font-serif text-xl font-bold text-white mb-4">Platform Overview</h2>
|
|---|
| 53 | <PlatformStats />
|
|---|
| 54 | </div>
|
|---|
| 55 |
|
|---|
| 56 | {/* Quick links */}
|
|---|
| 57 | <div className="grid grid-cols-1 sm:grid-cols-3 gap-4 mb-8">
|
|---|
| 58 | {adminLinks.map(link => (
|
|---|
| 59 | <button
|
|---|
| 60 | key={link.to}
|
|---|
| 61 | onClick={() => navigate(link.to)}
|
|---|
| 62 | className={`flex items-center gap-4 p-5 rounded-2xl border ${link.color} hover:scale-105 transition-all group text-left`}
|
|---|
| 63 | >
|
|---|
| 64 | <div className="w-12 h-12 rounded-xl bg-slate-800 flex items-center justify-center">
|
|---|
| 65 | {link.icon}
|
|---|
| 66 | </div>
|
|---|
| 67 | <div className="flex-1">
|
|---|
| 68 | <p className="text-white font-semibold">{link.label}</p>
|
|---|
| 69 | <p className="text-slate-400 text-sm">{link.desc}</p>
|
|---|
| 70 | </div>
|
|---|
| 71 | <ChevronRight size={16} className="text-slate-500 group-hover:text-white transition-colors" />
|
|---|
| 72 | </button>
|
|---|
| 73 | ))}
|
|---|
| 74 | </div>
|
|---|
| 75 |
|
|---|
| 76 | <div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
|---|
| 77 | {/* Recent users */}
|
|---|
| 78 | <div className="bg-slate-800 border border-slate-700 rounded-2xl p-6">
|
|---|
| 79 | <div className="flex items-center justify-between mb-4">
|
|---|
| 80 | <h3 className="font-serif text-lg font-bold text-white">Recent Users</h3>
|
|---|
| 81 | <Button size="sm" variant="ghost" onClick={() => navigate('/admin/users')}>
|
|---|
| 82 | View all <ChevronRight size={14} />
|
|---|
| 83 | </Button>
|
|---|
| 84 | </div>
|
|---|
| 85 | <div className="space-y-3">
|
|---|
| 86 | {recentUsers.map(user => (
|
|---|
| 87 | <div key={user.user_id} className="flex items-center gap-3">
|
|---|
| 88 | <Avatar name={user.name} size="sm" />
|
|---|
| 89 | <div className="flex-1 min-w-0">
|
|---|
| 90 | <p className="text-white text-sm font-medium">{user.name} {user.surname}</p>
|
|---|
| 91 | <p className="text-slate-500 text-xs">@{user.username}</p>
|
|---|
| 92 | </div>
|
|---|
| 93 | <RoleBadge role={user.role} />
|
|---|
| 94 | </div>
|
|---|
| 95 | ))}
|
|---|
| 96 | </div>
|
|---|
| 97 | </div>
|
|---|
| 98 |
|
|---|
| 99 | {/* Recent stories */}
|
|---|
| 100 | <div className="bg-slate-800 border border-slate-700 rounded-2xl p-6">
|
|---|
| 101 | <div className="flex items-center justify-between mb-4">
|
|---|
| 102 | <h3 className="font-serif text-lg font-bold text-white">Recent Stories</h3>
|
|---|
| 103 | <Button size="sm" variant="ghost" onClick={() => navigate('/admin/content')}>
|
|---|
| 104 | View all <ChevronRight size={14} />
|
|---|
| 105 | </Button>
|
|---|
| 106 | </div>
|
|---|
| 107 | <div className="space-y-3">
|
|---|
| 108 | {recentStories.map(story => (
|
|---|
| 109 | <div key={story.story_id} className="flex items-center gap-3">
|
|---|
| 110 | <div className="flex-1 min-w-0">
|
|---|
| 111 | <p className="text-white text-sm font-medium truncate">{story.title}</p>
|
|---|
| 112 | <p className="text-slate-500 text-xs">by @{story.author_username}</p>
|
|---|
| 113 | </div>
|
|---|
| 114 | <span className={`text-xs px-2 py-0.5 rounded-full ${
|
|---|
| 115 | story.status === 'published'
|
|---|
| 116 | ? 'bg-emerald-500/20 text-emerald-400'
|
|---|
| 117 | : story.status === 'draft'
|
|---|
| 118 | ? 'bg-amber-500/20 text-amber-400'
|
|---|
| 119 | : 'bg-slate-500/20 text-slate-400'
|
|---|
| 120 | }`}>
|
|---|
| 121 | {story.status}
|
|---|
| 122 | </span>
|
|---|
| 123 | </div>
|
|---|
| 124 | ))}
|
|---|
| 125 | </div>
|
|---|
| 126 | </div>
|
|---|
| 127 | </div>
|
|---|
| 128 | </div>
|
|---|
| 129 | )
|
|---|
| 130 | }
|
|---|