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.
This commit is contained in:
@@ -30,8 +30,6 @@
|
||||
original_date: datesData.original_date ? datesData.original_date.substring(0, 10) : '',
|
||||
start_date: datesData.start_date ? datesData.start_date.substring(0, 10) : '',
|
||||
end_date: datesData.end_date ? datesData.end_date.substring(0, 10) : '',
|
||||
capture_date: datesData.capture_date ? datesData.capture_date.substring(0, 10) : '',
|
||||
capture_time: datesData.capture_time || ''
|
||||
};
|
||||
}
|
||||
} else {
|
||||
@@ -48,7 +46,6 @@
|
||||
original_date: '',
|
||||
start_date: '',
|
||||
end_date: '',
|
||||
capture_date: '',
|
||||
capture_time: ''
|
||||
};
|
||||
}
|
||||
@@ -163,27 +160,7 @@
|
||||
type="date"
|
||||
bind:value={formData.end_date}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Fecha de Captura -->
|
||||
<div class="space-y-2">
|
||||
<Label for="capture_date">Fecha de Captura</Label>
|
||||
<Input
|
||||
id="capture_date"
|
||||
type="date"
|
||||
bind:value={formData.capture_date}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Hora de Captura -->
|
||||
<div class="space-y-2">
|
||||
<Label for="capture_time">Hora de Captura</Label>
|
||||
<Input
|
||||
id="capture_time"
|
||||
type="time"
|
||||
bind:value={formData.capture_time}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card.Content>
|
||||
|
||||
@@ -5,26 +5,175 @@
|
||||
import * as Select from '$lib/components/ui/select';
|
||||
import type { Pedimento } from '$lib/api/dashboard/a76/pedimentos';
|
||||
import type { PedimentoCode } from '$lib/api/dashboard/refrence_data/pedimento_codes';
|
||||
import type { CustomsSection } from '$lib/api/dashboard/refrence_data/customs_sections';
|
||||
import type { CustomsBroker } from '$lib/api/dashboard/a76/customs-brokers';
|
||||
import type { ClientProvider } from '$lib/api/dashboard/a76/clients-providers';
|
||||
import type { CodePedimentoRegimen } from '$lib/api/dashboard/refrence_data/code_pedimento_regimens';
|
||||
|
||||
let {
|
||||
pedimento,
|
||||
formData = $bindable(),
|
||||
pedimentoCodes = []
|
||||
pedimentoCodes = [],
|
||||
customsSections = [],
|
||||
customsBrokers = [],
|
||||
clients = [],
|
||||
codePedimentoRegimens = []
|
||||
}: {
|
||||
pedimento: Pedimento | null;
|
||||
formData?: any;
|
||||
pedimentoCodes?: PedimentoCode[];
|
||||
customsSections?: CustomsSection[];
|
||||
customsBrokers?: CustomsBroker[];
|
||||
clients?: ClientProvider[];
|
||||
codePedimentoRegimens?: CodePedimentoRegimen[];
|
||||
} = $props();
|
||||
|
||||
// Debug: verificar que los datos llegan
|
||||
$effect(() => {
|
||||
console.log('pedimentoCodes:', pedimentoCodes.length, 'items');
|
||||
// Extraer regímenes únicos de codePedimentoRegimens
|
||||
const uniqueRegimens = $derived(
|
||||
Array.from(new Set(codePedimentoRegimens.map(r => r.regimen_code).filter((code): code is string => code !== null)))
|
||||
.sort()
|
||||
.map(code => ({ code, label: code }))
|
||||
);
|
||||
|
||||
// Funciones de mapeo entre type_code (E/I) y operation_type (1/2)
|
||||
function typeCodeToOperationType(typeCode: string | null | undefined): number | null {
|
||||
if (!typeCode) return null;
|
||||
// E = Exportación = 1, I = Importación = 2
|
||||
if (typeCode.toUpperCase() === 'E') return 1;
|
||||
if (typeCode.toUpperCase() === 'I') return 2;
|
||||
return null;
|
||||
}
|
||||
|
||||
function operationTypeToTypeCode(operationType: number | null | undefined): string | null {
|
||||
if (operationType === null || operationType === undefined) return null;
|
||||
// 1 = Exportación = E, 2 = Importación = I
|
||||
if (operationType === 1) return 'E';
|
||||
if (operationType === 2) return 'I';
|
||||
return null;
|
||||
}
|
||||
|
||||
// Opciones filtradas para Régimen y Tipo de Operación basadas en las selecciones actuales
|
||||
// NOTA: La Clave NO se filtra, siempre muestra todas las opciones
|
||||
|
||||
const filteredRegimens = $derived(() => {
|
||||
// Si hay clave o tipo de operación seleccionado, filtrar
|
||||
if (formData.pedimento_code || formData.operation_type !== null) {
|
||||
const matches = codePedimentoRegimens.filter(r => {
|
||||
const matchesCode = !formData.pedimento_code || r.pedimento_code === formData.pedimento_code;
|
||||
const matchesType = formData.operation_type === null || r.type_code === operationTypeToTypeCode(formData.operation_type);
|
||||
return matchesCode && matchesType;
|
||||
});
|
||||
const validRegimens = new Set(matches.map(m => m.regimen_code).filter((code): code is string => code !== null));
|
||||
return Array.from(validRegimens).sort().map(code => ({ code, label: code }));
|
||||
}
|
||||
return uniqueRegimens;
|
||||
});
|
||||
|
||||
const filteredOperationTypes = $derived(() => {
|
||||
// Si hay clave o régimen seleccionado, filtrar
|
||||
if (formData.pedimento_code || formData.regime) {
|
||||
const matches = codePedimentoRegimens.filter(r => {
|
||||
const matchesCode = !formData.pedimento_code || r.pedimento_code === formData.pedimento_code;
|
||||
const matchesRegime = !formData.regime || r.regimen_code === formData.regime;
|
||||
return matchesCode && matchesRegime;
|
||||
});
|
||||
const validTypes = new Set(matches.map(m => typeCodeToOperationType(m.type_code)).filter(t => t !== null));
|
||||
return operationOptions.filter(opt => validTypes.has(opt.value));
|
||||
}
|
||||
return operationOptions;
|
||||
});
|
||||
|
||||
// Reactive synchronization between Clave, Régimen, and Tipo de Operación
|
||||
// REGLA: La Clave es el campo principal y NUNCA se modifica automáticamente
|
||||
// Solo se auto-llenan Régimen y Tipo de Operación basándose en la Clave
|
||||
|
||||
// Cuando cambia la Clave del Pedimento
|
||||
$effect(() => {
|
||||
const currentCode = formData.pedimento_code;
|
||||
if (!currentCode) return;
|
||||
|
||||
const matches = codePedimentoRegimens.filter(r => r.pedimento_code === currentCode);
|
||||
|
||||
if (matches.length === 0) return;
|
||||
|
||||
// Verificar si los valores actuales de régimen y tipo son válidos para esta clave
|
||||
const currentIsValid = matches.some(m => {
|
||||
const matchesRegime = !formData.regime || m.regimen_code === formData.regime;
|
||||
const matchesType = formData.operation_type === null || m.type_code === operationTypeToTypeCode(formData.operation_type);
|
||||
return matchesRegime && matchesType;
|
||||
});
|
||||
|
||||
// Si los valores actuales son válidos, NO auto-llenar
|
||||
if (currentIsValid && (formData.regime || formData.operation_type !== null)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Si solo hay un match y no hay valores válidos, auto-llenar
|
||||
if (matches.length === 1) {
|
||||
const match = matches[0];
|
||||
if (match.regimen_code && formData.regime !== match.regimen_code) {
|
||||
formData.regime = match.regimen_code;
|
||||
}
|
||||
const expectedOpType = typeCodeToOperationType(match.type_code);
|
||||
if (expectedOpType !== null && formData.operation_type !== expectedOpType) {
|
||||
formData.operation_type = expectedOpType;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Cuando cambia el Régimen
|
||||
$effect(() => {
|
||||
const currentRegime = formData.regime;
|
||||
if (!currentRegime) return;
|
||||
|
||||
const matches = codePedimentoRegimens.filter(r => r.regimen_code === currentRegime);
|
||||
|
||||
if (matches.length === 0) return;
|
||||
|
||||
// Si hay clave seleccionada, solo validar (NO auto-llenar tipo de operación)
|
||||
if (formData.pedimento_code) {
|
||||
const exactMatch = matches.find(m => m.pedimento_code === formData.pedimento_code);
|
||||
}
|
||||
// Si hay tipo de operación pero no clave, no hacer nada
|
||||
// (el usuario debe seleccionar la clave primero)
|
||||
});
|
||||
|
||||
// Cuando cambia el Tipo de Operación
|
||||
$effect(() => {
|
||||
const currentType = formData.operation_type;
|
||||
if (currentType === null || currentType === undefined) return;
|
||||
|
||||
const expectedTypeCode = operationTypeToTypeCode(currentType);
|
||||
const matches = codePedimentoRegimens.filter(r => r.type_code === expectedTypeCode);
|
||||
|
||||
if (matches.length === 0) return;
|
||||
|
||||
// Si hay clave seleccionada, actualizar régimen (forzar si no hay match exacto)
|
||||
if (formData.pedimento_code) {
|
||||
const exactMatch = matches.find(m => m.pedimento_code === formData.pedimento_code);
|
||||
if (exactMatch?.regimen_code && formData.regime !== exactMatch.regimen_code) {
|
||||
formData.regime = exactMatch.regimen_code;
|
||||
} else if (!exactMatch) {
|
||||
// No hay match exacto - buscar cualquier match con la clave actual
|
||||
const allMatchesForClave = codePedimentoRegimens.filter(r => r.pedimento_code === formData.pedimento_code);
|
||||
if (allMatchesForClave.length > 0) {
|
||||
// Forzar el régimen al primer match disponible para esta clave
|
||||
const firstMatch = allMatchesForClave[0];
|
||||
if (firstMatch.regimen_code) formData.regime = firstMatch.regimen_code;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Si hay régimen pero no clave, no hacer nada
|
||||
// (el usuario debe seleccionar la clave primero)
|
||||
});
|
||||
|
||||
// Obtener el año actual (últimos 2 dígitos)
|
||||
const currentYear = String(new Date().getFullYear()).slice(-2);
|
||||
|
||||
// Inicializar formData con los valores del pedimento (o vacío si es null)
|
||||
if (!formData) {
|
||||
formData = {
|
||||
year: pedimento?.year || '',
|
||||
year: pedimento?.year || currentYear,
|
||||
customs_office: pedimento?.customs_office || '',
|
||||
license: pedimento?.license || '',
|
||||
pedimento_number: pedimento?.pedimento_number || '',
|
||||
@@ -41,6 +190,13 @@
|
||||
};
|
||||
}
|
||||
|
||||
// Asegurar que el año siempre esté actualizado con el año actual
|
||||
$effect(() => {
|
||||
if (formData && !pedimento?.year) {
|
||||
formData.year = currentYear;
|
||||
}
|
||||
});
|
||||
|
||||
const operationOptions = [
|
||||
{ value: 1, label: 'Exportación' },
|
||||
{ value: 2, label: 'Importación' },
|
||||
@@ -85,6 +241,8 @@
|
||||
placeholder="23"
|
||||
maxlength={2}
|
||||
class="text-center"
|
||||
disabled
|
||||
readonly
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -92,15 +250,28 @@
|
||||
<div class="pb-2 text-2xl font-semibold text-muted-foreground">-</div>
|
||||
|
||||
<!-- Aduana -->
|
||||
<div class="space-y-2 w-16">
|
||||
<div class="space-y-2 w-20">
|
||||
<Label for="customs_office">Aduana</Label>
|
||||
<Input
|
||||
id="customs_office"
|
||||
bind:value={formData.customs_office}
|
||||
placeholder="01"
|
||||
maxlength={2}
|
||||
class="text-center"
|
||||
/>
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={formData.customs_office || ''}
|
||||
onValueChange={(v: string) => formData.customs_office = v ?? ''}
|
||||
>
|
||||
<Select.Trigger class="w-full">
|
||||
<span class="truncate">
|
||||
{formData.customs_office || 'Sel...'}
|
||||
</span>
|
||||
</Select.Trigger>
|
||||
<Select.Content class="max-w-[300px] max-h-[300px]">
|
||||
{#each customsSections as section}
|
||||
<Select.Item value={section.customs_code}>
|
||||
<span class="truncate overflow-hidden text-ellipsis whitespace-nowrap" title={`${section.customs_code} - ${section.section_name}`}>
|
||||
{section.customs_code} - {section.section_name}
|
||||
</span>
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
|
||||
<!-- Separador -->
|
||||
@@ -109,13 +280,26 @@
|
||||
<!-- Patente -->
|
||||
<div class="space-y-2 w-24">
|
||||
<Label for="license">Patente</Label>
|
||||
<Input
|
||||
id="license"
|
||||
bind:value={formData.license}
|
||||
placeholder="1234"
|
||||
maxlength={4}
|
||||
class="text-center"
|
||||
/>
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={formData.license || ''}
|
||||
onValueChange={(v: string) => formData.license = v ?? ''}
|
||||
>
|
||||
<Select.Trigger class="w-full">
|
||||
<span class="truncate">
|
||||
{formData.license || 'Sel...'}
|
||||
</span>
|
||||
</Select.Trigger>
|
||||
<Select.Content class="max-w-[300px] max-h-[300px]">
|
||||
{#each customsBrokers as broker}
|
||||
<Select.Item value={broker.license}>
|
||||
<span class="truncate overflow-hidden text-ellipsis whitespace-nowrap" title={`${broker.broker_key} - ${broker.name || ''}`}>
|
||||
{broker.broker_key} - {broker.name || ''}
|
||||
</span>
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
|
||||
<!-- Separador -->
|
||||
@@ -137,29 +321,24 @@
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<!-- ID del Cliente -->
|
||||
<div class="space-y-2">
|
||||
<Label for="client_id">ID del Cliente</Label>
|
||||
<Input
|
||||
id="client_id"
|
||||
type="number"
|
||||
bind:value={formData.client_id}
|
||||
placeholder="Ej: 123"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Tipo de Operación -->
|
||||
<div class="space-y-2">
|
||||
<Label for="operation_type">Tipo de Operación</Label>
|
||||
<Label for="client_id">Cliente</Label>
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={String(formData.operation_type ?? '')}
|
||||
onValueChange={(v: string) => formData.operation_type = v ? Number(v) : null}
|
||||
value={String(formData.client_id ?? '')}
|
||||
onValueChange={(v: string) => formData.client_id = v ? Number(v) : null}
|
||||
>
|
||||
<Select.Trigger class="w-full">
|
||||
{operationOptions.find(o => o.value === formData.operation_type)?.label || 'Seleccionar...'}
|
||||
<span class="truncate">
|
||||
{clients.find(c => c.id === formData.client_id)?.name || 'Seleccionar cliente...'}
|
||||
</span>
|
||||
</Select.Trigger>
|
||||
<Select.Content>
|
||||
{#each operationOptions as option}
|
||||
<Select.Item value={String(option.value)} label={option.label} />
|
||||
<Select.Content class="max-h-[300px]">
|
||||
{#each clients as client}
|
||||
<Select.Item value={String(client.id)} label={client.name}>
|
||||
<span class="truncate overflow-hidden text-ellipsis whitespace-nowrap" title={client.name}>
|
||||
{client.name}
|
||||
</span>
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
@@ -176,40 +355,81 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Clave del Pedimento -->
|
||||
<div class="space-y-2">
|
||||
<Label for="pedimento_code">Clave del Pedimento</Label>
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={formData.pedimento_code || ''}
|
||||
onValueChange={(v: string) => formData.pedimento_code = v ?? ''}
|
||||
>
|
||||
<Select.Trigger class="w-full">
|
||||
<span class="truncate">
|
||||
{pedimentoCodes.find(o => o.code === formData.pedimento_code)?.code || 'Seleccionar...'}
|
||||
</span>
|
||||
</Select.Trigger>
|
||||
<Select.Content class="max-w-[600px]">
|
||||
{#each pedimentoCodes as code}
|
||||
<Select.Item value={code.code}>
|
||||
<span class="truncate overflow-hidden text-ellipsis whitespace-nowrap" title={`${code.code} - ${code.description}`}>
|
||||
{code.code} - {code.description}
|
||||
</span>
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Régimen -->
|
||||
<div class="space-y-2">
|
||||
<Label for="regime">Régimen</Label>
|
||||
<Input
|
||||
id="regime"
|
||||
bind:value={formData.regime}
|
||||
placeholder="Ej: IMD"
|
||||
/>
|
||||
</div>
|
||||
<!-- Fila: Clave (2), Régimen (3), Tipo de Operación (11) -->
|
||||
<div class="flex items-end gap-2">
|
||||
<!-- Clave del Pedimento -->
|
||||
<div class="space-y-2 w-20">
|
||||
<Label for="pedimento_code">Clave</Label>
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={formData.pedimento_code || ''}
|
||||
onValueChange={(v: string) => formData.pedimento_code = v ?? ''}
|
||||
>
|
||||
<Select.Trigger class="w-full">
|
||||
<span class="truncate">
|
||||
{pedimentoCodes.find(o => o.code === formData.pedimento_code)?.code || 'Sel...'}
|
||||
</span>
|
||||
</Select.Trigger>
|
||||
<Select.Content class="max-w-[200px]">
|
||||
{#each pedimentoCodes as code}
|
||||
<Select.Item value={code.code}>
|
||||
<span class="truncate overflow-hidden text-ellipsis whitespace-nowrap" title={`${code.code} - ${code.description}`}>
|
||||
{code.code} - {code.description}
|
||||
</span>
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
|
||||
<!-- Régimen -->
|
||||
<div class="space-y-2 w-24">
|
||||
<Label for="regime">Régimen</Label>
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={formData.regime || ''}
|
||||
onValueChange={(v: string) => formData.regime = v ?? ''}
|
||||
>
|
||||
<Select.Trigger class="w-full">
|
||||
<span class="truncate">
|
||||
{formData.regime || 'Sel...'}
|
||||
</span>
|
||||
</Select.Trigger>
|
||||
<Select.Content class="max-w-[200px]">
|
||||
{#each filteredRegimens() as regimen}
|
||||
<Select.Item value={regimen.code}>
|
||||
<span class="truncate overflow-hidden text-ellipsis whitespace-nowrap" title={regimen.code}>
|
||||
{regimen.code}
|
||||
</span>
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
|
||||
<!-- Tipo de Operación -->
|
||||
<div class="space-y-2 flex-1">
|
||||
<Label for="operation_type">Tipo de Operación</Label>
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={String(formData.operation_type ?? '')}
|
||||
onValueChange={(v: string) => formData.operation_type = v ? Number(v) : null}
|
||||
>
|
||||
<Select.Trigger class="w-full">
|
||||
{operationOptions.find(o => o.value === formData.operation_type)?.label || 'Seleccionar...'}
|
||||
</Select.Trigger>
|
||||
<Select.Content>
|
||||
{#each filteredOperationTypes() as option}
|
||||
<Select.Item value={String(option.value)} label={option.label} />
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
|
||||
<!-- Estado -->
|
||||
<div class="space-y-2">
|
||||
|
||||
Reference in New Issue
Block a user