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

main
Last change on this file since e882b92 was e882b92, checked in by kikisrbinoska <srbinoskakristina07@…>, 13 days ago

Added corrected entitef from the ER diagram and changes for the logic to be coresponding to the ddl

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