1
1
import * as React from "react" ;
2
- import { useNavigate , Link } from "react-router" ;
2
+ import { useNavigate , Link , useFetcher , useActionData } from "react-router" ;
3
3
import {
4
4
Favorite as FavoriteIcon ,
5
5
Edit as EditIcon ,
6
6
Share as ShareIcon ,
7
- Delete as DeleteIcon
7
+ Delete as DeleteIcon ,
8
+ Flag as FlagIcon
8
9
} from '@mui/icons-material' ;
9
10
import ReactMarkdown from "react-markdown" ;
10
11
import {
@@ -14,7 +15,17 @@ import {
14
15
Chip ,
15
16
IconButton ,
16
17
Container ,
17
- Paper
18
+ Paper ,
19
+ Dialog ,
20
+ DialogTitle ,
21
+ DialogContent ,
22
+ Select ,
23
+ MenuItem ,
24
+ FormControl ,
25
+ InputLabel ,
26
+ TextField ,
27
+ Button ,
28
+ Tooltip
18
29
} from '@mui/material' ;
19
30
20
31
import UserContext , { type UserContextType } from "../contexts/UserContext" ;
@@ -40,17 +51,29 @@ export interface BlogPost {
40
51
description ?: string ;
41
52
}
42
53
54
+ interface ReportReason {
55
+ id : number ;
56
+ name : string ;
57
+ }
58
+
43
59
interface BlogPostDetailProps {
44
60
post : BlogPost ;
45
61
comments ?: any [ ] ;
46
- reportReasons ?: any [ ] ;
62
+ reportReasons ?: ReportReason [ ] ;
47
63
}
48
64
49
65
export default function BlogPostDetail ( { post, comments = [ ] , reportReasons = [ ] } : BlogPostDetailProps ) {
50
66
const navigate = useNavigate ( ) ;
67
+ const fetcher = useFetcher ( ) ;
68
+ const actionData = useActionData ( ) as { success ?: boolean ; error ?: string ; report ?: any } | undefined ;
51
69
const { user, isAuthenticated } = React . useContext < UserContextType > ( UserContext ) ;
52
70
const [ likesCount , setLikesCount ] = React . useState ( post . likes_count ) ;
53
71
const [ isLiked , setIsLiked ] = React . useState ( post . is_liked ) ;
72
+
73
+ // Report dialog state
74
+ const [ reportDialogOpen , setReportDialogOpen ] = React . useState ( false ) ;
75
+ const [ selectedReason , setSelectedReason ] = React . useState ( '' ) ;
76
+ const [ reportDescription , setReportDescription ] = React . useState ( '' ) ;
54
77
55
78
const handleShare = async ( ) => {
56
79
const url = window . location . href ;
@@ -110,6 +133,39 @@ export default function BlogPostDetail({ post, comments = [], reportReasons = []
110
133
}
111
134
} ;
112
135
136
+ const handleOpenReportDialog = ( ) => {
137
+ setReportDialogOpen ( true ) ;
138
+ } ;
139
+
140
+ const handleCloseReportDialog = ( ) => {
141
+ setReportDialogOpen ( false ) ;
142
+ setSelectedReason ( '' ) ;
143
+ setReportDescription ( '' ) ;
144
+ } ;
145
+
146
+ const handleSubmitReport = ( ) => {
147
+ if ( ! selectedReason ) return ;
148
+
149
+ const formData = new FormData ( ) ;
150
+ formData . append ( '_action' , 'reportPost' ) ;
151
+ formData . append ( 'reason' , selectedReason ) ;
152
+ formData . append ( 'description' , reportDescription . trim ( ) ) ;
153
+
154
+ fetcher . submit ( formData , { method : 'post' } ) ;
155
+ handleCloseReportDialog ( ) ;
156
+ } ;
157
+
158
+ // Handle action responses
159
+ React . useEffect ( ( ) => {
160
+ if ( actionData ?. error ) {
161
+ console . error ( '🔍 DEBUG: Action error:' , actionData . error ) ;
162
+ // You can add toast notification here
163
+ } else if ( actionData ?. success && actionData ?. report ) {
164
+ console . log ( '🔍 DEBUG: Post reported successfully:' , actionData ) ;
165
+ // You can add success toast notification here
166
+ }
167
+ } , [ actionData ] ) ;
168
+
113
169
return (
114
170
< Container maxWidth = "md" >
115
171
< Paper elevation = { 0 } sx = { { p : 3 , my : 3 } } >
@@ -237,18 +293,37 @@ export default function BlogPostDetail({ post, comments = [], reportReasons = []
237
293
>
238
294
< ShareIcon />
239
295
</ IconButton >
296
+
297
+ { /* Report button - always show when authenticated */ }
298
+ { isAuthenticated && (
299
+ < Tooltip title = { user ?. id === post . author ?. id ? "You can't report your own post" : "Report post" } >
300
+ < span >
301
+ < IconButton
302
+ onClick = { user ?. id === post . author ?. id ? undefined : handleOpenReportDialog }
303
+ disabled = { user ?. id === post . author ?. id }
304
+ aria-label = "Report post"
305
+ sx = { user ?. id === post . author ?. id ? {
306
+ color : 'action.disabled' ,
307
+ cursor : 'not-allowed'
308
+ } : { color : 'warning.main' } }
309
+ >
310
+ < FlagIcon />
311
+ </ IconButton >
312
+ </ span >
313
+ </ Tooltip >
314
+ ) }
240
315
</ Box >
241
316
242
317
{ isAuthenticated && user ?. id === post . author ?. id && (
243
318
< Box sx = { { display : 'flex' , gap : 1 } } >
244
- < IconButton
319
+ < IconButton
245
320
onClick = { handleEdit }
246
321
color = "primary"
247
322
aria-label = "Edit post"
248
323
>
249
324
< EditIcon />
250
325
</ IconButton >
251
- < IconButton
326
+ < IconButton
252
327
onClick = { handleDelete }
253
328
color = "error"
254
329
aria-label = "Delete post"
@@ -260,11 +335,53 @@ export default function BlogPostDetail({ post, comments = [], reportReasons = []
260
335
</ Box >
261
336
</ Paper >
262
337
263
- < BlogPostComments
264
- postId = { post . id }
265
- comments = { comments }
266
- reportReasons = { reportReasons }
338
+ < BlogPostComments
339
+ postId = { post . id }
340
+ comments = { comments }
341
+ reportReasons = { reportReasons }
267
342
/>
343
+
344
+ { /* Report Dialog */ }
345
+ < Dialog open = { reportDialogOpen } onClose = { handleCloseReportDialog } >
346
+ < DialogTitle > Report Post</ DialogTitle >
347
+ < DialogContent >
348
+ < FormControl fullWidth sx = { { mt : 2 } } >
349
+ < InputLabel > Reason</ InputLabel >
350
+ < Select
351
+ value = { selectedReason }
352
+ onChange = { ( e ) => setSelectedReason ( e . target . value ) }
353
+ label = "Reason"
354
+ >
355
+ { reportReasons . map ( ( reason ) => (
356
+ < MenuItem key = { reason . id } value = { reason . id } >
357
+ { reason . name }
358
+ </ MenuItem >
359
+ ) ) }
360
+ </ Select >
361
+ </ FormControl >
362
+ < TextField
363
+ fullWidth
364
+ multiline
365
+ rows = { 3 }
366
+ label = "Additional Details (Optional)"
367
+ value = { reportDescription }
368
+ onChange = { ( e ) => setReportDescription ( e . target . value ) }
369
+ sx = { { mt : 2 } }
370
+ />
371
+ < Box sx = { { mt : 2 , display : 'flex' , justifyContent : 'flex-end' } } >
372
+ < Button onClick = { handleCloseReportDialog } sx = { { mr : 1 } } >
373
+ Cancel
374
+ </ Button >
375
+ < Button
376
+ variant = "contained"
377
+ onClick = { handleSubmitReport }
378
+ disabled = { ! selectedReason }
379
+ >
380
+ Submit Report
381
+ </ Button >
382
+ </ Box >
383
+ </ DialogContent >
384
+ </ Dialog >
268
385
</ Container >
269
386
) ;
270
387
}
0 commit comments