source: chapterx-frontend/src/components/story/CommentSection.tsx@ acf690c

main
Last change on this file since acf690c 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.1 KB
Line 
1import React, { useState } from 'react'
2import { MessageCircle, Trash2, Send } from 'lucide-react'
3import { useAuthStore } from '../../store/authStore'
4import { useStoryStore } from '../../store/storyStore'
5import { useNotificationStore } from '../../store/notificationStore'
6import { useUIStore } from '../../store/uiStore'
7import { Comment } from '../../types'
8import { Avatar } from '../ui/Avatar'
9import { Button } from '../ui/Button'
10
11interface CommentSectionProps {
12 storyId: number
13 authorUserId: number
14}
15
16function timeAgo(str: string): string {
17 const diff = Date.now() - new Date(str).getTime()
18 const m = Math.floor(diff / 60000)
19 if (m < 1) return 'just now'
20 if (m < 60) return `${m}m ago`
21 const h = Math.floor(m / 60)
22 if (h < 24) return `${h}h ago`
23 return `${Math.floor(h / 24)}d ago`
24}
25
26export const CommentSection: React.FC<CommentSectionProps> = ({ storyId, authorUserId }) => {
27 const { currentUser } = useAuthStore()
28 const { comments, addComment, deleteComment } = useStoryStore()
29 const { addNotification } = useNotificationStore()
30 const { addToast } = useUIStore()
31 const [text, setText] = useState('')
32 const [submitting, setSubmitting] = useState(false)
33
34 const storyComments = comments
35 .filter(c => c.story_id === storyId)
36 .sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime())
37
38 const handleSubmit = async () => {
39 if (!text.trim() || !currentUser) return
40 setSubmitting(true)
41 await new Promise(r => setTimeout(r, 300))
42 const comment: Comment = {
43 comment_id: Date.now(),
44 story_id: storyId,
45 user_id: currentUser.user_id,
46 username: currentUser.username,
47 content: text.trim(),
48 created_at: new Date().toISOString(),
49 }
50 addComment(comment)
51 if (currentUser.user_id !== authorUserId) {
52 addNotification({
53 user_id: authorUserId,
54 type: 'comment',
55 title: 'New Comment',
56 message: `${currentUser.username} commented: "${text.trim().slice(0, 60)}..."`,
57 link: `/story/${storyId}`,
58 })
59 }
60 setText('')
61 setSubmitting(false)
62 addToast('Comment posted!')
63 }
64
65 return (
66 <div className="space-y-4">
67 <div className="flex items-center gap-2 mb-4">
68 <MessageCircle size={18} className="text-indigo-400" />
69 <h3 className="text-white font-semibold">Comments ({storyComments.length})</h3>
70 </div>
71
72 {/* Input */}
73 {currentUser ? (
74 <div className="flex gap-3">
75 <Avatar name={currentUser.name} size="sm" />
76 <div className="flex-1">
77 <textarea
78 value={text}
79 onChange={e => setText(e.target.value)}
80 placeholder="Share your thoughts..."
81 rows={3}
82 className="w-full bg-slate-800 border border-slate-700 rounded-xl px-4 py-3 text-sm text-white placeholder-slate-500 focus:outline-none focus:border-indigo-500 resize-none"
83 />
84 <div className="flex justify-end mt-2">
85 <Button
86 size="sm"
87 onClick={handleSubmit}
88 loading={submitting}
89 disabled={!text.trim()}
90 >
91 <Send size={14} />
92 Post Comment
93 </Button>
94 </div>
95 </div>
96 </div>
97 ) : (
98 <div className="bg-slate-800 border border-slate-700 rounded-xl p-4 text-center">
99 <p className="text-slate-400 text-sm">
100 <a href="/login" className="text-indigo-400 hover:text-indigo-300">Sign in</a> to leave a comment.
101 </p>
102 </div>
103 )}
104
105 {/* Comment list */}
106 <div className="space-y-3">
107 {storyComments.length === 0 ? (
108 <div className="text-center py-8 text-slate-500 text-sm">
109 No comments yet. Be the first to share your thoughts!
110 </div>
111 ) : (
112 storyComments.map(comment => (
113 <CommentCard
114 key={comment.comment_id}
115 comment={comment}
116 canDelete={currentUser?.user_id === comment.user_id || currentUser?.role === 'admin'}
117 onDelete={() => { deleteComment(comment.comment_id); addToast('Comment deleted', 'info') }}
118 />
119 ))
120 )}
121 </div>
122 </div>
123 )
124}
125
126const CommentCard: React.FC<{
127 comment: Comment
128 canDelete: boolean
129 onDelete: () => void
130}> = ({ comment, canDelete, onDelete }) => (
131 <div className="flex gap-3 p-4 bg-slate-800 rounded-xl border border-slate-700">
132 <Avatar name={comment.username} size="sm" />
133 <div className="flex-1 min-w-0">
134 <div className="flex items-center justify-between">
135 <span className="text-sm font-medium text-white">{comment.username}</span>
136 <div className="flex items-center gap-2">
137 <span className="text-xs text-slate-500">{timeAgo(comment.created_at)}</span>
138 {canDelete && (
139 <button onClick={onDelete} className="text-slate-500 hover:text-rose-400 transition-colors">
140 <Trash2 size={13} />
141 </button>
142 )}
143 </div>
144 </div>
145 <p className="text-slate-300 text-sm mt-1 leading-relaxed">{comment.content}</p>
146 </div>
147 </div>
148)
Note: See TracBrowser for help on using the repository browser.