Changeset 99c1e45 for chapterx-frontend/src/components/admin
- Timestamp:
- 06/24/26 16:28:50 (12 days ago)
- Branches:
- main
- Children:
- a8f4a2d
- Parents:
- 0b502c2
- Location:
- chapterx-frontend/src/components/admin
- Files:
-
- 2 edited
-
PlatformStats.tsx (modified) (1 diff)
-
UserTable.tsx (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
-
chapterx-frontend/src/components/admin/PlatformStats.tsx
r0b502c2 r99c1e45 1 import React from 'react'2 import { Users, BookOpen, FileText, Heart, Eye, MessageCircle } from 'lucide-react'1 import React, { useEffect } from 'react' 2 import { Users, BookOpen, Heart, MessageCircle } from 'lucide-react' 3 3 import { useAuthStore } from '../../store/authStore' 4 4 import { useStoryStore } from '../../store/storyStore' 5 5 6 6 export const PlatformStats: React.FC = () => { 7 const { allUsers } = useAuthStore()8 const { stories, comments } = useStoryStore()7 const { allUsers, fetchAllUsers } = useAuthStore() 8 const { stories, fetchStories } = useStoryStore() 9 9 10 const totalViews = stories.reduce((acc, s) => acc + s.total_views, 0) 10 useEffect(() => { fetchStories(); fetchAllUsers() }, []) 11 11 12 const totalLikes = stories.reduce((acc, s) => acc + s.total_likes, 0) 13 const totalComments = stories.reduce((acc, s) => acc + s.total_comments, 0) 12 14 const published = stories.filter(s => s.status === 'published').length 13 15 14 16 const stats = [ 15 { icon: <Users size={24} className="text-blue-300" />, label: 'Total Users', value: allUsers.length, color: 'bg-blue-500/20', change: '+12 this month'},17 { icon: <Users size={24} className="text-blue-300" />, label: 'Total Users', value: allUsers.length, color: 'bg-blue-500/20', change: `${allUsers.filter(u => u.role === 'writer').length} writers` }, 16 18 { icon: <BookOpen size={24} className="text-violet-300" />, label: 'Total Stories', value: stories.length, color: 'bg-violet-500/20', change: `${published} published` }, 17 { icon: <FileText size={24} className="text-emerald-300" />, label: 'Comments', value: comments.length, color: 'bg-emerald-500/20', change: 'Platform-wide' },18 19 { icon: <Heart size={24} className="text-rose-300" />, label: 'Total Likes', value: totalLikes.toLocaleString(), color: 'bg-rose-500/20', change: 'Across all stories' }, 19 { icon: <Eye size={24} className="text-amber-300" />, label: 'Total Views', value: totalViews.toLocaleString(), color: 'bg-amber-500/20', change: 'All time' }, 20 { icon: <MessageCircle size={24} className="text-cyan-300" />, label: 'Writers', value: allUsers.filter(u => u.role === 'writer').length, color: 'bg-cyan-500/20', change: 'Active creators' }, 20 { icon: <MessageCircle size={24} className="text-emerald-300" />, label: 'Total Comments', value: totalComments.toLocaleString(), color: 'bg-emerald-500/20', change: 'Platform-wide' }, 21 21 ] 22 22 23 23 return ( 24 <div className="grid grid-cols-2 md:grid-cols- 3gap-4">24 <div className="grid grid-cols-2 md:grid-cols-4 gap-4"> 25 25 {stats.map(stat => ( 26 26 <div key={stat.label} className="bg-slate-800 border border-slate-700 rounded-2xl p-6"> -
chapterx-frontend/src/components/admin/UserTable.tsx
r0b502c2 r99c1e45 1 1 import React, { useState } from 'react' 2 2 import { Search, Shield, UserX, UserCheck } from 'lucide-react' 3 import axios from 'axios' 3 4 import { useAuthStore } from '../../store/authStore' 4 import { useNotificationStore } from '../../store/notificationStore'5 5 import { useUIStore } from '../../store/uiStore' 6 6 import { User, UserRole } from '../../types' … … 10 10 import { Modal } from '../ui/Modal' 11 11 12 const API = 'https://localhost:7125/api' 13 12 14 export const UserTable: React.FC = () => { 13 const { allUsers, updateUserRole, currentUser } = useAuthStore() 14 const { addNotification } = useNotificationStore() 15 const { allUsers, updateUserRole, currentUser, token } = useAuthStore() 15 16 const { addToast } = useUIStore() 16 17 const [search, setSearch] = useState('') 17 18 const [confirmUser, setConfirmUser] = useState<User | null>(null) 18 19 const [confirmAction, setConfirmAction] = useState<'promote' | 'demote' | null>(null) 20 const [loading, setLoading] = useState(false) 21 22 const authHeaders = token ? { Authorization: `Bearer ${token}` } : {} 19 23 20 24 const filtered = allUsers.filter( … … 25 29 ) 26 30 27 const handlePromote = (user: User) => { 28 const newRole: UserRole = user.role === 'regular' ? 'writer' : user.role === 'writer' ? 'admin' : 'admin' 29 updateUserRole(user.user_id, newRole) 30 addNotification({ 31 user_id: user.user_id, 32 type: 'system', 33 title: 'Role Updated', 34 message: `Your account has been promoted to ${newRole}.`, 35 }) 36 addToast(`${user.username} promoted to ${newRole}`) 37 setConfirmUser(null) 31 const handlePromote = async (user: User) => { 32 setLoading(true) 33 try { 34 await axios.post(`${API}/admins`, { userId: user.user_id }, { headers: authHeaders }) 35 updateUserRole(user.user_id, 'admin') 36 addToast(`${user.username} promoted to admin`) 37 } catch (err: any) { 38 addToast(err?.response?.data?.message || 'Failed to promote user.', 'error') 39 } finally { 40 setLoading(false) 41 setConfirmUser(null) 42 } 38 43 } 39 44 40 const handleDemote = (user: User) => { 41 const newRole: UserRole = user.role === 'admin' ? 'writer' : 'regular' 42 updateUserRole(user.user_id, newRole) 43 addToast(`${user.username} role changed to ${newRole}`, 'info') 44 setConfirmUser(null) 45 const handleDemote = async (user: User) => { 46 setLoading(true) 47 try { 48 await axios.delete(`${API}/admins/${user.user_id}`, { headers: authHeaders }) 49 updateUserRole(user.user_id, 'writer') 50 addToast(`${user.username} removed from admin`, 'info') 51 } catch (err: any) { 52 addToast(err?.response?.data?.message || 'Failed to demote user.', 'error') 53 } finally { 54 setLoading(false) 55 setConfirmUser(null) 56 } 45 57 } 46 58 … … 140 152 variant={confirmAction === 'promote' ? 'primary' : 'danger'} 141 153 className="flex-1" 154 loading={loading} 142 155 onClick={() => confirmAction === 'promote' ? handlePromote(confirmUser) : handleDemote(confirmUser)} 143 156 >
Note:
See TracChangeset
for help on using the changeset viewer.
