Files
plantillas-proyectos/frontend/src/routes/dashboard/reference_data/containers/+page.svelte
acazares dcd42583d9 feat: replace SVG icons with Lucide icons in reference data pages
- Updated the "Nueva Sección" button in customs_sections, customs_warehouses, incoterms, invoice_types, material_types, payment_methods, pedimento_codes, pedimento_regimens, sectors, states, transport_modes, and transport_types pages to use the Plus icon from Lucide.
- Updated the "Actualizar" button in the same pages to use the RefreshCw icon from Lucide.
- Added a new edit dialog component for customs brokers with a comprehensive form for editing broker details, including validation and loading states.
2025-11-24 16:43:53 -06:00

170 lines
4.9 KiB
Svelte

<script lang="ts">
import { onMount } from 'svelte';
import { containersApi, type Container } from '$lib/api/dashboard/refrence_data/containers';
import DataTable from '$lib/components/dashboard/reference_data/containers/data-table.svelte';
import { createColumns } from '$lib/components/dashboard/reference_data/containers/columns.js';
import CreateEditDialog from '$lib/components/dashboard/reference_data/containers/create-edit-dialog.svelte';
import * as Card from '$lib/components/ui/card';
import { Button } from '$lib/components/ui/button';
import type { PageData } from './$types';
import { browser } from '$app/environment';
import { Plus, RefreshCw } from 'lucide-svelte';
// Los datos iniciales vienen del servidor
let { data }: { data: PageData } = $props();
// Estado para el diálogo de crear
let showCreateDialog = $state(false);
// Sincronizar token de cookies a localStorage al montar el componente
onMount(() => {
if (browser) {
// Función para obtener el valor de una cookie
const getCookie = (name: string): string | null => {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) return parts.pop()?.split(';').shift() || null;
return null;
};
// Verificar si hay token en las cookies
const cookieToken = getCookie('access_token');
const localToken = localStorage.getItem('access_token');
if (cookieToken && cookieToken !== localToken) {
localStorage.setItem('access_token', cookieToken);
}
// También sincronizar refresh_token si existe
const cookieRefreshToken = getCookie('refresh_token');
const localRefreshToken = localStorage.getItem('refresh_token');
if (cookieRefreshToken && cookieRefreshToken !== localRefreshToken) {
localStorage.setItem('refresh_token', cookieRefreshToken);
}
}
});
// Estado para infinite scroll
let allItems = $state<Container[]>(data.items || []);
let currentPage = $state(data.page || 1);
let pageSize = $state(50);
let totalItems = $state(data.total || 0);
let loading = $state(false);
let hasMore = $derived(allItems.length < totalItems);
let error = $state<string | null>(data.error || null);
async function loadMore() {
if (loading || !hasMore) return;
loading = true;
error = null;
try {
const response = await containersApi.list(currentPage + 1, pageSize);
if (response.error) {
console.error('📊 [Page] Error en loadMore:', response.error, 'Status:', response.status);
// Si es un error de autenticación (401 o 403) y no se pudo refrescar, mostrar mensaje específico
if (response.status === 401 || response.status === 403) {
error = 'Sesión expirada. Recargando página...';
// Recargar automáticamente después de 2 segundos
setTimeout(() => {
window.location.reload();
}, 2000);
} else {
error = response.error;
}
return;
}
if (response.data?.items) {
// Agregar los nuevos items al array existente
allItems = [...allItems, ...response.data.items];
currentPage++;
totalItems = response.data.total;
}
} catch (e) {
error = 'Error cargando más datos';
console.error('📊 [Page] Error loading more:', e);
} finally {
loading = false;
}
}
function reloadData() {
// Reset y recargar desde el principio
window.location.reload();
}
function handleCreateClick() {
showCreateDialog = true;
}
function handleSuccess() {
// Recargar datos después de crear/editar/eliminar
reloadData();
}
// Crear columnas con el callback onSuccess
const columns = createColumns(handleSuccess);
</script>
<div class="space-y-6">
<!-- Header -->
<div class="flex items-center justify-between">
<div>
<h1 class="text-3xl font-bold tracking-tight">Contenedores</h1>
<p class="text-muted-foreground">
Gestiona los tipos de contenedores disponibles
</p>
</div>
<Button onclick={handleCreateClick}>
<Plus class="mr-2" size={16} />
Nuevo Contenedor
</Button>
</div>
<!-- Error Message -->
{#if error}
<Card.Root class="border-destructive">
<Card.Header>
<Card.Title class="text-destructive">Error</Card.Title>
<Card.Description>{error}</Card.Description>
</Card.Header>
</Card.Root>
{/if}
<!-- Data Table -->
<Card.Root>
<Card.Header>
<div class="flex items-center justify-between">
<div>
<Card.Title>Listado de Contenedores</Card.Title>
<Card.Description>
Mostrando {allItems.length} de {totalItems} registros
</Card.Description>
</div>
<Button variant="outline" onclick={reloadData}>
<RefreshCw class="mr-2" size={16} />
Actualizar
</Button>
</div>
</Card.Header>
<Card.Content>
<!-- TanStack DataTable con Infinite Scroll -->
<DataTable
data={allItems}
{columns}
{loading}
{hasMore}
{loadMore}
/>
</Card.Content>
</Card.Root>
</div>
<!-- Diálogo de crear/editar -->
<CreateEditDialog bind:open={showCreateDialog} onSuccess={handleSuccess} />