feat: enhance DataTable component with pagination support and meta data handling
This commit is contained in:
parent
4df6c215ad
commit
f5698ee47f
@ -1,9 +1,9 @@
|
||||
import type {
|
||||
ColumnDef,
|
||||
ColumnFiltersState,
|
||||
PaginationState,
|
||||
SortingState,
|
||||
VisibilityState,
|
||||
PaginationState,
|
||||
} from '@tanstack/react-table';
|
||||
import {
|
||||
flexRender,
|
||||
@ -87,6 +87,7 @@ interface DataTableProps<TData, TValue> {
|
||||
showSelection?: boolean;
|
||||
rowSelection?: any;
|
||||
onRowSelectionChange?: (selection: any) => void;
|
||||
meta?: any; // PaginatedData<TData> from Inertia
|
||||
}
|
||||
|
||||
export function DataTable<TData, TValue>({
|
||||
@ -98,6 +99,7 @@ export function DataTable<TData, TValue>({
|
||||
showSelection = true,
|
||||
rowSelection = {},
|
||||
onRowSelectionChange,
|
||||
meta,
|
||||
}: DataTableProps<TData, TValue>) {
|
||||
const [sorting, setSorting] = React.useState<SortingState>([]);
|
||||
const [columnFilters, setColumnFilters] =
|
||||
@ -182,6 +184,7 @@ export function DataTable<TData, TValue>({
|
||||
},
|
||||
onPaginationChange: setPagination,
|
||||
autoResetPageIndex: false,
|
||||
manualPagination: !!meta,
|
||||
});
|
||||
|
||||
const selectedRows = table.getFilteredSelectedRowModel().rows;
|
||||
@ -499,38 +502,50 @@ export function DataTable<TData, TValue>({
|
||||
<div className="flex flex-col items-center justify-between gap-4 py-4 sm:flex-row">
|
||||
<div className="text-sm text-muted-foreground">
|
||||
{table.getFilteredSelectedRowModel().rows.length} dari{' '}
|
||||
{table.getFilteredRowModel().rows.length} baris dipilih.
|
||||
{meta
|
||||
? meta.total
|
||||
: table.getFilteredRowModel().rows.length}{' '}
|
||||
baris dipilih.
|
||||
</div>
|
||||
<div className="flex flex-col items-center gap-4 sm:flex-row sm:gap-6 lg:gap-8">
|
||||
<div className="flex items-center gap-2">
|
||||
<p className="text-sm font-medium">Baris per halaman</p>
|
||||
<Select
|
||||
value={`${table.getState().pagination.pageSize}`}
|
||||
onValueChange={(value) => {
|
||||
table.setPageSize(Number(value));
|
||||
}}
|
||||
>
|
||||
<SelectTrigger className="h-8 w-[70px]">
|
||||
<SelectValue
|
||||
placeholder={
|
||||
table.getState().pagination.pageSize
|
||||
}
|
||||
/>
|
||||
</SelectTrigger>
|
||||
<SelectContent side="top">
|
||||
{[10, 20, 30, 40, 50].map((pageSize) => (
|
||||
<SelectItem
|
||||
key={pageSize}
|
||||
value={`${pageSize}`}
|
||||
>
|
||||
{pageSize}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
{!meta && (
|
||||
<div className="flex items-center gap-2">
|
||||
<p className="text-sm font-medium">
|
||||
Baris per halaman
|
||||
</p>
|
||||
<Select
|
||||
value={`${table.getState().pagination.pageSize}`}
|
||||
onValueChange={(value) => {
|
||||
table.setPageSize(Number(value));
|
||||
}}
|
||||
>
|
||||
<SelectTrigger className="h-8 w-[70px]">
|
||||
<SelectValue
|
||||
placeholder={
|
||||
table.getState().pagination.pageSize
|
||||
}
|
||||
/>
|
||||
</SelectTrigger>
|
||||
<SelectContent side="top">
|
||||
{[10, 20, 30, 40, 50].map((pageSize) => (
|
||||
<SelectItem
|
||||
key={pageSize}
|
||||
value={`${pageSize}`}
|
||||
>
|
||||
{pageSize}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex items-center justify-center text-sm font-medium">
|
||||
{table.getFilteredRowModel().rows.length > 0 ? (
|
||||
{meta ? (
|
||||
<>
|
||||
Menampilkan {meta.from || 0} sampai{' '}
|
||||
{meta.to || 0} dari {meta.total} data
|
||||
</>
|
||||
) : table.getFilteredRowModel().rows.length > 0 ? (
|
||||
<>
|
||||
Menampilkan{' '}
|
||||
{table.getState().pagination.pageIndex *
|
||||
@ -550,46 +565,114 @@ export function DataTable<TData, TValue>({
|
||||
'Tidak ada data'
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
className="hidden h-8 w-8 p-0 lg:flex"
|
||||
onClick={() => table.setPageIndex(0)}
|
||||
disabled={!table.getCanPreviousPage()}
|
||||
>
|
||||
<span className="sr-only">Halaman pertama</span>
|
||||
<ChevronsLeft className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="h-8 w-8 p-0"
|
||||
onClick={() => table.previousPage()}
|
||||
disabled={!table.getCanPreviousPage()}
|
||||
>
|
||||
<span className="sr-only">Halaman sebelumnya</span>
|
||||
<ChevronLeft className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="h-8 w-8 p-0"
|
||||
onClick={() => table.nextPage()}
|
||||
disabled={!table.getCanNextPage()}
|
||||
>
|
||||
<span className="sr-only">Halaman selanjutnya</span>
|
||||
<ChevronRight className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="hidden h-8 w-8 p-0 lg:flex"
|
||||
onClick={() =>
|
||||
table.setPageIndex(table.getPageCount() - 1)
|
||||
}
|
||||
disabled={!table.getCanNextPage()}
|
||||
>
|
||||
<span className="sr-only">Halaman terakhir</span>
|
||||
<ChevronsRight className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
{meta ? (() => {
|
||||
const { current_page, last_page, prev_page_url, next_page_url } = meta;
|
||||
|
||||
const leftPages = [];
|
||||
for (let i = current_page - 3; i < current_page; i++) {
|
||||
if (i > 0) leftPages.push(i);
|
||||
}
|
||||
|
||||
const rightPages = [];
|
||||
for (let i = current_page + 1; i <= current_page + 2; i++) {
|
||||
if (i <= last_page) rightPages.push(i);
|
||||
}
|
||||
|
||||
const handlePageChange = (page: number) => {
|
||||
const url = new URL(window.location.href);
|
||||
url.searchParams.set('page', page.toString());
|
||||
import('@inertiajs/react').then(({ router }) => {
|
||||
router.get(url.toString(), {}, { preserveState: true, preserveScroll: true });
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex items-center space-x-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
className="h-8 w-8 p-0"
|
||||
onClick={() => {
|
||||
if (prev_page_url) {
|
||||
import('@inertiajs/react').then(({ router }) => {
|
||||
router.get(prev_page_url, {}, { preserveState: true, preserveScroll: true });
|
||||
});
|
||||
}
|
||||
}}
|
||||
disabled={!prev_page_url}
|
||||
>
|
||||
<ChevronLeft className="h-4 w-4" />
|
||||
</Button>
|
||||
|
||||
{leftPages.map(p => (
|
||||
<Button key={p} variant="outline" className="h-8 px-3" onClick={() => handlePageChange(p)}>
|
||||
{p}
|
||||
</Button>
|
||||
))}
|
||||
|
||||
<Button variant="outline" className="h-8 px-3 border-none bg-transparent shadow-none" disabled>
|
||||
...
|
||||
</Button>
|
||||
|
||||
{rightPages.map(p => (
|
||||
<Button key={p} variant="outline" className="h-8 px-3" onClick={() => handlePageChange(p)}>
|
||||
{p}
|
||||
</Button>
|
||||
))}
|
||||
|
||||
<Button
|
||||
variant="outline"
|
||||
className="h-8 w-8 p-0"
|
||||
onClick={() => {
|
||||
if (next_page_url) {
|
||||
import('@inertiajs/react').then(({ router }) => {
|
||||
router.get(next_page_url, {}, { preserveState: true, preserveScroll: true });
|
||||
});
|
||||
}
|
||||
}}
|
||||
disabled={!next_page_url}
|
||||
>
|
||||
<ChevronRight className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
})() : (
|
||||
<div className="flex items-center space-x-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
className="hidden h-8 w-8 p-0 lg:flex"
|
||||
onClick={() => table.setPageIndex(0)}
|
||||
disabled={!table.getCanPreviousPage()}
|
||||
>
|
||||
<ChevronsLeft className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="h-8 w-8 p-0"
|
||||
onClick={() => table.previousPage()}
|
||||
disabled={!table.getCanPreviousPage()}
|
||||
>
|
||||
<ChevronLeft className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="h-8 w-8 p-0"
|
||||
onClick={() => table.nextPage()}
|
||||
disabled={!table.getCanNextPage()}
|
||||
>
|
||||
<ChevronRight className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="hidden h-8 w-8 p-0 lg:flex"
|
||||
onClick={() =>
|
||||
table.setPageIndex(table.getPageCount() - 1)
|
||||
}
|
||||
disabled={!table.getCanNextPage()}
|
||||
>
|
||||
<ChevronsRight className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -12,3 +12,4 @@ export type * from './system-log';
|
||||
export type * from './dashboard';
|
||||
export type * from './role';
|
||||
export type * from './permission';
|
||||
export type * from './pagination';
|
||||
|
||||
15
resources/js/types/pagination.ts
Normal file
15
resources/js/types/pagination.ts
Normal file
@ -0,0 +1,15 @@
|
||||
export interface PaginatedData<T> {
|
||||
current_page: number;
|
||||
data: T[];
|
||||
first_page_url: string;
|
||||
from: number;
|
||||
last_page: number;
|
||||
last_page_url: string;
|
||||
links: { url: string | null; label: string; active: boolean }[];
|
||||
next_page_url: string | null;
|
||||
path: string;
|
||||
per_page: number;
|
||||
prev_page_url: string | null;
|
||||
to: number;
|
||||
total: number;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user