feat: Update Pedimento schemas and frontend components to enforce required fields and improve data handling

This commit is contained in:
Galindo97
2025-12-22 18:04:49 -06:00
parent ef1a8d743e
commit 6a06f19b1c
10 changed files with 284 additions and 143 deletions

View File

@@ -8,7 +8,7 @@ class PedimentoDatesBase(BaseModel):
"""Base schema for Pedimento Dates"""
entry_date: Optional[datetime] = Field(None, description="Entry date")
payment_date: datetime = Field(None, description="Payment date")
payment_date: datetime = Field(..., description="Payment date")
rectification_payment_date: Optional[datetime] = Field(
None, description="Rectification payment date"
)

View File

@@ -11,8 +11,8 @@ class PedimentoTransportMeansBase(BaseModel):
tenant_id: int = Field(..., description="Tenant ID")
destination: Optional[int] = Field(None, description="Destination")
entry_exit: Optional[str] = Field(None, max_length=2, description="Entry/exit")
arrival: Optional[str] = Field(None, max_length=2, description="Arrival")
departure: Optional[str] = Field(None, max_length=2, description="Departure")
arrival: str = Field(..., max_length=2, description="Arrival")
departure: str = Field(..., max_length=2, description="Departure")
class PedimentoTransportMeansCreate(BaseModel):
@@ -20,8 +20,8 @@ class PedimentoTransportMeansCreate(BaseModel):
destination: Optional[int] = Field(None, description="Destination")
entry_exit: Optional[str] = Field(None, max_length=2, description="Entry/exit")
arrival: Optional[str] = Field(None, max_length=2, description="Arrival")
departure: Optional[str] = Field(None, max_length=2, description="Departure")
arrival: str = Field(..., max_length=2, description="Arrival")
departure: str = Field(..., max_length=2, description="Departure")
class PedimentoTransportMeansUpdate(BaseModel):

View File

