From 6d4e74015ab7326cef22cbe1ce0ba586f3173458 Mon Sep 17 00:00:00 2001 From: hreyes Date: Wed, 29 Apr 2026 09:58:27 -0600 Subject: [PATCH] feature/partidas-pais-origen --- .../edit/items/fa/country-dialog.svelte | 145 +++++++++++++----- .../invoices/edit/items/fa/main-data.svelte | 2 +- .../edit/items/inv/item-sheet-inv.svelte | 2 +- 3 files changed, 106 insertions(+), 43 deletions(-) diff --git a/frontend/src/lib/components/dashboard/invoices/edit/items/fa/country-dialog.svelte b/frontend/src/lib/components/dashboard/invoices/edit/items/fa/country-dialog.svelte index be2a9491..5f38ce9c 100644 --- a/frontend/src/lib/components/dashboard/invoices/edit/items/fa/country-dialog.svelte +++ b/frontend/src/lib/components/dashboard/invoices/edit/items/fa/country-dialog.svelte @@ -3,7 +3,7 @@ import { Button } from '$lib/components/ui/button'; import { Input } from '$lib/components/ui/input'; import { Loader2, Search } from 'lucide-svelte'; - import { countriesApi } from '$lib/api/dashboard/reference_data/countries'; + import { countriesApi, type Country } from '$lib/api/dashboard/reference_data/countries'; import { companyStore } from '$lib/stores/company.svelte'; import { toast } from 'svelte-sonner'; @@ -12,17 +12,43 @@ onSelect }: { open: boolean; - onSelect: (country: any) => void; + onSelect: (country: Country) => void; } = $props(); - let countries: any[] = $state([]); - let filteredCountries: any[] = $state([]); + let countries = $state([]); let loading = $state(false); + let loadingMore = $state(false); let searchTerm = $state(''); + let previousSearchTerm = ''; + let page = $state(1); + const pageSize = 100; + let hasMore = $state(true); + let totalItems = $state(0); let error = $state(''); + let observer: IntersectionObserver | null = null; + let bottomSentinel: HTMLElement | null = $state(null); + let searchTimeout: ReturnType | undefined; + let isInitialized = false; - async function loadCountries() { - loading = true; + async function resetAndLoad() { + page = 1; + countries = []; + hasMore = true; + await loadCountries(true); + } + + async function loadMore() { + if (!hasMore || loading || loadingMore) return; + page += 1; + await loadCountries(false); + } + + async function loadCountries(isInitial: boolean) { + if (isInitial) { + loading = true; + } else { + loadingMore = true; + } error = ''; try { const companyId = companyStore.activeCompany?.id; @@ -31,13 +57,17 @@ return; } - // Para este diálogo, cargamos una cantidad grande o implementamos paginación si fuera necesario - // Por ahora seguimos el patrón original de cargar "todos" (limite 100 en backend) - const response = await countriesApi.list(companyId, 1, 100); - + const response = await countriesApi.list(companyId, page, pageSize, searchTerm); + if (response.data) { - countries = response.data.items || []; - filteredCountries = countries; + const newItems = response.data.items || []; + totalItems = response.data.total || 0; + if (isInitial) { + countries = newItems; + } else { + countries = [...countries, ...newItems]; + } + hasMore = countries.length < totalItems && newItems.length > 0; } else if (response.error) { error = `Error: ${response.error}`; toast.error(error); @@ -48,30 +78,16 @@ toast.error(error); } finally { loading = false; + loadingMore = false; } } - function filterCountries() { - if (!searchTerm.trim()) { - filteredCountries = countries; - } else { - const term = searchTerm.toLowerCase(); - filteredCountries = countries.filter( - (country) => - country.m3_key?.toLowerCase().includes(term) || - country.mex_key?.toLowerCase().includes(term) || - country.description_es?.toLowerCase().includes(term) || - country.description_en?.toLowerCase().includes(term) - ); - } - } - - function handleSelect(country: any) { + function handleSelect(country: Country) { onSelect(country); open = false; } - function handleRowKeydown(event: KeyboardEvent, country: any) { + function handleRowKeydown(event: KeyboardEvent, country: Country) { if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); handleSelect(country); @@ -79,13 +95,45 @@ } $effect(() => { - if (open) { - loadCountries(); + if (open && !isInitialized) { + isInitialized = true; + previousSearchTerm = searchTerm; + void resetAndLoad(); + } else if (!open) { + isInitialized = false; } }); $effect(() => { - filterCountries(); + const term = searchTerm; + if (isInitialized && term !== previousSearchTerm) { + if (searchTimeout) clearTimeout(searchTimeout); + searchTimeout = setTimeout(() => { + previousSearchTerm = term; + void resetAndLoad(); + }, 400); + } + }); + + $effect(() => { + if (bottomSentinel && hasMore && !loading && !loadingMore && open) { + if (observer) observer.disconnect(); + + observer = new IntersectionObserver( + (entries) => { + if (entries[0]?.isIntersecting && hasMore && !loading && !loadingMore) { + void loadMore(); + } + }, + { threshold: 0.1 } + ); + + observer.observe(bottomSentinel); + } + + return () => { + if (observer) observer.disconnect(); + }; }); @@ -100,14 +148,19 @@ +

+ {totalItems > 0 ? `${countries.length} de ${totalItems} mostrados` : ''} — desplázate para cargar + más; el catálogo completo supera 100 países. +

- {#if loading} + {#if loading && countries.length === 0}
@@ -136,21 +189,21 @@ - {#each filteredCountries as country} + {#each countries as country} handleSelect(country)} onkeydown={(event) => handleRowKeydown(event, country)} > - {country.m3_key || ''} - {country.mex_key || ''} - {country.description_es || ''} - {country.ame_key || ''} - {country.description_en || ''} + {country.m3_key || ''} + {country.mex_key || ''} + {country.description_es || ''} + {country.ame_key || ''} + {country.description_en || ''} {/each} - {#if filteredCountries.length === 0} + {#if countries.length === 0} No se encontraron resultados @@ -161,6 +214,16 @@
+ +
Usa flechas para navegar filas y Enter/Espacio para seleccionar.
diff --git a/frontend/src/lib/components/dashboard/invoices/edit/items/fa/main-data.svelte b/frontend/src/lib/components/dashboard/invoices/edit/items/fa/main-data.svelte index c3fe551c..1636f9b2 100644 --- a/frontend/src/lib/components/dashboard/invoices/edit/items/fa/main-data.svelte +++ b/frontend/src/lib/components/dashboard/invoices/edit/items/fa/main-data.svelte @@ -178,7 +178,7 @@ function handleCountrySelect(country: any) { customs.origin_country = country.m3_key || country.mex_key; - (customs as any).origin_country_name = country.description || country.description_en; + (customs as any).origin_country_name = country.description_es || country.description_en; } function handleFractionSelect(fraction: any) { diff --git a/frontend/src/lib/components/dashboard/invoices/edit/items/inv/item-sheet-inv.svelte b/frontend/src/lib/components/dashboard/invoices/edit/items/inv/item-sheet-inv.svelte index 74c8ecd5..b399a96d 100644 --- a/frontend/src/lib/components/dashboard/invoices/edit/items/inv/item-sheet-inv.svelte +++ b/frontend/src/lib/components/dashboard/invoices/edit/items/inv/item-sheet-inv.svelte @@ -113,7 +113,7 @@ if (!editingItem.customs) editingItem.customs = {} as any; if (editingItem.customs) { editingItem.customs.origin_country = country.m3_key || country.mex_key; - (editingItem.customs as any).origin_country_name = country.description || country.description_en; + (editingItem.customs as any).origin_country_name = country.description_es || country.description_en; } }