video preview version

This commit is contained in:
fahed
2026-02-08 22:51:42 +03:00
parent 5f7d922f92
commit 9b58e5e9aa
23 changed files with 890 additions and 544 deletions
+134
View File
@@ -0,0 +1,134 @@
import { useState, useEffect } from 'react'
import { Send, Trash2, MessageCircle } from 'lucide-react'
import { api, getInitials } from '../utils/api'
import { useAuth } from '../contexts/AuthContext'
import { useLanguage } from '../i18n/LanguageContext'
function relativeTime(dateStr, t) {
const now = Date.now()
const then = new Date(dateStr).getTime()
const diffMs = now - then
const diffMin = Math.floor(diffMs / 60000)
if (diffMin < 1) return t('comments.justNow')
if (diffMin < 60) return t('comments.minutesAgo').replace('{n}', diffMin)
const diffHours = Math.floor(diffMin / 60)
if (diffHours < 24) return t('comments.hoursAgo').replace('{n}', diffHours)
const diffDays = Math.floor(diffHours / 24)
return t('comments.daysAgo').replace('{n}', diffDays)
}
export default function CommentsSection({ entityType, entityId }) {
const { user } = useAuth()
const { t } = useLanguage()
const [comments, setComments] = useState([])
const [newComment, setNewComment] = useState('')
const [sending, setSending] = useState(false)
useEffect(() => {
if (entityType && entityId) loadComments()
}, [entityType, entityId])
const loadComments = async () => {
try {
const data = await api.get(`/comments/${entityType}/${entityId}`)
setComments(Array.isArray(data) ? data : (data.data || []))
} catch (err) {
console.error('Failed to load comments:', err)
}
}
const handleSend = async () => {
if (!newComment.trim() || sending) return
setSending(true)
try {
await api.post(`/comments/${entityType}/${entityId}`, { content: newComment.trim() })
setNewComment('')
loadComments()
} catch (err) {
console.error('Failed to send comment:', err)
} finally {
setSending(false)
}
}
const handleDelete = async (id) => {
try {
await api.delete(`/comments/${id}`)
loadComments()
} catch (err) {
console.error('Failed to delete comment:', err)
}
}
const canDelete = (comment) => {
if (!user) return false
if (comment.user_id === user.id) return true
return user.role === 'superadmin' || user.role === 'manager'
}
return (
<div className="space-y-3">
<h4 className="text-sm font-semibold text-text-primary flex items-center gap-1.5">
<MessageCircle className="w-4 h-4" />
{t('comments.title')}
{comments.length > 0 && (
<span className="text-xs font-medium text-text-tertiary bg-surface-tertiary px-1.5 py-0.5 rounded-full">
{comments.length}
</span>
)}
</h4>
{comments.length === 0 && (
<p className="text-xs text-text-tertiary py-2">{t('comments.noComments')}</p>
)}
<div className="space-y-2 max-h-64 overflow-y-auto">
{comments.map(c => (
<div key={c.id} className="flex items-start gap-2 group">
<div className="w-7 h-7 rounded-full bg-brand-primary/10 text-brand-primary flex items-center justify-center text-[10px] font-bold shrink-0 mt-0.5">
{c.user_avatar ? (
<img src={c.user_avatar} className="w-full h-full rounded-full object-cover" alt="" />
) : (
getInitials(c.user_name)
)}
</div>
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2">
<span className="text-xs font-medium text-text-primary">{c.user_name}</span>
<span className="text-[10px] text-text-tertiary">{relativeTime(c.created_at, t)}</span>
{canDelete(c) && (
<button
onClick={() => handleDelete(c.id)}
className="p-0.5 rounded text-text-tertiary hover:text-red-500 opacity-0 group-hover:opacity-100 transition-opacity ml-auto"
>
<Trash2 className="w-3 h-3" />
</button>
)}
</div>
<p className="text-xs text-text-secondary whitespace-pre-wrap break-words">{c.content}</p>
</div>
</div>
))}
</div>
{/* Input */}
<div className="flex items-center gap-2">
<input
type="text"
value={newComment}
onChange={e => setNewComment(e.target.value)}
onKeyDown={e => e.key === 'Enter' && !e.shiftKey && handleSend()}
placeholder={t('comments.placeholder')}
className="flex-1 px-3 py-1.5 text-sm border border-border rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-primary/20 focus:border-brand-primary"
/>
<button
onClick={handleSend}
disabled={!newComment.trim() || sending}
className="p-2 bg-brand-primary text-white rounded-lg hover:bg-brand-primary-light disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
>
<Send className="w-4 h-4" />
</button>
</div>
</div>
)
}