854 lines
30 KiB
TypeScript
854 lines
30 KiB
TypeScript
"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<Issue[]>([]);
|
|
const [totalElements, setTotalElements] = React.useState<number>(0);
|
|
const [totalPages, setTotalPages] = React.useState<number>(1);
|
|
const [isSidebarOpen, setIsSidebarOpen] = React.useState<boolean>(true);
|
|
const [selectedIssue, setSelectedIssue] = React.useState<Issue | null>(null);
|
|
const [search, setSearch] = React.useState<string>("");
|
|
const [sortOrder, setSortOrder] = React.useState<"latest" | "oldest">(
|
|
"latest"
|
|
);
|
|
const [page, setPage] = React.useState<number>(1);
|
|
const [pageSize, setPageSize] = React.useState<number>(10);
|
|
const [selectedTicketId, setSelectedTicketId] = React.useState<string | null>(
|
|
null
|
|
);
|
|
const [selectedMap, setSelectedMap] = React.useState<Record<string, boolean>>(
|
|
{}
|
|
);
|
|
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<string, boolean> = {};
|
|
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<string, boolean> = {};
|
|
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<string>("");
|
|
|
|
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 (
|
|
<div className="w-full min-h-screen bg-gray-50">
|
|
{/* Header */}
|
|
<div className="px-4 py-4 border-b bg-white">
|
|
<div className="max-w-full mx-auto">
|
|
<h2 className="text-lg font-semibold">
|
|
Semua Ticket: {totalElements}
|
|
</h2>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex h-[calc(100vh-84px)]">
|
|
{/* Sidebar */}
|
|
<aside
|
|
className={cn(
|
|
"bg-white border-r transition-all duration-200",
|
|
isSidebarOpen ? "w-56" : "w-12"
|
|
)}
|
|
>
|
|
<div className="h-full flex flex-col">
|
|
<div className="p-3 flex items-center justify-between border-b">
|
|
<div
|
|
className={cn(
|
|
"text-sm font-medium",
|
|
!isSidebarOpen && "hidden"
|
|
)}
|
|
>
|
|
<div
|
|
className={cn(
|
|
"flex items-center gap-2",
|
|
!isSidebarOpen && "justify-center"
|
|
)}
|
|
>
|
|
<span className={cn(!isSidebarOpen && "hidden")}>
|
|
All New Issues
|
|
</span>
|
|
<Badge className={cn(!isSidebarOpen && "hidden")}>
|
|
{totalElements}
|
|
</Badge>
|
|
</div>
|
|
</div>
|
|
<button
|
|
aria-label="toggle sidebar"
|
|
className="p-1 rounded-md hover:bg-gray-100"
|
|
onClick={() => setIsSidebarOpen((s) => !s)}
|
|
title={isSidebarOpen ? "Collapse" : "Open"}
|
|
>
|
|
{isSidebarOpen ? <ChevronLeft /> : <ChevronRight />}
|
|
</button>
|
|
</div>
|
|
|
|
<nav className="flex-1 overflow-auto">
|
|
<ul className="text-sm">
|
|
<li
|
|
className={cn(
|
|
"px-3 py-3 border-b flex items-center justify-between",
|
|
!isSidebarOpen && "justify-center"
|
|
)}
|
|
>
|
|
<div
|
|
className={cn(
|
|
"flex items-center gap-2",
|
|
!isSidebarOpen && "justify-center"
|
|
)}
|
|
>
|
|
<span className={cn(!isSidebarOpen && "hidden")}>
|
|
All New Issues
|
|
</span>
|
|
<Badge className={cn(!isSidebarOpen && "hidden")}>
|
|
{totalElements}
|
|
</Badge>
|
|
</div>
|
|
{/* when collapsed show total */}
|
|
{!isSidebarOpen && <Badge>{totalElements}</Badge>}
|
|
</li>
|
|
|
|
{[
|
|
"All Issues",
|
|
"My Open Issues",
|
|
"My Closed Issues",
|
|
"Live Chat In Progress",
|
|
].map((label) => (
|
|
<li key={label} className="px-3 py-4 border-b">
|
|
<button className="w-full text-left text-sm">
|
|
{label}
|
|
</button>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</nav>
|
|
</div>
|
|
</aside>
|
|
|
|
{/* Middle: Issue List */}
|
|
<section className="flex-shrink-0 w-[420px] bg-white border-r flex flex-col">
|
|
{/* top controls: select all, search, sort */}
|
|
<div className="p-3 border-b">
|
|
<div className="flex items-center gap-3">
|
|
<input
|
|
type="checkbox"
|
|
checked={allSelected}
|
|
onChange={toggleSelectAll}
|
|
className="h-4 w-4"
|
|
/>
|
|
<div className="flex-1">
|
|
<div className="relative">
|
|
<Input
|
|
placeholder="Cari..."
|
|
value={search}
|
|
onChange={(e) => setSearch(e.target.value)}
|
|
className="pl-10"
|
|
/>
|
|
<div className="absolute left-3 top-1/2 -translate-y-1/2 pointer-events-none">
|
|
<Search size={16} />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<DropdownMenu>
|
|
<DropdownMenuTrigger asChild>
|
|
<Button variant="outline" size="sm">
|
|
{sortOrder === "latest" ? "Latest" : "Oldest"}
|
|
</Button>
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent className="w-48">
|
|
<DropdownMenuRadioGroup
|
|
value={sortOrder}
|
|
onValueChange={(value: string) =>
|
|
setSortOrder(value as "latest" | "oldest")
|
|
}
|
|
>
|
|
<DropdownMenuRadioItem value="latest">
|
|
Latest
|
|
</DropdownMenuRadioItem>
|
|
<DropdownMenuRadioItem value="oldest">
|
|
Oldest
|
|
</DropdownMenuRadioItem>
|
|
</DropdownMenuRadioGroup>
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
</div>
|
|
|
|
<div>
|
|
<DropdownMenu>
|
|
<DropdownMenuTrigger asChild>
|
|
<Button variant="ghost" size="sm">
|
|
<MoreVertical />
|
|
</Button>
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent className="w-40">
|
|
<div className="px-3 py-2 text-xs text-muted-foreground">
|
|
Show
|
|
</div>
|
|
<DropdownMenuRadioGroup
|
|
value={String(pageSize)}
|
|
onValueChange={(v) => {
|
|
setPageSize(Number(v));
|
|
setPage(1);
|
|
}}
|
|
>
|
|
<DropdownMenuRadioItem value="10">
|
|
10
|
|
</DropdownMenuRadioItem>
|
|
<DropdownMenuRadioItem value="20">
|
|
20
|
|
</DropdownMenuRadioItem>
|
|
<DropdownMenuRadioItem value="25">
|
|
25
|
|
</DropdownMenuRadioItem>
|
|
<DropdownMenuRadioItem value="50">
|
|
50
|
|
</DropdownMenuRadioItem>
|
|
</DropdownMenuRadioGroup>
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* list */}
|
|
<div className="flex-1 overflow-auto">
|
|
<ul>
|
|
{issues.length === 0 ? (
|
|
<li className="p-6 text-center text-sm text-muted-foreground">
|
|
No issues
|
|
</li>
|
|
) : (
|
|
issues.map((it) => {
|
|
const key = String(it.id);
|
|
const src = sourceToIconLabel(it.source);
|
|
return (
|
|
<li
|
|
key={key}
|
|
onClick={() => {
|
|
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" : ""
|
|
)}
|
|
>
|
|
<div className="flex items-start pt-1">
|
|
<input
|
|
type="checkbox"
|
|
checked={Boolean(selectedMap[key])}
|
|
onChange={(e) => {
|
|
e.stopPropagation();
|
|
toggleSelectOne(it.id);
|
|
}}
|
|
onClick={(e) => e.stopPropagation()}
|
|
className="h-4 w-4 mt-1"
|
|
/>
|
|
</div>
|
|
|
|
<div className="flex-1">
|
|
<div className="flex items-center justify-between">
|
|
<div className="flex items-center gap-3">
|
|
<Avatar className="h-8 w-8">
|
|
<AvatarFallback>{src.short}</AvatarFallback>
|
|
</Avatar>
|
|
<div>
|
|
<div className="text-sm font-medium">
|
|
{(it.title ?? "")
|
|
.split(" ")
|
|
.slice(0, 25)
|
|
.join(" ") +
|
|
((it.title ?? "").split(" ").length > 25
|
|
? "..."
|
|
: "")}
|
|
</div>
|
|
|
|
<div className="text-xs text-muted-foreground flex items-center gap-2">
|
|
<span>{src.label}</span>
|
|
<span>•</span>
|
|
<span>{it.timeAgo ?? "—"}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex items-center gap-2">
|
|
<Badge className="text-xs">New Issue</Badge>
|
|
<div className="text-xs text-muted-foreground">
|
|
{it.timeAgo}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</li>
|
|
);
|
|
})
|
|
)}
|
|
</ul>
|
|
</div>
|
|
|
|
{/* pagination simple */}
|
|
<div className="p-3 border-t flex items-center justify-between">
|
|
<div className="text-sm text-muted-foreground">
|
|
{`1-${pageSize} of ${totalElements}`}
|
|
</div>
|
|
<div className="flex items-center gap-2">
|
|
<Button
|
|
variant="outline"
|
|
size="sm"
|
|
onClick={() => setPage((p) => Math.max(1, p - 1))}
|
|
disabled={page <= 1}
|
|
>
|
|
Prev
|
|
</Button>
|
|
<div className="text-sm">
|
|
{page} / {totalPages}
|
|
</div>
|
|
<Button
|
|
variant="outline"
|
|
size="sm"
|
|
onClick={() => setPage((p) => Math.min(totalPages, p + 1))}
|
|
disabled={page >= totalPages}
|
|
>
|
|
Next
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{/* Right: Chat Panel */}
|
|
<main className="flex-1 overflow-y-auto bg-gray-50">
|
|
{selectedTicketId ? (
|
|
<FormDetailTicketing id={selectedTicketId} />
|
|
) : (
|
|
<div className="h-full flex items-center justify-center text-gray-400">
|
|
Pilih ticket dari sebelah kiri
|
|
</div>
|
|
)}
|
|
</main>
|
|
{/* <section className="flex-1 flex flex-col bg-white">
|
|
<div className="border-b">
|
|
<div className="flex items-center px-4 py-3">
|
|
<div className="flex-1">
|
|
{selectedIssue ? (
|
|
<div className="flex items-center gap-3">
|
|
<div className="text-sm font-medium">
|
|
{(selectedIssue?.title ?? "")
|
|
.split(" ")
|
|
.slice(0, 25)
|
|
.join(" ") +
|
|
((selectedIssue?.title ?? "").split(" ").length > 25
|
|
? "..."
|
|
: "")}
|
|
</div>
|
|
|
|
<div className="text-xs text-muted-foreground">
|
|
• {selectedIssue.source}
|
|
</div>
|
|
</div>
|
|
) : (
|
|
<div className="text-sm text-muted-foreground">Chat</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex-1 overflow-auto p-6">
|
|
{!selectedIssue ? (
|
|
<div className="h-full flex items-center justify-center text-muted-foreground">
|
|
<div className="text-center">
|
|
<div className="mb-4">
|
|
<svg
|
|
width="72"
|
|
height="72"
|
|
viewBox="0 0 24 24"
|
|
className="mx-auto opacity-60"
|
|
>
|
|
<path
|
|
fill="currentColor"
|
|
d="M12 3C7 3 3 6.6 3 11c0 1.9.8 3.6 2.2 5v3.1L8 17.9c1 .3 2 .5 4 .5 5 0 9-3.6 9-8.1S17 3 12 3z"
|
|
/>
|
|
</svg>
|
|
</div>
|
|
<div className="text-sm">
|
|
Pilih issue untuk melihat detail
|
|
</div>
|
|
</div>
|
|
</div>
|
|
) : (
|
|
<div className="space-y-6">
|
|
{chatMessages.map((m) => (
|
|
<div
|
|
key={m.id}
|
|
className={cn(
|
|
"max-w-[70%] px-4 py-3 rounded-xl relative",
|
|
m.from === "agent"
|
|
? "ml-auto bg-green-50 text-gray-800"
|
|
: "mr-auto bg-blue-50 text-gray-800"
|
|
)}
|
|
>
|
|
<div className="whitespace-pre-wrap leading-relaxed text-sm">
|
|
{m.text}
|
|
</div>
|
|
<div className="mt-2 text-[11px] text-muted-foreground flex items-center justify-end gap-2">
|
|
<span>{m.from === "agent" ? "Anda" : "User"}</span>
|
|
<span>•</span>
|
|
<span>{m.time}</span>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
<div className="border-t px-4 py-3">
|
|
<div className="max-w-full mx-auto">
|
|
<div className="flex flex-col gap-2">
|
|
<textarea
|
|
placeholder='Enter your reply or type "/" to insert a quick reply'
|
|
value={replyText}
|
|
onChange={(e) => setReplyText(e.target.value)}
|
|
className="w-full border rounded-md p-3 min-h-[64px] resize-none focus:outline-none focus:ring"
|
|
/>
|
|
<div className="flex items-center justify-between gap-3">
|
|
<div className="flex items-center gap-2">
|
|
<Button
|
|
variant="outline"
|
|
size="sm"
|
|
onClick={handleTranslate}
|
|
title="Translate"
|
|
>
|
|
Translate
|
|
</Button>
|
|
</div>
|
|
|
|
<div className="flex items-center gap-3">
|
|
<Button
|
|
variant="outline"
|
|
size="sm"
|
|
onClick={() => sendReply(true)}
|
|
disabled={!replyText.trim()}
|
|
>
|
|
Kirim & Resolve
|
|
</Button>
|
|
<Button
|
|
size="sm"
|
|
onClick={() => sendReply(false)}
|
|
disabled={!replyText.trim()}
|
|
>
|
|
Kirim
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section> */}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
// "use client";
|
|
// import * as React from "react";
|
|
// import {
|
|
// ColumnDef,
|
|
// ColumnFiltersState,
|
|
// PaginationState,
|
|
// SortingState,
|
|
// VisibilityState,
|
|
// flexRender,
|
|
// getCoreRowModel,
|
|
// getFilteredRowModel,
|
|
// getPaginationRowModel,
|
|
// getSortedRowModel,
|
|
// useReactTable,
|
|
// } from "@tanstack/react-table";
|
|
// import { Button } from "@/components/ui/button";
|
|
// import {
|
|
// Table,
|
|
// TableBody,
|
|
// TableCell,
|
|
// TableHead,
|
|
// TableHeader,
|
|
// TableRow,
|
|
// } from "@/components/ui/table";
|
|
// import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
|
// import {
|
|
// ChevronLeft,
|
|
// ChevronRight,
|
|
// Eye,
|
|
// MoreVertical,
|
|
// Search,
|
|
// SquarePen,
|
|
// Trash2,
|
|
// TrendingDown,
|
|
// TrendingUp,
|
|
// } from "lucide-react";
|
|
// import { cn } from "@/lib/utils";
|
|
// import {
|
|
// DropdownMenu,
|
|
// DropdownMenuContent,
|
|
// DropdownMenuItem,
|
|
// DropdownMenuRadioGroup,
|
|
// DropdownMenuRadioItem,
|
|
// DropdownMenuTrigger,
|
|
// } from "@/components/ui/dropdown-menu";
|
|
// import { Input } from "@/components/ui/input";
|
|
// import { InputGroup, InputGroupText } from "@/components/ui/input-group";
|
|
// import { paginationBlog } from "@/service/blog/blog";
|
|
// import { ticketingPagination } from "@/service/ticketing/ticketing";
|
|
// import { Badge } from "@/components/ui/badge";
|
|
// import { useRouter, useSearchParams } from "next/navigation";
|
|
// import TablePagination from "@/components/table/table-pagination";
|
|
// import columns from "./columns";
|
|
// const TicketingTable = () => {
|
|
// const router = useRouter();
|
|
// const searchParams = useSearchParams();
|
|
// const [dataTable, setDataTable] = React.useState<any[]>([]);
|
|
// const [totalData, setTotalData] = React.useState<number>(1);
|
|
// const [sorting, setSorting] = React.useState<SortingState>([]);
|
|
// const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
|
// []
|
|
// );
|
|
// const [search, setSearch] = React.useState("");
|
|
// const [columnVisibility, setColumnVisibility] =
|
|
// React.useState<VisibilityState>({});
|
|
// const [showData, setShowData] = React.useState("10");
|
|
// const [rowSelection, setRowSelection] = React.useState({});
|
|
// const [pagination, setPagination] = React.useState<PaginationState>({
|
|
// pageIndex: 0,
|
|
// pageSize: Number(showData),
|
|
// });
|
|
// const [page, setPage] = React.useState(1);
|
|
// const [totalPage, setTotalPage] = React.useState(1);
|
|
// const table = useReactTable({
|
|
// data: dataTable,
|
|
// columns,
|
|
// onSortingChange: setSorting,
|
|
// onColumnFiltersChange: setColumnFilters,
|
|
// getCoreRowModel: getCoreRowModel(),
|
|
// getPaginationRowModel: getPaginationRowModel(),
|
|
// getSortedRowModel: getSortedRowModel(),
|
|
// getFilteredRowModel: getFilteredRowModel(),
|
|
// onColumnVisibilityChange: setColumnVisibility,
|
|
// onRowSelectionChange: setRowSelection,
|
|
// onPaginationChange: setPagination,
|
|
// state: {
|
|
// sorting,
|
|
// columnFilters,
|
|
// columnVisibility,
|
|
// rowSelection,
|
|
// pagination,
|
|
// },
|
|
// });
|
|
// let typingTimer: any;
|
|
// const doneTypingInterval = 1500;
|
|
// const handleKeyUp = () => {
|
|
// clearTimeout(typingTimer);
|
|
// typingTimer = setTimeout(doneTyping, doneTypingInterval);
|
|
// };
|
|
// const handleKeyDown = () => {
|
|
// clearTimeout(typingTimer);
|
|
// };
|
|
// async function doneTyping() {
|
|
// fetchData();
|
|
// }
|
|
// React.useEffect(() => {
|
|
// const pageFromUrl = searchParams?.get("page");
|
|
// if (pageFromUrl) {
|
|
// setPage(Number(pageFromUrl));
|
|
// }
|
|
// }, [searchParams]);
|
|
// React.useEffect(() => {
|
|
// fetchData();
|
|
// }, [page]);
|
|
// async function fetchData() {
|
|
// try {
|
|
// const res = await ticketingPagination(search, Number(showData), page - 1);
|
|
// const data = res?.data?.data;
|
|
// const contentData = data?.content;
|
|
// contentData.forEach((item: any, index: number) => {
|
|
// item.no = (page - 1) * Number(showData) + index + 1;
|
|
// });
|
|
// console.log("contentData : ", contentData);
|
|
// setDataTable(contentData);
|
|
// setTotalData(data?.totalElements);
|
|
// setTotalPage(data?.totalPages);
|
|
// } catch (error) {
|
|
// console.error("Error fetching tasks:", error);
|
|
// }
|
|
// }
|
|
// return (
|
|
// <>
|
|
// {" "}
|
|
// <div className="flex justify-between py-3">
|
|
// {" "}
|
|
// <Input
|
|
// type="text"
|
|
// placeholder="Search"
|
|
// onKeyUp={handleKeyUp}
|
|
// onKeyDown={handleKeyDown}
|
|
// onChange={(e) => setSearch(e.target.value)}
|
|
// className="max-w-[300px]"
|
|
// />{" "}
|
|
// <div className="flex flex-row gap-2">
|
|
// {" "}
|
|
// <DropdownMenu>
|
|
// {" "}
|
|
// <DropdownMenuTrigger asChild>
|
|
// {" "}
|
|
// <Button size="md" variant="outline">
|
|
// {" "}
|
|
// 1 - {showData} Data{" "}
|
|
// </Button>{" "}
|
|
// </DropdownMenuTrigger>{" "}
|
|
// <DropdownMenuContent className="w-56 text-sm">
|
|
// {" "}
|
|
// <DropdownMenuRadioGroup
|
|
// value={showData}
|
|
// onValueChange={setShowData}
|
|
// >
|
|
// {" "}
|
|
// <DropdownMenuRadioItem value="10">
|
|
// {" "}
|
|
// 1 - 10 Data{" "}
|
|
// </DropdownMenuRadioItem>{" "}
|
|
// <DropdownMenuRadioItem value="20">
|
|
// {" "}
|
|
// 1 - 20 Data{" "}
|
|
// </DropdownMenuRadioItem>{" "}
|
|
// <DropdownMenuRadioItem value="25">
|
|
// {" "}
|
|
// 1 - 25 Data{" "}
|
|
// </DropdownMenuRadioItem>{" "}
|
|
// <DropdownMenuRadioItem value="50">
|
|
// {" "}
|
|
// 1 - 50 Data{" "}
|
|
// </DropdownMenuRadioItem>{" "}
|
|
// </DropdownMenuRadioGroup>{" "}
|
|
// </DropdownMenuContent>{" "}
|
|
// </DropdownMenu>{" "}
|
|
// </div>{" "}
|
|
// </div>{" "}
|
|
// <Table className="overflow-hidden">
|
|
// {" "}
|
|
// <TableHeader>
|
|
// {" "}
|
|
// {table.getHeaderGroups().map((headerGroup) => (
|
|
// <TableRow key={headerGroup.id} className="bg-default-200">
|
|
// {" "}
|
|
// {headerGroup.headers.map((header) => (
|
|
// <TableHead key={header.id}>
|
|
// {" "}
|
|
// {header.isPlaceholder
|
|
// ? null
|
|
// : flexRender(
|
|
// header.column.columnDef.header,
|
|
// header.getContext()
|
|
// )}{" "}
|
|
// </TableHead>
|
|
// ))}{" "}
|
|
// </TableRow>
|
|
// ))}{" "}
|
|
// </TableHeader>{" "}
|
|
// <TableBody>
|
|
// {" "}
|
|
// {table.getRowModel().rows?.length ? (
|
|
// table.getRowModel().rows.map((row) => (
|
|
// <TableRow
|
|
// key={row.id}
|
|
// data-state={row.getIsSelected() && "selected"}
|
|
// className="h-[75px]"
|
|
// >
|
|
// {" "}
|
|
// {row.getVisibleCells().map((cell) => (
|
|
// <TableCell key={cell.id}>
|
|
// {" "}
|
|
// {flexRender(
|
|
// cell.column.columnDef.cell,
|
|
// cell.getContext()
|
|
// )}{" "}
|
|
// </TableCell>
|
|
// ))}{" "}
|
|
// </TableRow>
|
|
// ))
|
|
// ) : (
|
|
// <TableRow>
|
|
// {" "}
|
|
// <TableCell colSpan={columns.length} className="h-24 text-center">
|
|
// {" "}
|
|
// No results.{" "}
|
|
// </TableCell>{" "}
|
|
// </TableRow>
|
|
// )}{" "}
|
|
// </TableBody>{" "}
|
|
// </Table>{" "}
|
|
// <TablePagination
|
|
// table={table}
|
|
// totalData={totalData}
|
|
// totalPage={totalPage}
|
|
// />{" "}
|
|
// </>
|
|
// );
|
|
// };
|
|
// export default TicketingTable;
|