source: chapterx-frontend/src/components/admin/ContentModerationTable.tsx@ b373fea

main
Last change on this file since b373fea was b62cefc, checked in by kikisrbinoska <srbinoskakristina07@…>, 4 months ago

Added frontend and imporoved AI Suggestions service

  • Property mode set to 100644
File size: 5.3 KB
Line 
1import React, { useState } from 'react'
2import { Trash2, Eye, AlertTriangle } from 'lucide-react'
3import { useStoryStore } from '../../store/storyStore'
4import { useNotificationStore } from '../../store/notificationStore'
5import { useUIStore } from '../../store/uiStore'
6import { Story } from '../../types'
7import { StatusBadge, GenreBadge } from '../ui/Badge'
8import { Button } from '../ui/Button'
9import { Modal } from '../ui/Modal'
10import { useNavigate } from 'react-router-dom'
11
12export const ContentModerationTable: React.FC = () => {
13 const { stories, deleteStory } = useStoryStore()
14 const { addNotification } = useNotificationStore()
15 const { addToast } = useUIStore()
16 const navigate = useNavigate()
17 const [confirmStory, setConfirmStory] = useState<Story | null>(null)
18
19 const handleDelete = (story: Story) => {
20 deleteStory(story.story_id)
21 addNotification({
22 user_id: story.user_id,
23 type: 'system',
24 title: 'Story Removed',
25 message: `Your story "${story.title}" has been removed by an administrator.`,
26 })
27 addToast(`"${story.title}" removed from platform`, 'info')
28 setConfirmStory(null)
29 }
30
31 const formatDate = (str: string) =>
32 new Date(str).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })
33
34 return (
35 <div>
36 <div className="overflow-x-auto rounded-xl border border-slate-700">
37 <table className="w-full text-sm">
38 <thead>
39 <tr className="border-b border-slate-700 bg-slate-800">
40 <th className="text-left px-4 py-3 text-slate-400 font-medium">Story</th>
41 <th className="text-left px-4 py-3 text-slate-400 font-medium hidden md:table-cell">Author</th>
42 <th className="text-left px-4 py-3 text-slate-400 font-medium hidden lg:table-cell">Genres</th>
43 <th className="text-left px-4 py-3 text-slate-400 font-medium">Status</th>
44 <th className="text-left px-4 py-3 text-slate-400 font-medium hidden sm:table-cell">Published</th>
45 <th className="text-right px-4 py-3 text-slate-400 font-medium">Actions</th>
46 </tr>
47 </thead>
48 <tbody className="divide-y divide-slate-800">
49 {stories.map(story => (
50 <tr key={story.story_id} className="hover:bg-slate-800/50 transition-colors">
51 <td className="px-4 py-3">
52 <div>
53 <p className="text-white font-medium line-clamp-1">{story.title}</p>
54 {story.mature_content && (
55 <span className="text-xs text-rose-400">18+</span>
56 )}
57 </div>
58 </td>
59 <td className="px-4 py-3 text-slate-400 hidden md:table-cell">@{story.author_username}</td>
60 <td className="px-4 py-3 hidden lg:table-cell">
61 <div className="flex flex-wrap gap-1">
62 {story.genres.slice(0, 2).map(g => <GenreBadge key={g} genre={g} />)}
63 </div>
64 </td>
65 <td className="px-4 py-3"><StatusBadge status={story.status} /></td>
66 <td className="px-4 py-3 text-slate-500 hidden sm:table-cell">{formatDate(story.created_at)}</td>
67 <td className="px-4 py-3">
68 <div className="flex items-center justify-end gap-2">
69 <button
70 onClick={() => navigate(`/story/${story.story_id}`)}
71 className="text-slate-400 hover:text-white transition-colors p-1"
72 >
73 <Eye size={14} />
74 </button>
75 <button
76 onClick={() => setConfirmStory(story)}
77 className="text-slate-400 hover:text-rose-400 transition-colors p-1"
78 >
79 <Trash2 size={14} />
80 </button>
81 </div>
82 </td>
83 </tr>
84 ))}
85 </tbody>
86 </table>
87 {stories.length === 0 && (
88 <div className="text-center py-10 text-slate-500">No stories to moderate.</div>
89 )}
90 </div>
91
92 {/* Confirm modal */}
93 <Modal
94 isOpen={!!confirmStory}
95 onClose={() => setConfirmStory(null)}
96 title="Remove Story"
97 size="sm"
98 >
99 {confirmStory && (
100 <div className="space-y-4">
101 <div className="flex items-start gap-3 p-3 bg-rose-500/10 rounded-xl border border-rose-500/20">
102 <AlertTriangle size={18} className="text-rose-400 flex-shrink-0 mt-0.5" />
103 <div>
104 <p className="text-white font-medium text-sm">"{confirmStory.title}"</p>
105 <p className="text-slate-400 text-xs mt-1">
106 This will permanently remove the story and notify the author. This action cannot be undone.
107 </p>
108 </div>
109 </div>
110 <div className="flex gap-3">
111 <Button variant="secondary" className="flex-1" onClick={() => setConfirmStory(null)}>
112 Cancel
113 </Button>
114 <Button variant="danger" className="flex-1" onClick={() => handleDelete(confirmStory)}>
115 <Trash2 size={14} />
116 Remove Story
117 </Button>
118 </div>
119 </div>
120 )}
121 </Modal>
122 </div>
123 )
124}
Note: See TracBrowser for help on using the repository browser.