feat: update sidebar navigation for fixed asset classes and add tariff fractions page
This commit is contained in:
@@ -298,7 +298,7 @@ export function getSidebarData(): SidebarData {
|
||||
items: [
|
||||
{
|
||||
title: m["sidebar.goods.classes"](),
|
||||
url: "/dashboard/goods/classes",
|
||||
url: "/dashboard/goods/fixed-asset-classes",
|
||||
},
|
||||
{
|
||||
title: m["sidebar.goods.parts"](),
|
||||
@@ -386,18 +386,7 @@ export function getSidebarData(): SidebarData {
|
||||
url: "/dashboard/customs_brokers",
|
||||
icon: BadgeCheck,
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: "Mercancías",
|
||||
url: "#",
|
||||
icon: Package,
|
||||
items: [
|
||||
{
|
||||
title: "Catálogo de Clases de Activo Fijo",
|
||||
url: "/dashboard/catalogs/fixed-asset-classes",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
title: m["sidebar.reference_data.configuracion"](),
|
||||
url: "#",
|
||||
|
||||
@@ -0,0 +1,211 @@
|
||||
<script lang="ts">
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import { Input } from '$lib/components/ui/input';
|
||||
import * as Card from '$lib/components/ui/card';
|
||||
import * as Table from '$lib/components/ui/table';
|
||||
import { Search, Loader2, Download } from 'lucide-svelte';
|
||||
import { getTariffFractions, type TariffFraction } from '$lib/api/dashboard/a76/general_catalogs/tariff-fractions';
|
||||
import { companyStore } from '$lib/stores/company.svelte';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
let tariffFractions = $state<TariffFraction[]>([]);
|
||||
let filteredFractions = $state<TariffFraction[]>([]);
|
||||
let searchQuery = $state('');
|
||||
let isLoading = $state(false);
|
||||
let currentPage = $state(1);
|
||||
let totalPages = $state(1);
|
||||
let totalRecords = $state(0);
|
||||
const pageSize = 50;
|
||||
|
||||
onMount(() => {
|
||||
loadTariffFractions();
|
||||
});
|
||||
|
||||
// Filtrar fracciones cuando cambia la búsqueda
|
||||
$effect(() => {
|
||||
if (searchQuery.trim() === '') {
|
||||
filteredFractions = tariffFractions;
|
||||
} else {
|
||||
const query = searchQuery.toLowerCase();
|
||||
filteredFractions = tariffFractions.filter(
|
||||
(fraction) =>
|
||||
fraction.code.toLowerCase().includes(query) ||
|
||||
fraction.fraction.toLowerCase().includes(query) ||
|
||||
(fraction.description ?? '').toLowerCase().includes(query) ||
|
||||
(fraction.nico ?? '').toLowerCase().includes(query) ||
|
||||
(fraction.umt ?? '').toLowerCase().includes(query)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
async function loadTariffFractions(page: number = 1) {
|
||||
const companyId = companyStore.activeCompany?.id;
|
||||
if (!companyId) {
|
||||
console.error('No hay compañía activa');
|
||||
return;
|
||||
}
|
||||
|
||||
isLoading = true;
|
||||
try {
|
||||
const response = await getTariffFractions(page, pageSize, companyId);
|
||||
if (response.data) {
|
||||
tariffFractions = response.data.items;
|
||||
filteredFractions = response.data.items;
|
||||
totalPages = response.data.pages;
|
||||
totalRecords = response.data.total;
|
||||
currentPage = response.data.page;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error cargando fracciones arancelarias:', error);
|
||||
} finally {
|
||||
isLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function goToPage(page: number) {
|
||||
if (page >= 1 && page <= totalPages && page !== currentPage) {
|
||||
await loadTariffFractions(page);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="container mx-auto p-6">
|
||||
<Card.Root>
|
||||
<Card.Header class="text-center border-b">
|
||||
<Card.Title class="text-2xl font-bold uppercase">
|
||||
Catálogo de Fracciones SITAR - SCAII
|
||||
</Card.Title>
|
||||
<p class="text-muted-foreground mt-2">
|
||||
Nomenclatura arancelaria mexicana completa
|
||||
</p>
|
||||
</Card.Header>
|
||||
|
||||
<Card.Content class="pt-6">
|
||||
<div class="space-y-6">
|
||||
<!-- Barra de búsqueda y acciones -->
|
||||
<div class="flex gap-4 items-center">
|
||||
<div class="flex-1 relative">
|
||||
<Search class="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
|
||||
<Input
|
||||
bind:value={searchQuery}
|
||||
placeholder="Buscar por código, fracción, descripción, NICO o UMT..."
|
||||
class="pl-9"
|
||||
/>
|
||||
</div>
|
||||
<Button variant="outline" size="icon" title="Exportar a CSV">
|
||||
<Download class="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<!-- Información de registros -->
|
||||
<div class="flex items-center justify-between text-sm text-muted-foreground">
|
||||
<div>
|
||||
{#if isLoading}
|
||||
<div class="flex items-center gap-2">
|
||||
<Loader2 class="h-4 w-4 animate-spin" />
|
||||
<span>Cargando...</span>
|
||||
</div>
|
||||
{:else}
|
||||
Mostrando {filteredFractions.length} de {totalRecords} fracciones arancelarias
|
||||
{#if searchQuery}
|
||||
(filtrado)
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
{#if !searchQuery && totalPages > 1}
|
||||
<div class="flex items-center gap-2">
|
||||
<span>Página {currentPage} de {totalPages}</span>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<!-- Tabla de fracciones -->
|
||||
<div class="border rounded-md overflow-auto max-h-[600px]">
|
||||
<Table.Root>
|
||||
<Table.Header class="sticky top-0 bg-background z-10">
|
||||
<Table.Row>
|
||||
<Table.Head class="w-[100px]">Código</Table.Head>
|
||||
<Table.Head class="w-[120px]">Fracción</Table.Head>
|
||||
<Table.Head class="min-w-[350px]">Descripción</Table.Head>
|
||||
<Table.Head class="w-[80px]">NICO</Table.Head>
|
||||
<Table.Head class="w-[80px]">UMT</Table.Head>
|
||||
<Table.Head class="w-[100px]">Adv. Impo</Table.Head>
|
||||
<Table.Head class="w-[100px]">Adv. Expo</Table.Head>
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
{#if filteredFractions.length === 0}
|
||||
<Table.Row>
|
||||
<Table.Cell colspan={7} class="text-center py-8 text-muted-foreground">
|
||||
{#if isLoading}
|
||||
Cargando fracciones arancelarias...
|
||||
{:else if searchQuery}
|
||||
No se encontraron fracciones que coincidan con la búsqueda
|
||||
{:else}
|
||||
No hay fracciones arancelarias disponibles
|
||||
{/if}
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
{:else}
|
||||
{#each filteredFractions as fraction (fraction.id)}
|
||||
<Table.Row class="hover:bg-muted/50">
|
||||
<Table.Cell class="font-mono text-sm">{fraction.code}</Table.Cell>
|
||||
<Table.Cell class="font-mono text-sm font-medium">{fraction.fraction}</Table.Cell>
|
||||
<Table.Cell class="text-sm">
|
||||
{fraction.description || '-'}
|
||||
</Table.Cell>
|
||||
<Table.Cell class="text-sm">{fraction.nico || '-'}</Table.Cell>
|
||||
<Table.Cell class="text-sm">{fraction.umt || '-'}</Table.Cell>
|
||||
<Table.Cell class="text-sm">{fraction.adv_impo || '-'}</Table.Cell>
|
||||
<Table.Cell class="text-sm">{fraction.adv_expo || '-'}</Table.Cell>
|
||||
</Table.Row>
|
||||
{/each}
|
||||
{/if}
|
||||
</Table.Body>
|
||||
</Table.Root>
|
||||
</div>
|
||||
|
||||
<!-- Paginación -->
|
||||
{#if !searchQuery && totalPages > 1}
|
||||
<div class="flex items-center justify-center gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
disabled={currentPage === 1 || isLoading}
|
||||
onclick={() => goToPage(1)}
|
||||
>
|
||||
Primera
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
disabled={currentPage === 1 || isLoading}
|
||||
onclick={() => goToPage(currentPage - 1)}
|
||||
>
|
||||
Anterior
|
||||
</Button>
|
||||
<span class="px-4 text-sm">
|
||||
Página {currentPage} de {totalPages}
|
||||
</span>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
disabled={currentPage === totalPages || isLoading}
|
||||
onclick={() => goToPage(currentPage + 1)}
|
||||
>
|
||||
Siguiente
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
disabled={currentPage === totalPages || isLoading}
|
||||
onclick={() => goToPage(totalPages)}
|
||||
>
|
||||
Última
|
||||
</Button>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
</div>
|
||||
Reference in New Issue
Block a user