170 lines
6.7 KiB
TypeScript
170 lines
6.7 KiB
TypeScript
import React, { useEffect, useState } from "react"
|
||
import { Link } from "react-router-dom"
|
||
import { Heart, MessageCircle, MoreHorizontal } from "lucide-react"
|
||
import { getComments, postComment } from "../../api/deal/commentDeal"
|
||
import { useAuth } from "../../context/AuthContext"
|
||
import { timeAgo } from "../../utils/timeAgo"
|
||
import type { Comment } from "../../models/comment/Comment"
|
||
|
||
type DealCommentsProps = {
|
||
dealId: number
|
||
onRequireLogin: () => void
|
||
}
|
||
|
||
export default function DealComments({ dealId, onRequireLogin }: DealCommentsProps) {
|
||
const [comments, setComments] = useState<Comment[]>([])
|
||
const [newComment, setNewComment] = useState("")
|
||
const [loading, setLoading] = useState(false)
|
||
const { isAuthenticated } = useAuth()
|
||
|
||
useEffect(() => {
|
||
async function loadComments() {
|
||
try {
|
||
const data = await getComments(dealId)
|
||
setComments(data)
|
||
} catch (err) {
|
||
console.error("Yorumlar alınamadı:", err)
|
||
}
|
||
}
|
||
loadComments()
|
||
}, [dealId])
|
||
|
||
const handleSubmit = async (e: React.FormEvent) => {
|
||
e.preventDefault()
|
||
if (!newComment.trim()) return
|
||
if (!isAuthenticated) return onRequireLogin()
|
||
|
||
setLoading(true)
|
||
try {
|
||
const added = await postComment(dealId, newComment)
|
||
setComments((prev) => [added, ...prev])
|
||
setNewComment("")
|
||
} catch (err: any) {
|
||
console.error(err)
|
||
alert(err.message || "Sunucu hatası")
|
||
} finally {
|
||
setLoading(false)
|
||
}
|
||
}
|
||
|
||
return (
|
||
<div className="rounded-3xl bg-surface border border-white/10 p-5 flex flex-col">
|
||
{/* Header */}
|
||
<div className="flex items-center justify-between gap-3">
|
||
<h2 className="text-lg font-semibold text-text">Yorumlar</h2>
|
||
<span className="text-xs text-text-muted bg-background border border-white/10 rounded-full px-3 py-1">
|
||
{comments.length}
|
||
</span>
|
||
</div>
|
||
|
||
{/* List: only desktop scroll */}
|
||
<div className="mt-4 flex-1 lg:max-h-[calc(100vh-280px)] lg:overflow-y-auto pr-2">
|
||
{comments.length > 0 ? (
|
||
<div className="divide-y divide-white/10">
|
||
{comments.map((c) => (
|
||
<div key={c.id} className="py-5 first:pt-0 last:pb-0">
|
||
<div className="flex gap-3">
|
||
<Link to={`/user/${c.user.username}`} className="shrink-0">
|
||
<img
|
||
src={
|
||
c.user.avatarUrl ||
|
||
`${import.meta.env.BASE_URL}placeholders/placeholder-profile.png`
|
||
}
|
||
alt={c.user.username}
|
||
className="w-16 h-16 rounded-full object-cover border border-white/10"
|
||
/>
|
||
</Link>
|
||
|
||
<div className="min-w-0 flex-1">
|
||
<div className="flex items-center justify-between gap-3">
|
||
<div className="min-w-0 flex items-center gap-2">
|
||
<Link
|
||
to={`/user/${c.user.username}`}
|
||
className="text-lg font-semibold text-text hover:underline truncate"
|
||
>
|
||
{c.user.username}
|
||
</Link>
|
||
<span className="text-xs text-text-muted">
|
||
{timeAgo(c.createdAt)}
|
||
</span>
|
||
</div>
|
||
|
||
<button
|
||
type="button"
|
||
className="inline-flex items-center justify-center rounded-lg p-2 bg-background border border-white/10 text-text-muted hover:text-text hover:border-white/20 transition"
|
||
aria-label="Yorum seçenekleri"
|
||
>
|
||
<MoreHorizontal className="w-4 h-4" />
|
||
</button>
|
||
</div>
|
||
|
||
<p className="mt-2 text-base text-text leading-relaxed whitespace-pre-line">
|
||
{c.text}
|
||
</p>
|
||
|
||
<div className="mt-3 flex items-center gap-2">
|
||
<button
|
||
type="button"
|
||
className="inline-flex items-center gap-2 rounded-lg px-3 py-2 text-xs font-semibold bg-background border border-white/10 text-text-muted hover:text-primary hover:border-white/20 transition"
|
||
>
|
||
<Heart className="w-4 h-4" />
|
||
<span>25</span>
|
||
</button>
|
||
|
||
<button
|
||
type="button"
|
||
className="inline-flex items-center gap-2 rounded-lg px-3 py-2 text-xs font-semibold bg-background border border-white/10 text-text-muted hover:text-primary hover:border-white/20 transition"
|
||
>
|
||
<MessageCircle className="w-4 h-4" />
|
||
<span>Yanıtla</span>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
) : (
|
||
<div className="rounded-2xl bg-background border border-white/10 p-4 text-center">
|
||
<div className="text-sm font-semibold text-text">Henüz yorum yok</div>
|
||
<div className="text-xs text-text-muted mt-1">
|
||
İlk yorumu sen yazabilirsin.
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
|
||
{/* Footer (always visible) */}
|
||
<div className="mt-5 pt-5 border-t border-white/10">
|
||
{isAuthenticated ? (
|
||
<form onSubmit={handleSubmit} className="flex items-center gap-3">
|
||
<input
|
||
type="text"
|
||
value={newComment}
|
||
onChange={(e) => setNewComment(e.target.value)}
|
||
placeholder="Yorum ekle..."
|
||
className="flex-1 rounded-xl border border-white/10 bg-background px-4 py-3 text-sm text-text placeholder:text-text-muted/70 outline-none focus:ring-2 focus:ring-primary/40"
|
||
disabled={loading}
|
||
/>
|
||
<button
|
||
type="submit"
|
||
disabled={loading}
|
||
className="shrink-0 rounded-xl px-5 py-3 text-sm font-semibold bg-primary text-black hover:bg-primary-hover transition disabled:opacity-60 disabled:cursor-not-allowed"
|
||
>
|
||
{loading ? "..." : "Gönder"}
|
||
</button>
|
||
</form>
|
||
) : (
|
||
<button
|
||
type="button"
|
||
onClick={onRequireLogin}
|
||
className="w-full rounded-xl border border-white/10 bg-background px-4 py-3 text-sm font-semibold text-primary hover:border-white/20 transition"
|
||
>
|
||
Yorum yazmak için giriş yap veya kayıt ol
|
||
</button>
|
||
)}
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|