@@ -33,7 +33,7 @@ class PedimentosBase(BaseModel):
year: Optional[str] = Field(None, max_length=2, description="Year")
customs_office: Optional[str] = Field(
None, max_length=2, description="Customs office"
None, max_length=3, description="Customs office"
)
license: Optional[str] = Field(None, max_length=4, description="License")
pedimento_number: Optional[str] = Field(
@@ -41,11 +41,11 @@ class PedimentosBase(BaseModel):
)
client_id: Optional[int] = Field(None, description="Client ID")
operation_type: Optional[int] = Field(None, description="Operation type")
pedimento_type: Optional[int] = Field(None, description="Pedimento type")
pedimento_code: Optional[str] = Field(
None, max_length=2, description="Pedimento key"
pedimento_type: Optional[str] = Field(None, max_length=20, description="Pedimento type")
pedimento_code: str = Field(
..., max_length=2, description="Pedimento key"
)
regime: Optional[str] = Field(None, max_length=3, description="Regime")
regime: str = Field(..., max_length=3, description="Regime")
status: Optional[str] = Field(None, max_length=30, description="Status")
usd_value: Optional[Decimal] = Field(None, description="USD value")
paid_price: Optional[Decimal] = Field(None, description="Paid price")
@@ -57,14 +57,17 @@ class PedimentosCreate(PedimentosBase):
# Override to make required fields non-optional
year: str = Field(..., max_length=2, description="Year")
customs_office: str = Field(..., max_length=2, description="Customs office")
customs_office: str = Field(..., max_length=3, description="Customs office")
license: str = Field(..., max_length=4, description="License")
pedimento_number: str = Field(..., max_length=7, description="Pedimento number")
client_id: int = Field(..., description="Client ID")
operation_type: int = Field(..., description="Operation type")
pedimento_type: int = Field(..., description="Pedimento type")
pedimento_type: str = Field(..., max_length=20, description="Pedimento type")
pedimento_code: str = Field(
..., max_length=2, description="Pedimento key"
)
regime: str = Field(..., max_length=3, description="Regime")
status: str = Field(..., max_length=30, description="Status")
status: str = Field(..., max_length=30, description="Status")
pedimento_dates: Optional[PedimentoDatesCreate] = None
pedimento_decrementables: Optional[PedimentoDecrementablesCreate] = None
@@ -87,12 +90,12 @@ class PedimentosUpdate(BaseModel):
"""Schema for updating a Pedimento"""
year: Optional[str] = Field(None, max_length=2)
customs_office: Optional[str] = Field(None, max_length=2)
customs_office: Optional[str] = Field(None, max_length=3)
license: Optional[str] = Field(None, max_length=4)
pedimento_number: Optional[str] = Field(None, max_length=7)
client_id: Optional[int] = None
operation_type: Optional[int] = None
pedimento_type: Optional[int] = None
pedimento_type: Optional[str] = Field(None, max_length=20)
pedimento_code: Optional[str] = Field(None, max_length=2)
regime: Optional[str] = Field(None, max_length=3)
status: Optional[str] = Field(None, max_length=30)

View File

@@ -94,12 +94,12 @@ class Pedimentos(Base, TenantScopedMixin, TimestampMixin):
id: Mapped[int] = mapped_column(Integer)
year: Mapped[str] = mapped_column(String(2))
customs_office: Mapped[str] = mapped_column(String(2))
customs_office: Mapped[str] = mapped_column(String(3))
license: Mapped[str] = mapped_column(String(4))
pedimento_number: Mapped[str] = mapped_column(String(7))
client_id: Mapped[int] = mapped_column(Integer)
operation_type: Mapped[int] = mapped_column(Integer)
pedimento_type: Mapped[int] = mapped_column(Integer)
pedimento_type: Mapped[str] = mapped_column(String(20))
pedimento_code: Mapped[str] = mapped_column(String(2))
regime: Mapped[str] = mapped_column(String(3))
status: Mapped[str] = mapped_column(String(30))

View File

@@ -0,0 +1,44 @@
/**
* API Client para Exchange Rate
*/
import { api } from '$lib/api';
export interface ExchangeRate {
id: number;
date: string;
value: number;
local_currency: string | null;
foreign_currency: string | null;
company_id: number;
tenant_id: number;
}
export interface ExchangeRateListResponse {
items: ExchangeRate[];
total: number;
page: number;
size: number;
pages: number;
}
/**
* Get exchange rate by date
*/
export async function getExchangeRateByDate(date: string): Promise<ExchangeRate | null> {
try {
// Convert date to ISO format with time for backend datetime field
const dateWithTime = `${date}T00:00:00`;
const response = await api.get<ExchangeRateListResponse>(`/v1/a76/exchange-rate?date=${dateWithTime}`);
if (response.data && response.data.items && response.data.items.length > 0) {
// Find USD exchange rate
const usdRate = response.data.items.find(rate => rate.foreign_currency === 'USD');
return usdRate || null;
}
return null;
} catch (error) {
console.error('Error fetching exchange rate:', error);
return null;
}
}

View File

@@ -59,7 +59,7 @@ export interface Pedimento {
pedimento_number?: string | null;
client_id?: number | null;
operation_type?: number | null;
pedimento_type?: number | null;
pedimento_type?: string | null;
pedimento_code?: string | null;
regime?: string | null;
status?: string | null;
@@ -89,7 +89,7 @@ export interface CreatePedimentoData {
pedimento_number?: string | null;
client_id?: number | null;
operation_type?: number | null;
pedimento_type?: number | null;
pedimento_type?: string | null;
pedimento_code?: string | null;
regime?: string | null;
status?: string | null;
@@ -111,7 +111,7 @@ export interface UpdatePedimentoData {
pedimento_number?: string | null;
client_id?: number | null;
operation_type?: number | null;
pedimento_type?: number | null;
pedimento_type?: string | null;
pedimento_code?: string | null;
regime?: string | null;
status?: string | null;

View File

@@ -3,6 +3,7 @@
import { Input } from '$lib/components/ui/input';
import { Label } from '$lib/components/ui/label';
import * as Select from '$lib/components/ui/select';
import { getExchangeRateByDate } from '$lib/api/dashboard/a76/exchange-rate';
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';
@@ -52,77 +53,96 @@
return null;
}
// Opciones constantes
const operationOptions = [
{ value: 1, label: 'Exportación' },
{ value: 2, label: 'Importación' },
];
// 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(() => {
const filteredRegimens = $derived.by(() => {
if (!formData) return uniqueRegimens;
// Si hay clave o tipo de operación seleccionado, filtrar
if (formData.pedimento_code || formData.operation_type !== null) {
if (formData.pedimento_code || formData.operation_type !== null && formData.operation_type !== undefined) {
const expectedTypeCode = operationTypeToTypeCode(formData.operation_type);
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);
const matchesType = !expectedTypeCode || r.type_code === expectedTypeCode;
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;
});
// Determinar si el régimen debe ser seleccionable o readonly
const hasMultipleRegimens = $derived.by(() => {
if (!formData) return false;
return filteredRegimens.length > 1;
});
const filteredOperationTypes = $derived.by(() => {
if (!formData) return operationOptions;
// Solo filtrar por la clave del pedimento (no por régimen)
// Esto permite cambiar entre Exportación e Importación libremente
if (formData.pedimento_code) {
const matches = codePedimentoRegimens.filter(r => r.pedimento_code === formData.pedimento_code);
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;
});
// Track previous values to detect changes
let previousPedimentoCode = $state(formData?.pedimento_code || '');
let previousOperationType = $state(formData?.operation_type ?? null);
// 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(() => {
if (!formData) return;
const currentCode = formData.pedimento_code;
// Detectar si la clave cambió
const codeChanged = currentCode !== previousPedimentoCode;
// Solo procesar si la clave cambió
if (!codeChanged) return;
// Actualizar el tracking
previousPedimentoCode = currentCode;
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;
// Auto-llenar con el primer match
const firstMatch = matches[0];
if (firstMatch.regimen_code) {
formData.regime = firstMatch.regimen_code;
}
// 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;
}
const expectedOpType = typeCodeToOperationType(firstMatch.type_code);
if (expectedOpType !== null) {
formData.operation_type = expectedOpType;
}
});
// Cuando cambia el Régimen
$effect(() => {
if (!formData) return;
const currentRegime = formData.regime;
if (!currentRegime) return;
@@ -140,31 +160,40 @@
// Cuando cambia el Tipo de Operación
$effect(() => {
if (!formData) return;
const currentType = formData.operation_type;
// Detectar si el tipo de operación cambió
const typeChanged = currentType !== previousOperationType;
previousOperationType = currentType;
// Si no hay clave seleccionada, no hacer nada
if (!formData.pedimento_code) return;
// Si el tipo es null/undefined, no hacer nada
if (currentType === null || currentType === undefined) return;
const expectedTypeCode = operationTypeToTypeCode(currentType);
const matches = codePedimentoRegimens.filter(r => r.type_code === expectedTypeCode);
// Buscar matches para esta combinación de clave + tipo de operación
const matches = codePedimentoRegimens.filter(r =>
r.pedimento_code === formData.pedimento_code &&
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;
}
// Obtener regímenes válidos para esta combinación
const validRegimens = new Set(matches.map(m => m.regimen_code).filter((code): code is string => code !== null));
// Si el tipo de operación cambió O el régimen actual no es válido, actualizar el régimen
if (typeChanged || !formData.regime || !validRegimens.has(formData.regime)) {
const firstMatch = matches[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)
@@ -180,7 +209,7 @@
pedimento_number: pedimento?.pedimento_number || '',
client_id: pedimento?.client_id ?? null,
operation_type: pedimento?.operation_type ?? null,
pedimento_type: pedimento?.pedimento_type ?? null,
pedimento_type: pedimento?.pedimento_type || '',
pedimento_code: pedimento?.pedimento_code || '',
regime: pedimento?.regime || '',
status: pedimento?.status || '',
@@ -205,10 +234,24 @@
}
});
const operationOptions = [
{ value: 1, label: 'Exportación' },
{ value: 2, label: 'Importación' },
];
// Obtener automáticamente el tipo de cambio cuando cambie la fecha de entrada
$effect(() => {
if (formData && formData.entry_date) {
console.log('Buscando tipo de cambio para fecha:', formData.entry_date);
getExchangeRateByDate(formData.entry_date)
.then(usdRate => {
console.log('Tipo de cambio USD encontrado:', usdRate);
if (usdRate && formData) {
formData.exchange_rate = usdRate.value;
console.log('Tipo de cambio actualizado a:', usdRate.value);
} else {
console.warn('No se encontró tipo de cambio USD para la fecha:', formData.entry_date);
}
})
.catch(err => console.error('Error al obtener tipo de cambio:', err));
}
});
const statusOptions = [
{ value: 'MODIFICABLE', label: 'Modificable' },
@@ -227,6 +270,13 @@
{ value: 'CARTA CUPO', label: 'Carta cupo' },
{ value: 'ESPERA CANCELAR CARTA CUPO', label: 'Espera cancelar carta cupo' }
];
const pedimentoTypeOptions = [
{ value: 'automovil', label: 'Automóvil' },
{ value: 'complementario', label: 'Complementario' },
{ value: 'consolidado', label: 'Consolidado' },
{ value: 'normal', label: 'Normal' }
];
</script>
<Card.Root>
@@ -241,7 +291,7 @@
<div class="grid grid-cols-1 md:grid-cols-[auto_auto_auto_auto_auto_auto_auto_auto_auto_1fr_auto] md:items-end gap-4 md:gap-2">
<!-- Año -->
<div class="space-y-2">
<Label for="year">Año</Label>
<Label for="year">Año <span class="text-red-500">*</span></Label>
<Input
id="year"
bind:value={formData.year}
@@ -258,7 +308,7 @@
<!-- Aduana -->
<div class="space-y-2">
<Label for="customs_office">Aduana</Label>
<Label for="customs_office">Aduana <span class="text-red-500">*</span></Label>
<Select.Root
type="single"
value={formData.customs_office || ''}
@@ -266,17 +316,23 @@
>
<Select.Trigger class="w-full md:w-20">
<span class="truncate">
{formData.customs_office || 'Sel...'}
{formData.customs_office ? formData.customs_office.substring(0, 2) : '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}
{#if customsSections.length === 0}
<div class="px-2 py-1.5 text-sm text-muted-foreground">
No hay aduanas disponibles
</div>
{:else}
{#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}
{/if}
</Select.Content>
</Select.Root>
</div>
@@ -286,7 +342,7 @@
<!-- Patente -->
<div class="space-y-2">
<Label for="license">Patente</Label>
<Label for="license">Patente <span class="text-red-500">*</span></Label>
<Select.Root
type="single"
value={formData.license || ''}
@@ -298,13 +354,20 @@
</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}
{#if customsBrokers.length === 0}
<div class="px-2 py-1.5 text-sm text-muted-foreground">
No hay patentes disponibles
</div>
{:else}
{#each customsBrokers as broker}
{@const displayName = broker.name || broker.broker_key || ''}
<Select.Item value={broker.license}>
<span class="truncate overflow-hidden text-ellipsis whitespace-nowrap" title={displayName ? `${displayName} - ${broker.license}` : broker.license}>
{displayName ? `${displayName} - ${broker.license}` : broker.license}
</span>
</Select.Item>
{/each}
{/if}
</Select.Content>
</Select.Root>
</div>
@@ -314,7 +377,7 @@
<!-- Número de Pedimento -->
<div class="space-y-2">
<Label for="pedimento_number">Número de Pedimento</Label>
<Label for="pedimento_number">Número de Pedimento <span class="text-red-500">*</span></Label>
<Input
id="pedimento_number"
bind:value={formData.pedimento_number}
@@ -326,13 +389,16 @@
<!-- Tipo de Cambio -->
<div class="space-y-2">
<Label for="exchange_rate">Tipo de Cambio</Label>
<Label for="exchange_rate">Tipo de Cambio <span class="text-red-500">*</span></Label>
<Input
id="exchange_rate"
type="number"
step="0.0001"
bind:value={formData.exchange_rate}
placeholder="Ej: 17.5000"
placeholder="En fecha de entrada"
readonly
disabled
class="bg-muted cursor-not-allowed"
/>
</div>
</div>
@@ -340,7 +406,7 @@
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 md:gap-2">
<!-- Clave del Pedimento -->
<div class="space-y-2">
<Label for="pedimento_code">Clave</Label>
<Label for="pedimento_code">Clave <span class="text-red-500">*</span></Label>
<Select.Root
type="single"
value={formData.pedimento_code || ''}
@@ -351,37 +417,14 @@
{pedimentoCodes.find(o => o.code === formData.pedimento_code)?.code || 'Sel...'}
</span>
</Select.Trigger>
<Select.Content class="max-w-[200px]">
<Select.Content class="max-w-[400px] max-h-[300px]">
{#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">
<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>
<div class="flex items-center gap-2">
<span class="font-medium">{code.code}</span>
<span class="text-muted-foreground">-</span>
<span class="flex-1 truncate">{code.description}</span>
</div>
</Select.Item>
{/each}
</Select.Content>
@@ -400,18 +443,54 @@
{operationOptions.find(o => o.value === formData.operation_type)?.label || 'Seleccionar...'}
</Select.Trigger>
<Select.Content>
{#each filteredOperationTypes() as option}
{#each filteredOperationTypes as option}
<Select.Item value={String(option.value)} label={option.label} />
{/each}
</Select.Content>
</Select.Root>
</div>
<!-- Régimen -->
<div class="space-y-2">
<Label for="regime">Régimen <span class="text-red-500">*</span></Label>
{#if hasMultipleRegimens}
<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>
{:else}
<Input
id="regime"
bind:value={formData.regime}
placeholder="Automático"
class="text-left"
readonly
disabled
/>
{/if}
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<!-- ID del Cliente -->
<div class="space-y-2">
<Label for="client_id">Cliente</Label>
<Label for="client_id">Cliente <span class="text-red-500">*</span></Label>
<Select.Root
type="single"
value={String(formData.client_id ?? '')}
@@ -423,26 +502,40 @@
</span>
</Select.Trigger>
<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}
{#if clients.length === 0}
<div class="px-2 py-1.5 text-sm text-muted-foreground">
No hay clientes disponibles
</div>
{:else}
{#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}
{/if}
</Select.Content>
</Select.Root>
</div>
<!-- Tipo de Pedimento -->
<div class="space-y-2">
<Label for="pedimento_type">Tipo de Pedimento</Label>
<Input
id="pedimento_type"
type="number"
bind:value={formData.pedimento_type}
placeholder="Ej: 1"
/>
<Label for="pedimento_type">Tipo de Pedimento <span class="text-red-500">*</span></Label>
<Select.Root
type="single"
value={formData.pedimento_type || ''}
onValueChange={(v: string) => formData.pedimento_type = v ?? ''}
>
<Select.Trigger class="w-full">
{pedimentoTypeOptions.find(o => o.value === formData.pedimento_type)?.label || 'Seleccionar...'}
</Select.Trigger>
<Select.Content>
{#each pedimentoTypeOptions as option}
<Select.Item value={option.value} label={option.label} />
{/each}
</Select.Content>
</Select.Root>
</div>
</div>
@@ -451,7 +544,7 @@
<!-- Estado -->
<div class="space-y-2">
<Label for="status">Estado</Label>
<Label for="status">Estado <span class="text-red-500">*</span></Label>
<Select.Root
type="single"
value={formData.status || ''}

View File

@@ -27,7 +27,7 @@
? {
name: userData.name || userData.preferred_username || "",
email: userData.email || "",
avatar: "/avatars/default.jpg", // Puedes agregar avatar desde Keycloak si está disponible
// avatar: "/avatars/default.jpg", // Puedes agregar avatar desde Keycloak si está disponible
}
: sidebarData.user,
});

View File

@@ -56,7 +56,7 @@ export function getSidebarData(): SidebarData {
user: {
name: "", // Se llena dinámicamente desde Keycloak
email: "", // Se llena dinámicamente desde Keycloak
avatar: "/avatars/default.jpg", // Avatar por defecto
// avatar: "/avatars/default.jpg", // Avatar por defecto
},
teams: [
{

View File

@@ -109,6 +109,7 @@
client_id: 'ID del Cliente',
operation_type: 'Tipo de Operación',
pedimento_type: 'Tipo de Pedimento',
pedimento_code: 'Clave',
regime: 'Régimen',
status: 'Estado'
};