feat: Implement Code Pedimento Regimens Data Table

- Added columns definition for Code Pedimento Regimens including ID, Pedimento Code, Regimen Code, Type, and Actions.
- Created DataTableActions component for handling actions on each row.
- Developed data-table component to manage pagination and rendering of the data table.
- Introduced helper functions for rendering components and snippets in table cells.
- Integrated API call in the server-side load function to fetch Code Pedimento Regimens data with pagination support.
- Updated package.json to include lucide-svelte dependency for icons.
This commit is contained in:
2025-11-02 13:15:05 -06:00
parent f29e219cf8
commit 2b735c27b8
30 changed files with 1390 additions and 69 deletions

View File

@@ -0,0 +1,85 @@
import type { ColumnDef } from "@tanstack/table-core";
import { renderComponent, renderSnippet } from "$lib/components/ui/data-table/index.js";
import { createRawSnippet } from "svelte";
import DataTableActions from "./data-table-actions.svelte";
export type CodePedimentoRegimen = {
id: number;
pedimento_code: string;
regimen_code: string | null;
type_code: string | null;
};
export const columns: ColumnDef<CodePedimentoRegimen>[] = [
{
accessorKey: "id",
header: "ID",
cell: ({ row }) => {
const idSnippet = createRawSnippet<[{ id: number }]>((getId) => {
const { id } = getId();
return {
render: () => `<div class="font-medium">${id}</div>`
};
});
return renderSnippet(idSnippet, { id: row.original.id });
}
},
{
accessorKey: "pedimento_code",
header: "Código Pedimento",
cell: ({ row }) => {
const codeSnippet = createRawSnippet<[{ code: string }]>((getCode) => {
const { code } = getCode();
return {
render: () =>
`<code class="relative rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-sm">${code}</code>`
};
});
return renderSnippet(codeSnippet, { code: row.original.pedimento_code });
}
},
{
accessorKey: "regimen_code",
header: "Código Régimen",
cell: ({ row }) => {
const regimenSnippet = createRawSnippet<[{ code: string | null }]>((getCode) => {
const { code } = getCode();
if (code) {
return {
render: () =>
`<code class="relative rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-sm">${code}</code>`
};
}
return {
render: () => `<span class="text-muted-foreground">N/A</span>`
};
});
return renderSnippet(regimenSnippet, { code: row.original.regimen_code });
}
},
{
accessorKey: "type_code",
header: "Tipo",
cell: ({ row }) => {
const typeSnippet = createRawSnippet<[{ type: string | null }]>((getType) => {
const { type } = getType();
if (type) {
return {
render: () =>
`<span class="inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2">${type}</span>`
};
}
return {
render: () => `<span class="text-muted-foreground">N/A</span>`
};
});
return renderSnippet(typeSnippet, { type: row.original.type_code });
}
},
{
id: "actions",
cell: ({ row }) => {
return renderComponent(DataTableActions, { item: row.original });
}
}
];

View File

@@ -0,0 +1,32 @@
<script lang="ts">
import EllipsisIcon from "@lucide/svelte/icons/ellipsis";
import { Button } from "$lib/components/ui/button/index.js";
import * as DropdownMenu from "$lib/components/ui/dropdown-menu/index.js";
import type { CodePedimentoRegimen } from "./columns.js";
let { item }: { item: CodePedimentoRegimen } = $props();
</script>
<DropdownMenu.Root>
<DropdownMenu.Trigger>
{#snippet child({ props })}
<Button {...props} variant="ghost" size="icon" class="relative size-8 p-0">
<span class="sr-only">Abrir menú</span>
<EllipsisIcon class="size-4" />
</Button>
{/snippet}
</DropdownMenu.Trigger>
<DropdownMenu.Content align="end">
<DropdownMenu.Group>
<DropdownMenu.Label>Acciones</DropdownMenu.Label>
<DropdownMenu.Item onclick={() => navigator.clipboard.writeText(item.id.toString())}>
Copiar ID
</DropdownMenu.Item>
</DropdownMenu.Group>
<DropdownMenu.Separator />
<DropdownMenu.Item>Ver detalles</DropdownMenu.Item>
<DropdownMenu.Item>Editar</DropdownMenu.Item>
<DropdownMenu.Separator />
<DropdownMenu.Item class="text-destructive">Eliminar</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Root>

View File

@@ -0,0 +1,139 @@
<script lang="ts" generics="TData, TValue">
import {
type ColumnDef,
type PaginationState,
getCoreRowModel,
getPaginationRowModel
} 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;
};
let {
data,
columns,
totalItems,
currentPage,
pageSize,
onPageChange
}: 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;
}
}
});
$effect(() => {
pagination = {
pageIndex: currentPage - 1,
pageSize: pageSize
};
});
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">
<Table.Root>
<Table.Header>
{#each table.getHeaderGroups() as headerGroup (headerGroup.id)}
<Table.Row>
{#each headerGroup.headers as header (header.id)}
<Table.Head>
{#if !header.isPlaceholder}
<FlexRender
content={header.column.columnDef.header}
context={header.getContext()}
/>
{/if}
</Table.Head>
{/each}
</Table.Row>
{/each}
</Table.Header>
<Table.Body>
{#each table.getRowModel().rows as row (row.id)}
<Table.Row data-state={row.getIsSelected() && "selected"}>
{#each row.getVisibleCells() as cell (cell.id)}
<Table.Cell>
<FlexRender
content={cell.column.columnDef.cell}
context={cell.getContext()}
/>
</Table.Cell>
{/each}
</Table.Row>
{:else}
<Table.Row>
<Table.Cell colspan={columns.length} class="h-24 text-center">
No hay resultados.
</Table.Cell>
</Table.Row>
{/each}
</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>