Inputs de busqueda
This commit is contained in:
@@ -61,6 +61,9 @@ class DodaService:
|
||||
if filters.get("patent"):
|
||||
query = query.filter(
|
||||
Doda.patent.ilike(f"%{filters['patent']}%"))
|
||||
if filters.get("operation_type"):
|
||||
query = query.filter(
|
||||
Doda.operation_type == filters["operation_type"])
|
||||
|
||||
total = query.count()
|
||||
dodas = query.offset(skip).limit(limit).all()
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
LayoutGrid,
|
||||
Printer
|
||||
} from 'lucide-svelte';
|
||||
import * as Select from '$lib/components/ui/select';
|
||||
import { companyStore } from '$lib/stores/company.svelte';
|
||||
import DataTable from '$lib/components/dashboard/general_catalogs/doda/data-table.svelte';
|
||||
import { createColumns } from '$lib/components/dashboard/general_catalogs/doda/columns';
|
||||
@@ -30,8 +31,14 @@
|
||||
let { data } = $props();
|
||||
let dialogOpen = $state(false);
|
||||
|
||||
// Filtros
|
||||
let searchIntegration = $state($page.url.searchParams.get('integration_number') || '');
|
||||
// Filtros centralizados
|
||||
let filters = $state({
|
||||
integration_number: $page.url.searchParams.get('integration_number') || '',
|
||||
patent: $page.url.searchParams.get('patent') || '',
|
||||
status: $page.url.searchParams.get('status') || '',
|
||||
operation_type: $page.url.searchParams.get('operation_type') || ''
|
||||
});
|
||||
|
||||
let timeout: ReturnType<typeof setTimeout>;
|
||||
|
||||
// State for infinite scroll
|
||||
@@ -47,7 +54,7 @@
|
||||
let selectedId = $state<number | null>(null);
|
||||
const selectedDoda = $derived(selectedId ? allItems.find((i) => i.id === selectedId) : null);
|
||||
|
||||
// Sincronizar con datos del servidor al cargar
|
||||
// Sincronizar con datos del servidor al cargar (primera carga)
|
||||
$effect(() => {
|
||||
if (data.dodas) {
|
||||
allItems = data.dodas.items || [];
|
||||
@@ -56,6 +63,35 @@
|
||||
}
|
||||
});
|
||||
|
||||
// Sincronizar filtros con la URL de forma reactiva
|
||||
$effect(() => {
|
||||
if (browser) {
|
||||
const params = new URLSearchParams();
|
||||
if (filters.integration_number) params.set('integration_number', filters.integration_number);
|
||||
if (filters.patent) params.set('patent', filters.patent);
|
||||
if (filters.status) params.set('status', filters.status);
|
||||
if (filters.operation_type) params.set('operation_type', filters.operation_type);
|
||||
|
||||
const queryString = params.toString();
|
||||
const newUrl = queryString ? `?${queryString}` : window.location.pathname;
|
||||
|
||||
if (window.location.search !== (queryString ? `?${queryString}` : '')) {
|
||||
window.history.replaceState({}, '', newUrl);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Disparar recarga cuando cambian los filtros (con debounce)
|
||||
$effect(() => {
|
||||
// Observamos todos los campos de filtros
|
||||
const _ = { ...filters };
|
||||
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(() => {
|
||||
reloadData();
|
||||
}, 400);
|
||||
});
|
||||
|
||||
// Atajos
|
||||
useShortcuts(
|
||||
'Lista DODA',
|
||||
@@ -73,8 +109,12 @@
|
||||
const companyId = companyStore.activeCompany?.id;
|
||||
if (!companyId) return;
|
||||
|
||||
const filters = searchIntegration ? { integration_number: searchIntegration } : {};
|
||||
const res = await getDodas(currentPage + 1, pageSize, filters, Number(companyId));
|
||||
// Limpiar filtros vacíos
|
||||
const activeFilters = Object.fromEntries(
|
||||
Object.entries(filters).filter(([_, v]) => v !== '')
|
||||
);
|
||||
|
||||
const res = await getDodas(currentPage + 1, pageSize, activeFilters, Number(companyId));
|
||||
|
||||
if (res.data) {
|
||||
allItems = [...allItems, ...res.data.items];
|
||||
@@ -89,13 +129,17 @@
|
||||
}
|
||||
|
||||
async function reloadData() {
|
||||
if (!browser) return;
|
||||
loading = true;
|
||||
try {
|
||||
const companyId = companyStore.activeCompany?.id;
|
||||
if (!companyId) return;
|
||||
|
||||
const filters = searchIntegration ? { integration_number: searchIntegration } : {};
|
||||
const res = await getDodas(1, pageSize, filters, Number(companyId));
|
||||
const activeFilters = Object.fromEntries(
|
||||
Object.entries(filters).filter(([_, v]) => v !== '')
|
||||
);
|
||||
|
||||
const res = await getDodas(1, pageSize, activeFilters, Number(companyId));
|
||||
|
||||
if (res.data) {
|
||||
allItems = res.data.items;
|
||||
@@ -105,17 +149,20 @@
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error reloading DODAs:', e);
|
||||
toast.error('Error al recargar datos');
|
||||
// Evitar mostrar error si es solo carga inicial y falla por falta de login etc
|
||||
if (allItems.length > 0) toast.error('Error al recargar datos');
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
function handleSearch() {
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(() => {
|
||||
reloadData();
|
||||
}, 500);
|
||||
function clearFilters() {
|
||||
filters = {
|
||||
integration_number: '',
|
||||
patent: '',
|
||||
status: '',
|
||||
operation_type: ''
|
||||
};
|
||||
}
|
||||
|
||||
function handleCreateClick() {
|
||||
@@ -161,21 +208,77 @@
|
||||
|
||||
<Card.Root>
|
||||
<Card.Header>
|
||||
<Card.Title>Filtros</Card.Title>
|
||||
<Card.Description>Búsqueda rápida por número de integración</Card.Description>
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<Card.Title>Filtros Avanzados</Card.Title>
|
||||
<Card.Description>Refina tu búsqueda mediante múltiples criterios</Card.Description>
|
||||
</div>
|
||||
<Button variant="ghost" size="sm" onclick={clearFilters} class="text-muted-foreground">
|
||||
<RotateCcw class="mr-2 h-4 w-4" />
|
||||
Limpiar Filtros
|
||||
</Button>
|
||||
</div>
|
||||
</Card.Header>
|
||||
<Card.Content>
|
||||
<div class="max-w-sm space-y-2">
|
||||
<Label for="search-integration">No. Integración</Label>
|
||||
<div class="relative">
|
||||
<Search class="absolute top-2.5 left-2.5 h-4 w-4 text-muted-foreground" />
|
||||
<Input
|
||||
id="search-integration"
|
||||
placeholder="Escribe para buscar..."
|
||||
bind:value={searchIntegration}
|
||||
oninput={handleSearch}
|
||||
class="pl-9"
|
||||
/>
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="search-integration">No. Integración</Label>
|
||||
<div class="relative">
|
||||
<Search class="absolute top-2.5 left-2.5 h-4 w-4 text-muted-foreground" />
|
||||
<Input
|
||||
id="search-integration"
|
||||
placeholder="Buscar integración..."
|
||||
bind:value={filters.integration_number}
|
||||
class="pl-9"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label for="search-patent">Patente</Label>
|
||||
<Input id="search-patent" placeholder="Buscar patente..." bind:value={filters.patent} />
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label>Estatus</Label>
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={filters.status}
|
||||
onValueChange={(v) => (filters.status = v)}
|
||||
>
|
||||
<Select.Trigger class="w-full">
|
||||
{filters.status || 'Todos los estatus'}
|
||||
</Select.Trigger>
|
||||
<Select.Content>
|
||||
<Select.Item value="">Todos</Select.Item>
|
||||
<Select.Item value="PENDIENTE">PENDIENTE</Select.Item>
|
||||
<Select.Item value="GENERADO">GENERADO</Select.Item>
|
||||
<Select.Item value="VALIDADO">VALIDADO</Select.Item>
|
||||
<Select.Item value="ELIMINADO">ELIMINADO</Select.Item>
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label>Operación</Label>
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={filters.operation_type}
|
||||
onValueChange={(v) => (filters.operation_type = v)}
|
||||
>
|
||||
<Select.Trigger class="w-full">
|
||||
{filters.operation_type === 'I'
|
||||
? 'Importación'
|
||||
: filters.operation_type === 'E'
|
||||
? 'Exportación'
|
||||
: 'Todas'}
|
||||
</Select.Trigger>
|
||||
<Select.Content>
|
||||
<Select.Item value="">Todas</Select.Item>
|
||||
<Select.Item value="I">I - Importación</Select.Item>
|
||||
<Select.Item value="E">E - Exportación</Select.Item>
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
</div>
|
||||
</Card.Content>
|
||||
|
||||
Reference in New Issue
Block a user