"use client"; import * as React from "react"; import { ticketingPagination } from "@/service/ticketing/ticketing"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Badge } from "@/components/ui/badge"; import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar"; import { ChevronLeft, ChevronRight, Search, MoreVertical } from "lucide-react"; import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuRadioGroup, DropdownMenuRadioItem, } from "@/components/ui/dropdown-menu"; import { cn } from "@/lib/utils"; import FormDetailTicketing from "@/components/form/ticketing/ticketing-detail-form"; import { useParams } from "next/navigation"; /** * TicketingLayout * * Features implemented: * - Header with total ticket count * - Collapsible left sidebar with menu and a toggle arrow (shows total content count) * - Middle issue list (list items, select all checkbox, search input with debounce, sort latest/oldest) * - Right chat panel (bubble style) that opens when clicking an issue * - Chat input area with Translate, Kirim & Resolve, Kirim buttons * * Notes: * - Replace / adjust icons and utilities if your project differs. * - ticketingPagination(search, pageSize, pageIndex) is used; ensure it returns the same structure as before. */ type Issue = { id: string | number; title?: string; source?: string; createdAt?: string; status?: string; timeAgo?: string; }; export default function TicketingTable() { const params = useParams(); const mediaId = params?.media_id; const [issues, setIssues] = React.useState([]); const [totalElements, setTotalElements] = React.useState(0); const [totalPages, setTotalPages] = React.useState(1); const [isSidebarOpen, setIsSidebarOpen] = React.useState(true); const [selectedIssue, setSelectedIssue] = React.useState(null); const [search, setSearch] = React.useState(""); const [sortOrder, setSortOrder] = React.useState<"latest" | "oldest">( "latest" ); const [page, setPage] = React.useState(1); const [pageSize, setPageSize] = React.useState(10); const [selectedTicketId, setSelectedTicketId] = React.useState( null ); const [selectedMap, setSelectedMap] = React.useState>( {} ); const allSelected = React.useMemo(() => { if (!issues.length) return false; return issues.every((i) => selectedMap[String(i.id)]); }, [issues, selectedMap]); React.useEffect(() => { const t = setTimeout(() => { fetchData(); }, 450); return () => clearTimeout(t); }, [search, page, pageSize, sortOrder]); React.useEffect(() => { fetchData(); }, []); async function fetchData() { try { const res = await ticketingPagination(search, pageSize, page - 1, mediaId == 'all' ? "" : mediaId as string); const data = res?.data?.data; const content = data?.content || []; const mapped: Issue[] = content.map((it: any, idx: number) => ({ id: it.id ?? idx, title: it.title ?? it.subject ?? "No Title", source: it.source ?? it.channel ?? "unknown", timeAgo: it.timeAgo ?? it.duration ?? "—", createdAt: it.createdAt, status: it.status, })); setIssues(mapped); setTotalElements(data?.totalElements ?? 0); setTotalPages(data?.totalPages ?? 1); const newMap: Record = {}; mapped.forEach((m) => { newMap[String(m.id)] = Boolean(selectedMap[String(m.id)]); }); setSelectedMap(newMap); } catch (err) { console.error("fetchData error", err); } } function toggleSelectAll() { const next: Record = {}; if (!allSelected) { issues.forEach((i) => { next[String(i.id)] = true; }); } else { // clear } setSelectedMap(next); } function toggleSelectOne(id: string | number) { const key = String(id); setSelectedMap((prev) => ({ ...prev, [key]: !prev[key] })); } // UI helpers function sourceToIconLabel(source?: string) { if (!source) return { label: "Other", short: "OT" }; const s = source.toLowerCase(); if (s.includes("insta") || s.includes("instagram")) return { label: "Instagram", short: "IG" }; if (s.includes("facebook") || s.includes("fb")) return { label: "Facebook", short: "FB" }; if (s.includes("tiktok")) return { label: "TikTok", short: "TT" }; if (s.includes("youtube")) return { label: "YouTube", short: "YT" }; if (s.includes("comment") || s.includes("kolom")) return { label: "Komentar", short: "CM" }; return { label: source, short: source.slice(0, 2).toUpperCase() }; } const [chatMessages, setChatMessages] = React.useState< { id: string; from: "user" | "agent"; text: string; time?: string }[] >([]); const [replyText, setReplyText] = React.useState(""); React.useEffect(() => { if (!selectedIssue) { setChatMessages([]); return; } setChatMessages([ { id: "m1", from: "user", text: "Hallo, untuk patroli hari ini sudah bisa di akses di daily tasks, silahkan anda periksa", time: "06:00", }, { id: "m2", from: "agent", text: "Terima kasih. Kami akan cek dan tindak lanjut segera. Mohon lampirkan bukti bila ada.", time: "06:05", }, ]); }, [selectedIssue]); function sendReply(closeAfter = false) { if (!replyText.trim()) return; const id = `m${Date.now()}`; setChatMessages((c) => [ ...c, { id, from: "agent", text: replyText, time: new Date().toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", }), }, ]); setReplyText(""); if (closeAfter) { setTimeout(() => { setSelectedIssue(null); }, 300); } } function handleTranslate() { if (!replyText.trim()) return; setReplyText((t) => t + " (translated)"); } return (
{/* Header */}

Semua Ticket: {totalElements}

{/* Sidebar */} {/* Middle: Issue List */}
{/* top controls: select all, search, sort */}
setSearch(e.target.value)} className="pl-10" />
setSortOrder(value as "latest" | "oldest") } > Latest Oldest
Show
{ setPageSize(Number(v)); setPage(1); }} > 10 20 25 50
{/* list */}
    {issues.length === 0 ? (
  • No issues
  • ) : ( issues.map((it) => { const key = String(it.id); const src = sourceToIconLabel(it.source); return (
  • { setSelectedIssue(it); setSelectedTicketId(String(it.id)); }} className={cn( "flex items-start gap-3 px-4 py-3 border-b cursor-pointer hover:bg-gray-50", selectedIssue?.id === it.id ? "bg-gray-100" : "" )} >
    { e.stopPropagation(); toggleSelectOne(it.id); }} onClick={(e) => e.stopPropagation()} className="h-4 w-4 mt-1" />
    {src.short}
    {(it.title ?? "") .split(" ") .slice(0, 25) .join(" ") + ((it.title ?? "").split(" ").length > 25 ? "..." : "")}
    {src.label} {it.timeAgo ?? "—"}
    New Issue
    {it.timeAgo}
  • ); }) )}
{/* pagination simple */}
{`1-${pageSize} of ${totalElements}`}
{page} / {totalPages}
{/* Right: Chat Panel */}
{selectedTicketId ? ( ) : (
Pilih ticket dari sebelah kiri
)}
{/*
{selectedIssue ? (
{(selectedIssue?.title ?? "") .split(" ") .slice(0, 25) .join(" ") + ((selectedIssue?.title ?? "").split(" ").length > 25 ? "..." : "")}
• {selectedIssue.source}
) : (
Chat
)}
{!selectedIssue ? (
Pilih issue untuk melihat detail
) : (
{chatMessages.map((m) => (
{m.text}
{m.from === "agent" ? "Anda" : "User"} {m.time}
))}
)}