feat: Implement token refresh mechanism and infinite scroll for code pedimento regimens
This commit is contained in:
@@ -1,78 +1,68 @@
|
||||
<script lang="ts" generics="TData, TValue">
|
||||
import { onMount } from 'svelte';
|
||||
import {
|
||||
type ColumnDef,
|
||||
type PaginationState,
|
||||
getCoreRowModel,
|
||||
getPaginationRowModel
|
||||
getCoreRowModel
|
||||
} from "@tanstack/table-core";
|
||||
import { createSvelteTable, FlexRender } from "$lib/components/ui/data-table/index.js";
|
||||
import * as Table from "$lib/components/ui/table/index.js";
|
||||
import { Button } from "$lib/components/ui/button/index.js";
|
||||
|
||||
type DataTableProps<TData, TValue> = {
|
||||
columns: ColumnDef<TData, TValue>[];
|
||||
data: TData[];
|
||||
totalItems: number;
|
||||
currentPage: number;
|
||||
pageSize: number;
|
||||
onPageChange: (page: number) => void;
|
||||
loading: boolean;
|
||||
hasMore: boolean;
|
||||
loadMore: () => void;
|
||||
};
|
||||
|
||||
let {
|
||||
data,
|
||||
columns,
|
||||
totalItems,
|
||||
currentPage,
|
||||
pageSize,
|
||||
onPageChange
|
||||
loading,
|
||||
hasMore,
|
||||
loadMore
|
||||
}: DataTableProps<TData, TValue> = $props();
|
||||
|
||||
let pagination = $state<PaginationState>({
|
||||
pageIndex: currentPage - 1,
|
||||
pageSize: pageSize
|
||||
});
|
||||
|
||||
const table = createSvelteTable({
|
||||
get data() {
|
||||
return data;
|
||||
},
|
||||
columns,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
manualPagination: true,
|
||||
pageCount: Math.ceil(totalItems / pageSize),
|
||||
state: {
|
||||
get pagination() {
|
||||
return pagination;
|
||||
}
|
||||
}
|
||||
getCoreRowModel: getCoreRowModel()
|
||||
});
|
||||
|
||||
$effect(() => {
|
||||
pagination = {
|
||||
pageIndex: currentPage - 1,
|
||||
pageSize: pageSize
|
||||
let scrollContainer = $state<HTMLDivElement>();
|
||||
let loadingTrigger = $state<HTMLDivElement>();
|
||||
|
||||
// Intersection Observer para detectar cuando el usuario llega al final
|
||||
onMount(() => {
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
const [entry] = entries;
|
||||
if (entry.isIntersecting && hasMore && !loading) {
|
||||
loadMore();
|
||||
}
|
||||
},
|
||||
{
|
||||
root: scrollContainer,
|
||||
threshold: 0.1
|
||||
}
|
||||
);
|
||||
|
||||
if (loadingTrigger) {
|
||||
observer.observe(loadingTrigger);
|
||||
}
|
||||
|
||||
return () => {
|
||||
observer.disconnect();
|
||||
};
|
||||
});
|
||||
|
||||
function handlePreviousPage() {
|
||||
if (currentPage > 1) {
|
||||
onPageChange(currentPage - 1);
|
||||
}
|
||||
}
|
||||
|
||||
function handleNextPage() {
|
||||
const totalPages = Math.ceil(totalItems / pageSize);
|
||||
if (currentPage < totalPages) {
|
||||
onPageChange(currentPage + 1);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="w-full">
|
||||
<div class="rounded-md border">
|
||||
<div class="rounded-md border max-h-[600px] overflow-y-auto" bind:this={scrollContainer}>
|
||||
<Table.Root>
|
||||
<Table.Header>
|
||||
<Table.Header class="sticky top-0 bg-background z-10">
|
||||
{#each table.getHeaderGroups() as headerGroup (headerGroup.id)}
|
||||
<Table.Row>
|
||||
{#each headerGroup.headers as header (header.id)}
|
||||
@@ -107,33 +97,27 @@
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
{/each}
|
||||
|
||||
<!-- Loading Trigger - Se activa cuando es visible -->
|
||||
{#if hasMore}
|
||||
<Table.Row>
|
||||
<Table.Cell colspan={columns.length} class="h-20 text-center">
|
||||
<div bind:this={loadingTrigger}>
|
||||
{#if loading}
|
||||
<div class="flex items-center justify-center gap-2">
|
||||
<div class="h-4 w-4 animate-spin rounded-full border-2 border-primary border-t-transparent"></div>
|
||||
<span class="text-muted-foreground text-sm">Cargando más...</span>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="text-muted-foreground text-sm">
|
||||
Desplázate para cargar más
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
{/if}
|
||||
</Table.Body>
|
||||
</Table.Root>
|
||||
</div>
|
||||
<div class="flex items-center justify-between space-x-2 py-4">
|
||||
<div class="text-muted-foreground text-sm">
|
||||
Mostrando {data.length} de {totalItems} registro(s)
|
||||
</div>
|
||||
<div class="flex items-center space-x-2">
|
||||
<div class="text-muted-foreground text-sm">
|
||||
Página {currentPage} de {Math.ceil(totalItems / pageSize)}
|
||||
</div>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onclick={handlePreviousPage}
|
||||
disabled={currentPage === 1}
|
||||
>
|
||||
Anterior
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onclick={handleNextPage}
|
||||
disabled={currentPage >= Math.ceil(totalItems / pageSize)}
|
||||
>
|
||||
Siguiente
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user