"use client"; import { ColumnDef, ColumnResizeDirection, ColumnResizeMode, flexRender, getCoreRowModel, getPaginationRowModel, getSortedRowModel, SortingState, useReactTable, } from "@tanstack/react-table"; import React, { useCallback, useEffect, useState } from "react"; import { Button, Label, Table } from "flowbite-react"; import { HiChevronLeft, HiChevronRight, HiPlus } from "react-icons/hi"; import { useLocal } from "@/lib/utils/use-local"; import { debouncedHandler } from "@/lib/utils/debounceHandler"; import { FaChevronUp } from "react-icons/fa6"; import Link from "next/link"; import { init_column } from "./lib/column"; import { toast } from "sonner"; import { Loader2, Sticker } from "lucide-react"; import { InputSearch } from "../ui/input-search"; import { FaChevronDown } from "react-icons/fa"; import get from "lodash.get"; import { Checkbox } from "../ui/checkbox"; import { getNumber } from "@/lib/utils/getNumber"; import { formatMoney } from "../form/field/TypeInput"; import { cloneFM } from "@/lib/utils/cloneFm"; import { ResizableBox } from "react-resizable"; export const TableEditBetter: React.FC = ({ name, column, align = "center", onLoad, take = 20, header, disabledPagination, disabledHeader, disabledHeadTable, hiddenNoRow, disabledHoverRow, onInit, onCount, fm, mode, feature, onChange, delete_name, }) => { const [data, setData] = useState([]); const [columns, setColumns] = useState([] as any[]); const sideLeft = typeof header?.sideLeft === "function" ? header.sideLeft : null; const sideRight = typeof header?.sideRight === "function" ? header.sideRight : null; const checkbox = Array.isArray(feature) && feature?.length ? feature.includes("checkbox") : false; const local = useLocal({ table: null as any, data: [] as any[], dataForm: [] as any[], listData: [] as any[], sort: {} as any, search: null as any, count: 0 as any, addRow: (row: any) => { const data = fm.data?.[name] || []; data.push(row); fm.data[name] = data; fm.render(); local.data = fm.data[name]; local.render(); }, selection: { all: false, partial: [] as any[], }, renderRow: (row: any) => { setData((prev) => [...prev, row]); local.data = data; local.render(); }, removeRow: (row: any) => { // setData((prev) => prev.filter((item) => item !== row)); // Update state lokal // local.data = local.data.filter((item: any) => item !== row); // Hapus row dari local.data // local.render(); // Panggil render untuk memperbarui UI console.log("HALOO"); const data = fm.data?.[name] || []; // data.push(row); if (delete_name) { const ids: any[] = Array.isArray(fm.data?.[delete_name]) ? fm.data?.deleted_line_ids : []; if (row?.id) { ids.push(row.id); } fm.data[delete_name] = ids; } fm.data[name] = data.filter((item: any) => item !== row); fm.render(); local.data = fm.data[name]; local.render(); console.log({ fm }); // local.data = fm.data[name]; // local.render(); }, reload: async () => { toast.info( <> {"Loading..."} ); if (Array.isArray(onLoad)) { local.data = onLoad; local.render(); setData(onLoad); } else { const res: any = onLoad({ search: local.search, sort: local.sort, take, paging: 1, }); if (res instanceof Promise) { res.then((e) => { local.data = e; cloneListFM(e); local.render(); setData(e); setTimeout(() => { toast.dismiss(); }, 2000); }); } else { local.data = res; cloneListFM(res); local.render(); setData(res); setTimeout(() => { toast.dismiss(); }, 2000); } } }, }); // const cloneListFM = (data: any[]) => { // if (mode === "form") { // local.dataForm = data.map((e: any) => cloneFM(fm, e)); // local.render(); // } // }; useEffect(() => { const defaultColumns: any[] = init_column(column); const col = defaultColumns?.length ? defaultColumns.map((e: any) => { return { ...e, width: e?.width || "auto", }; }) : ([] as any[]); setColumns(col); local.data = fm?.data[name] || []; local.render(); console.log(columns); }, []); const handleResize = (index: any, width: any) => { setColumns((prevColumns: any) => { const updatedColumns = [...prevColumns]; updatedColumns[index].width = width; return updatedColumns; }); }; return ( <>
{!disabledHeader ? (
{sideLeft ? sideLeft(local) : <>}
{sideRight ? sideRight(local) : <>}
) : ( <> )}
th:first-child { width: 20px !important; /* Atur lebar sesuai kebutuhan */ text-align: center; } .table-row-element > td:first-child { width: 20px !important; /* Atur lebar sesuai kebutuhan */ text-align: center; } ` )} > {!disabledHeadTable ? ( {columns.map((col, idx) => { return ( ); })} ) : ( <> )} {local.data.map((row: any, index: any) => { const fm_row = cloneFM(fm, row); return ( {columns.map((col, idx) => { const param = { row: row, name: col?.name, idx, tbl: local, fm_row: fm_row, onChange, }; const renderData = typeof col?.renderCell === "function" ? ( col.renderCell(param) ) : ( <>No Column ); return ( ); })} ); })}
{col?.name}
{renderData}
{!local?.data?.length && (
No Data
)}
); return ( <>
{!disabledHeader ? (
{sideLeft ? ( sideLeft(local) ) : ( <> )}
{ e.preventDefault(); await local.reload(); }} >
{ const value = e.target.value; local.search = value; local.render(); handleSearch(); }} />
{sideRight ? sideRight(local) : <>}
) : ( <> )}
th:first-child { width: 20px !important; /* Atur lebar sesuai kebutuhan */ text-align: center; } .table-row-element > td:first-child { width: 20px !important; /* Atur lebar sesuai kebutuhan */ text-align: center; } ` )} > {!disabledHeadTable ? ( {table.getHeaderGroups().map((headerGroup) => ( {headerGroup.headers.map((header, index) => { const name = header.column.id; const col = column.find( (e: any) => e?.name === name ); const isSort = name === "select" ? false : typeof col?.sortable === "boolean" ? col.sortable : true; const resize = name === "select" ? false : typeof col?.resize === "boolean" ? col.resize : true; return ( ); })} ))} ) : ( <> )} {table.getRowModel().rows.map((row, idx) => { const fm_row = mode === "form" ? local.dataForm?.[idx] : null; return ( td { vertical-align: ${align}; } `, "border-none" )} > {row.getVisibleCells().map((cell: any) => { const ctx = cell.getContext(); const param = { row: row.original, name: get(ctx, "column.columnDef.accessorKey"), cell, idx, tbl: local, fm_row: fm_row, onChange, }; const head = column.find( (e: any) => e?.name === get(ctx, "column.columnDef.accessorKey") ); const renderData = typeof head?.renderCell === "function" ? head.renderCell(param) : flexRender( cell.column.columnDef.cell, cell.getContext() ); return ( {renderData} ); })} ); })}
{ if (isSort) { const sort = local?.sort?.[name]; const mode = sort === "desc" ? null : sort === "asc" ? "desc" : "asc"; local.sort = mode ? { [name]: mode, } : {}; local.render(); local.reload(); } }} className={cx( "flex flex-grow flex-row flex-grow select-none items-center flex-row text-base text-nowrap", isSort ? " cursor-pointer" : "" )} >
{header.isPlaceholder ? null : flexRender( header.column.columnDef.header, header.getContext() )}
{isSort ? (
) : ( <> )}
{headerGroup.headers.length !== index + 1 ? (
header.column.resetSize(), onMouseDown: header.getResizeHandler(), onTouchStart: header.getResizeHandler(), className: cx( `resizer bg-[#b3c9fe] cursor-e-resize ${ table.options.columnResizeDirection } ${ header.column.getIsResizing() ? "isResizing" : "" }`, css` width: 1px; cursor: e-resize !important; ` ), style: { transform: columnResizeMode === "onEnd" && header.column.getIsResizing() ? `translateX(${ (table.options .columnResizeDirection === "rtl" ? -1 : 1) * (table.getState() .columnSizingInfo .deltaOffset ?? 0) }px)` : "", }, }} >
) : null}
{!hiddenNoRow && !table.getRowModel().rows?.length && (
No Data
)}
table.nextPage()} onPrevPage={() => table.previousPage()} disabledNextPage={!table.getCanNextPage()} disabledPrevPage={!table.getCanPreviousPage()} page={table.getState().pagination.pageIndex + 1} setPage={(page: any) => { setPagination({ pageIndex: page, pageSize: 20, }); }} countPage={table.getPageCount()} countData={local.data.length} take={take} onChangePage={(page: number) => { table.setPageIndex(page); }} />
); }; export const Pagination: React.FC = ({ onNextPage, onPrevPage, disabledNextPage, disabledPrevPage, page, count, list, setPage, onChangePage, }) => { const local = useLocal({ page: 1 as any, pagination: [] as any, }); useEffect(() => { local.page = page; local.pagination = getPagination(page, Math.ceil(count / 20)); local.render(); }, [page, count]); return (
Showing {local.page * 20 - 19} to{" "} {list.data?.length >= 20 ? local.page * 20 : local.page === 1 && Math.ceil(count / 20) === 1 ? list.data?.length : local.page * 20 - 19 + list.data?.length}{" "} of {formatMoney(getNumber(count))} results
{ if (!disabledPrevPage) { onPrevPage(); } }} className={cx( "flex flex-row items-center gap-x-2 justify-center rounded p-1 ", disabledPrevPage ? "text-gray-200 border-gray-200 border px-2" : "cursor-pointer text-gray-500 hover:bg-gray-100 hover:text-gray-900 border-gray-500 border px-2" )} > Previous
{ if (!disabledNextPage) { onNextPage(); } }} className={cx( "flex flex-row items-center gap-x-2 justify-center rounded p-1 ", disabledNextPage ? "text-gray-200 border-gray-200 border px-2" : "cursor-pointer text-gray-500 hover:bg-gray-100 hover:text-gray-900 border-gray-500 border px-2" )} > Next
); }; export const PaginationPage: React.FC = ({ onNextPage, onPrevPage, disabledNextPage, disabledPrevPage, page, count, list, take, setPage, onChangePage, }) => { const local = useLocal({ page: 1 as any, pagination: [] as any, }); useEffect(() => { local.page = page; local.pagination = getPagination(page, Math.ceil(count / take)); local.render(); }, [page, count]); return (
{ if (!disabledPrevPage) { onPrevPage(); } }} className={cx( "flex flex-row items-center gap-x-2 justify-center rounded-full p-2 text-md", disabledPrevPage ? "text-gray-200 border-gray-200 border " : "cursor-pointer text-gray-500 hover:bg-gray-100 hover:text-gray-900 border-gray-500 border " )} >
{ if (!disabledNextPage) { onNextPage(); } }} className={cx( "flex flex-row items-center gap-x-2 justify-center rounded-full p-2 ", disabledNextPage ? "text-gray-200 border-gray-200 border" : "cursor-pointer text-gray-500 hover:bg-gray-100 hover:text-gray-900 border-gray-500 border " )} >
); }; const getPagination = (currentPage: number, totalPages: number) => { const pagination: { label: string; active: boolean }[] = []; const maxVisible = 5; // Jumlah maksimal elemen yang ditampilkan const halfRange = Math.floor((maxVisible - 3) / 2); if (totalPages <= maxVisible) { // Jika total halaman lebih kecil dari batas, tampilkan semua halaman for (let i = 1; i <= totalPages; i++) { pagination.push({ label: i.toString(), active: i === currentPage }); } } else { pagination.push({ label: "1", active: currentPage === 1 }); // Halaman pertama selalu ada if (currentPage > halfRange + 2) { pagination.push({ label: "...", active: false }); // Awal titik-titik } const startPage = Math.max(2, currentPage - halfRange); const endPage = Math.min(totalPages - 1, currentPage + halfRange); for (let i = startPage; i <= endPage; i++) { pagination.push({ label: i.toString(), active: i === currentPage }); } if (currentPage < totalPages - halfRange - 1) { pagination.push({ label: "...", active: false }); // Akhir titik-titik } pagination.push({ label: totalPages.toString(), active: currentPage === totalPages, }); // Halaman terakhir selalu ada } return pagination; };