103 lines
3.8 KiB
TypeScript
103 lines
3.8 KiB
TypeScript
"use client"
|
|
|
|
import { useState, useEffect, useCallback } from "react"
|
|
import { useAuth } from "@/lib/auth-context"
|
|
import { Button } from "@/components/ui/button"
|
|
import { Textarea } from "@/components/ui/textarea"
|
|
import { Avatar, AvatarFallback } from "@/components/ui/avatar"
|
|
import type { Comment } from "@/lib/types"
|
|
|
|
interface TicketCommentsProps {
|
|
ticketId: number
|
|
}
|
|
|
|
export function TicketComments({ ticketId }: TicketCommentsProps) {
|
|
const [comments, setComments] = useState<Comment[]>([])
|
|
const [newComment, setNewComment] = useState("")
|
|
const [loading, setLoading] = useState(false)
|
|
const { user, authHeader } = useAuth()
|
|
const API_URL = process.env.NEXT_PUBLIC_API_URL + "/api"
|
|
|
|
const fetchComments = useCallback(async () => {
|
|
if (!authHeader) return
|
|
try {
|
|
const res = await fetch(`${API_URL}/tickets/${ticketId}/comments`, {
|
|
headers: { "Authorization": authHeader }
|
|
})
|
|
if (res.ok) {
|
|
const data = await res.json()
|
|
setComments(data)
|
|
}
|
|
} catch (e) {
|
|
console.error(e)
|
|
}
|
|
}, [ticketId, authHeader])
|
|
|
|
useEffect(() => {
|
|
if (authHeader) {
|
|
fetchComments()
|
|
}
|
|
}, [fetchComments, authHeader])
|
|
|
|
const handlePost = async () => {
|
|
if (!authHeader || !newComment.trim()) return
|
|
setLoading(true)
|
|
try {
|
|
const res = await fetch(`${API_URL}/tickets/${ticketId}/comments`, {
|
|
method: "POST",
|
|
headers: {
|
|
"Authorization": authHeader,
|
|
"Content-Type": "application/json"
|
|
},
|
|
body: JSON.stringify({ text: newComment })
|
|
})
|
|
if (res.ok) {
|
|
setNewComment("")
|
|
fetchComments()
|
|
}
|
|
} catch (e) {
|
|
console.error(e)
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div className="space-y-4">
|
|
<h3 className="font-semibold">Comments</h3>
|
|
<div className="space-y-4 max-h-[300px] overflow-y-auto pr-2">
|
|
{comments.length === 0 && <p className="text-sm text-muted-foreground">No comments yet.</p>}
|
|
{comments.map((comment) => (
|
|
<div key={comment.id} className="flex gap-3">
|
|
<Avatar className="h-8 w-8">
|
|
<AvatarFallback>{comment.author.name ? comment.author.name.charAt(0) : '?'}</AvatarFallback>
|
|
</Avatar>
|
|
<div className="flex-1 space-y-1">
|
|
<div className="flex items-center justify-between">
|
|
<span className="text-sm font-medium">{comment.author.name}</span>
|
|
<span className="text-xs text-muted-foreground">{new Date(comment.createdAt).toLocaleString()}</span>
|
|
</div>
|
|
<div className="p-2 bg-muted/50 rounded-md">
|
|
<p className="text-sm text-foreground/90 whitespace-pre-wrap">{comment.text}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
<div className="space-y-2 pt-2">
|
|
<Textarea
|
|
placeholder="Write a comment..."
|
|
value={newComment}
|
|
onChange={(e) => setNewComment(e.target.value)}
|
|
className="min-h-[80px]"
|
|
/>
|
|
<div className="flex justify-end">
|
|
<Button onClick={handlePost} disabled={!newComment.trim() || loading}>
|
|
Post Comment
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|