Merge pull request 'fix/pedimentos-upgrades' (#131) from fix/pedimentos-upgrades into development

Reviewed-on: ADUANASOFT/anexo76#131
This commit is contained in:
2026-02-11 23:45:55 +00:00
9 changed files with 406 additions and 91 deletions

View File

@@ -1,37 +1,46 @@
import { Select as SelectPrimitive } from "bits-ui";
import Group from "./select-group.svelte";
import Label from "./select-label.svelte";
import Item from "./select-item.svelte";
import Content from "./select-content.svelte";
import Item from "./select-item.svelte";
import Label from "./select-label.svelte";
import Root from "./select-root.svelte";
import Trigger from "./select-trigger.svelte";
import Separator from "./select-separator.svelte";
import ScrollDownButton from "./select-scroll-down-button.svelte";
import ScrollUpButton from "./select-scroll-up-button.svelte";
import ScrollDownButton from "./select-scroll-down-button.svelte";
import GroupHeading from "./select-group-heading.svelte";
import SearchableSelect from "./select-searchable.svelte";
const Root = SelectPrimitive.Root;
const Group = SelectPrimitive.Group;
const Input = SelectPrimitive.Input;
const Value = SelectPrimitive.Value;
export {
Root,
Group,
Input,
Label,
Item,
Value,
Content,
Trigger,
Separator,
ScrollDownButton,
ScrollUpButton,
ScrollDownButton,
GroupHeading,
SearchableSelect,
//
Root as Select,
Group as SelectGroup,
Input as SelectInput,
Label as SelectLabel,
Item as SelectItem,
Value as SelectValue,
Content as SelectContent,
Trigger as SelectTrigger,
Separator as SelectSeparator,
ScrollDownButton as SelectScrollDownButton,
ScrollUpButton as SelectScrollUpButton,
ScrollDownButton as SelectScrollDownButton,
GroupHeading as SelectGroupHeading,
SearchableSelect as SelectSearchable,
};

View File

@@ -3,6 +3,8 @@
import SelectScrollUpButton from "./select-scroll-up-button.svelte";
import SelectScrollDownButton from "./select-scroll-down-button.svelte";
import { cn, type WithoutChild } from "$lib/utils.js";
import { getContext } from "svelte";
import { selectSearchContextKey, type SelectSearchContext } from "./select-search-context";
let {
ref = $bindable(null),
@@ -10,10 +12,56 @@
sideOffset = 4,
portalProps,
children,
searchable = true,
searchPlaceholder = "Buscar...",
autoFocusSearch = true,
...restProps
}: WithoutChild<SelectPrimitive.ContentProps> & {
portalProps?: SelectPrimitive.PortalProps;
searchable?: boolean;
searchPlaceholder?: string;
autoFocusSearch?: boolean;
} = $props();
const searchContext = getContext<SelectSearchContext | undefined>(selectSearchContextKey);
let searchQuery = $state('');
let isOpen = $state(false);
let searchInputRef = $state<HTMLInputElement | null>(null);
$effect(() => {
if (!searchContext) return;
const unsubscribe = searchContext.query.subscribe((value) => {
searchQuery = value;
});
return unsubscribe;
});
$effect(() => {
if (!searchContext) return;
const unsubscribe = searchContext.open.subscribe((value) => {
isOpen = value;
});
return unsubscribe;
});
$effect(() => {
if (!searchable || !autoFocusSearch || !isOpen || !searchInputRef) return;
searchInputRef.focus();
searchInputRef.select();
});
function updateQuery(next: string) {
searchQuery = next;
searchContext?.query.set(next);
}
function handleContentKeydown(event: KeyboardEvent) {
if (!searchable || !searchInputRef) return;
if (event.ctrlKey || event.metaKey || event.altKey) return;
if (event.key.length === 1) {
searchInputRef.focus();
}
}
</script>
<SelectPrimitive.Portal {...portalProps}>
@@ -21,6 +69,7 @@
bind:ref
{sideOffset}
data-slot="select-content"
onkeydown={handleContentKeydown}
class={cn(
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 max-h-(--bits-select-content-available-height) origin-(--bits-select-content-transform-origin) relative z-50 min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-md border shadow-md data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
className
@@ -28,6 +77,23 @@
{...restProps}
>
<SelectScrollUpButton />
{#if searchable}
<div class="border-input sticky top-0 z-10 bg-popover px-2 py-1.5">
<input
type="text"
bind:this={searchInputRef}
value={searchQuery}
placeholder={searchPlaceholder}
autofocus={autoFocusSearch}
class="placeholder:text-muted-foreground flex h-8 w-full rounded-md border border-input bg-background px-3 py-1 text-sm outline-none focus:border-ring focus:ring-1 focus:ring-ring"
oninput={(event) => updateQuery((event.currentTarget as HTMLInputElement).value)}
onclick={(e) => e.stopPropagation()}
onkeydown={(e) => e.stopPropagation()}
/>
</div>
{/if}
<SelectPrimitive.Viewport
class={cn(
"h-(--bits-select-anchor-height) min-w-(--bits-select-anchor-width) w-full scroll-my-1 p-1"

View File

@@ -2,39 +2,66 @@
import CheckIcon from "@lucide/svelte/icons/check";
import { Select as SelectPrimitive } from "bits-ui";
import { cn, type WithoutChild } from "$lib/utils.js";
import { getContext } from "svelte";
import { selectSearchContextKey, type SelectSearchContext } from "./select-search-context";
let {
ref = $bindable(null),
class: className,
value,
label,
searchText,
children: childrenProp,
...restProps
}: WithoutChild<SelectPrimitive.ItemProps> = $props();
}: WithoutChild<SelectPrimitive.ItemProps> & { searchText?: string } = $props();
// Get search context
const searchContext = getContext<SelectSearchContext | undefined>(selectSearchContextKey);
let query = $state('');
$effect(() => {
if (!searchContext) return;
const unsubscribe = searchContext.query.subscribe((value) => {
query = value;
});
return unsubscribe;
});
// Determine if item should be visible based on search
const isVisible = $derived(() => {
if (!searchContext) return true;
const normalizedQuery = query.toLowerCase();
if (!normalizedQuery) return true;
const itemLabel = (searchText || label || value || '').toString().toLowerCase();
return itemLabel.includes(normalizedQuery);
});
</script>
<SelectPrimitive.Item
bind:ref
{value}
data-slot="select-item"
tabindex="0"
class={cn(
"data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground outline-hidden *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2 relative flex w-full cursor-default select-none items-center gap-2 rounded-sm py-1.5 pl-2 pr-8 text-sm data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
"focus-visible:bg-accent focus-visible:text-accent-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
className
)}
{...restProps}
>
{#snippet children({ selected, highlighted })}
<span class="absolute right-2 flex size-3.5 items-center justify-center">
{#if selected}
<CheckIcon class="size-4" />
{#if isVisible()}
<SelectPrimitive.Item
bind:ref
{value}
data-slot="select-item"
tabindex="0"
class={cn(
"data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground outline-hidden *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2 relative flex w-full cursor-default select-none items-center gap-2 rounded-sm py-1.5 pl-2 pr-8 text-sm data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
"focus-visible:bg-accent focus-visible:text-accent-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
className
)}
{...restProps}
>
{#snippet children({ selected, highlighted })}
<span class="absolute right-2 flex size-3.5 items-center justify-center">
{#if selected}
<CheckIcon class="size-4" />
{/if}
</span>
{#if childrenProp}
{@render childrenProp({ selected, highlighted })}
{:else}
{label || value}
{/if}
</span>
{#if childrenProp}
{@render childrenProp({ selected, highlighted })}
{:else}
{label || value}
{/if}
{/snippet}
</SelectPrimitive.Item>
{/snippet}
</SelectPrimitive.Item>
{/if}

View File

@@ -0,0 +1,33 @@
<script lang="ts">
import { Select as SelectPrimitive } from "bits-ui";
import { writable } from "svelte/store";
import { setContext } from "svelte";
import { selectSearchContextKey, type SelectSearchContext } from "./select-search-context";
import { type WithoutChild } from "$lib/utils.js";
let { children, ...restProps }: WithoutChild<SelectPrimitive.RootProps> = $props();
let open = $state(false);
const query = writable("");
const openStore = writable(false);
const context: SelectSearchContext = {
query,
open: openStore,
setOpen: (next) => {
open = next;
}
};
setContext(selectSearchContextKey, context);
$effect(() => {
openStore.set(open);
if (!open) {
query.set("");
}
});
</script>
<SelectPrimitive.Root bind:open {...restProps}>
{@render children?.()}
</SelectPrimitive.Root>

View File

@@ -0,0 +1,9 @@
import type { Writable } from "svelte/store";
export const selectSearchContextKey = Symbol("select-search");
export type SelectSearchContext = {
query: Writable<string>;
open: Writable<boolean>;
setOpen: (next: boolean) => void;
};

View File

@@ -0,0 +1,157 @@
<script lang="ts">
import { Combobox as ComboboxPrimitive } from 'bits-ui';
import { ChevronDown, X } from 'lucide-svelte';
import { cn } from '$lib/utils.js';
interface Item {
value: string;
label: string;
}
let {
items = [],
value = $bindable(''),
onValueChange,
placeholder = 'Seleccionar...',
searchPlaceholder = 'Buscar...',
emptyMessage = 'No se encontraron resultados',
class: className = '',
disabled = false
}: {
items: Item[];
value?: string;
onValueChange?: (value: string) => void;
placeholder?: string;
searchPlaceholder?: string;
emptyMessage?: string;
class?: string;
disabled?: boolean;
} = $props();
let searchQuery = $state('');
let open = $state(false);
let isTyping = $state(false);
let containerWidth = $state(0);
// Get selected item label
const selectedLabel = $derived((items ?? []).find((item) => item.value === value)?.label || '');
// Filter items based on search query
// Show all items unless the user is explicitly typing
const filteredItems = $derived(
!isTyping || searchQuery.trim() === ''
? (items ?? [])
: (items ?? []).filter(
(item) =>
(item.label ?? '').toLowerCase().includes(searchQuery.toLowerCase()) ||
(item.value ?? '').toLowerCase().includes(searchQuery.toLowerCase())
)
);
function handleValueChange(v: string) {
value = v;
onValueChange?.(v);
open = false;
isTyping = false;
}
function handleClear(e: Event) {
e.stopPropagation();
value = '';
onValueChange?.('');
searchQuery = '';
isTyping = false;
}
function handleInput() {
isTyping = true;
}
function handleFocus() {
if (!open) {
open = true;
isTyping = false; // Show all items on initial focus
}
}
// Sincronizar el input con el valor seleccionado cuando se cierra
$effect(() => {
if (!open) {
searchQuery = selectedLabel;
isTyping = false;
}
});
// Sincronización inicial o cambio externo
$effect(() => {
if (!open && value) {
searchQuery = selectedLabel;
}
});
</script>
<ComboboxPrimitive.Root bind:open {disabled} onValueChange={handleValueChange}>
<div class="relative {className}" bind:clientWidth={containerWidth}>
<div class="relative">
<ComboboxPrimitive.Input
bind:value={searchQuery}
{placeholder}
aria-label={placeholder}
class={cn(
'border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 shadow-xs flex h-9 w-full items-center rounded-md border bg-transparent px-3 py-2 pr-24 text-sm outline-none transition-[color,box-shadow] focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50',
!value && !searchQuery && 'text-muted-foreground'
)}
oninput={handleInput}
onfocus={handleFocus}
/>
<div class="absolute right-2 top-1/2 -translate-y-1/2 flex items-center gap-1">
{#if value && !disabled}
<button
type="button"
tabindex="-1"
onclick={handleClear}
aria-label="Limpiar selección"
class="hover:bg-accent focus-visible:bg-accent rounded-sm p-1 transition-colors outline-none"
>
<X class="size-4" />
</button>
{/if}
<ComboboxPrimitive.Trigger
tabindex="-1"
aria-label="Abrir opciones"
class="hover:bg-accent focus-visible:bg-accent rounded-sm p-1 transition-colors outline-none"
>
<ChevronDown class="size-4 opacity-50" />
</ComboboxPrimitive.Trigger>
</div>
</div>
<ComboboxPrimitive.Portal>
<ComboboxPrimitive.Content
class={cn(
'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-[300px] overflow-y-auto rounded-md border p-1 shadow-md outline-none'
)}
style="width: {containerWidth > 0 ? containerWidth + 'px' : '300px'}"
sideOffset={4}
>
{#if filteredItems.length === 0}
<div class="text-muted-foreground py-6 text-center text-sm italic">
{emptyMessage}
</div>
{:else}
{#each filteredItems as item (item.value)}
<ComboboxPrimitive.Item
value={item.value}
label={item.label}
class={cn(
'data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground relative flex cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50'
)}
>
<span class="flex-1 truncate">{item.label}</span>
</ComboboxPrimitive.Item>
{/each}
{/if}
</ComboboxPrimitive.Content>
</ComboboxPrimitive.Portal>
</div>
</ComboboxPrimitive.Root>

View File

@@ -2,6 +2,8 @@
import { Select as SelectPrimitive } from "bits-ui";
import ChevronDownIcon from "@lucide/svelte/icons/chevron-down";
import { cn, type WithoutChild } from "$lib/utils.js";
import { getContext } from "svelte";
import { selectSearchContextKey, type SelectSearchContext } from "./select-search-context";
let {
ref = $bindable(null),
@@ -12,12 +14,25 @@
}: WithoutChild<SelectPrimitive.TriggerProps> & {
size?: "sm" | "default";
} = $props();
const searchContext = getContext<SelectSearchContext | undefined>(selectSearchContextKey);
function handleTriggerKeydown(event: KeyboardEvent) {
if (!searchContext) return;
if (event.ctrlKey || event.metaKey || event.altKey) return;
if (event.key.length === 1) {
searchContext.setOpen(true);
searchContext.query.update((value) => value + event.key);
event.preventDefault();
}
}
</script>
<SelectPrimitive.Trigger
bind:ref
data-slot="select-trigger"
data-size={size}
onkeydown={handleTriggerKeydown}
class={cn(
"border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 shadow-xs flex w-fit select-none items-center justify-between gap-2 whitespace-nowrap rounded-md border bg-transparent px-3 py-2 text-sm outline-none transition-[color,box-shadow] focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
className

View File

@@ -58,30 +58,46 @@ export const load: PageServerLoad = async ({ params, cookies, fetch }) => {
// Si el ID es "new", es una creación
if (params.id === 'new') {
const [pedimentoCodesResponse, customsSectionsResponse, customsBrokersResponse, clientsResponse, codePedimentoRegimensResponse] = await Promise.all([
pedimentoCodesPromise,
customsSectionsPromise,
customsBrokersPromise,
clientsPromise,
codePedimentoRegimensPromise
]);
try {
const [pedimentoCodesResponse, customsSectionsResponse, customsBrokersResponse, clientsResponse, codePedimentoRegimensResponse] = await Promise.all([
pedimentoCodesPromise,
customsSectionsPromise,
customsBrokersPromise,
clientsPromise,
codePedimentoRegimensPromise
]);
const pedimentoCodes = pedimentoCodesResponse.ok ? await pedimentoCodesResponse.json() : { items: [] };
const customsSections = customsSectionsResponse.ok ? await customsSectionsResponse.json() : { items: [] };
const customsBrokers = customsBrokersResponse.ok ? await customsBrokersResponse.json() : { items: [] };
const clients = clientsResponse.ok ? await clientsResponse.json() : { items: [] };
const codePedimentoRegimens = codePedimentoRegimensResponse.ok ? await codePedimentoRegimensResponse.json() : { items: [] };
const pedimentoCodes = pedimentoCodesResponse.ok ? await pedimentoCodesResponse.json() : { items: [] };
const customsSections = customsSectionsResponse.ok ? await customsSectionsResponse.json() : { items: [] };
const customsBrokers = customsBrokersResponse.ok ? await customsBrokersResponse.json() : { items: [] };
const clients = clientsResponse.ok ? await clientsResponse.json() : { items: [] };
const codePedimentoRegimens = codePedimentoRegimensResponse.ok ? await codePedimentoRegimensResponse.json() : { items: [] };
return {
pedimento: null,
pedimentoId: null,
isCreate: true,
pedimentoCodes: pedimentoCodes.items || [],
customsSections: customsSections.items || [],
customsBrokers: customsBrokers.items || [],
clients: clients.items || [],
codePedimentoRegimens: codePedimentoRegimens.items || []
};
return {
pedimento: null,
pedimentoId: null,
isCreate: true,
pedimentoCodes: pedimentoCodes.items || [],
customsSections: customsSections.items || [],
customsBrokers: customsBrokers.items || [],
clients: clients.items || [],
codePedimentoRegimens: codePedimentoRegimens.items || []
};
} catch (e) {
console.error('❌ Error loading new pedimento data:', e);
// Graceful degradation: return empty data so the page loads, but show error
return {
pedimento: null,
pedimentoId: null,
isCreate: true,
pedimentoCodes: [],
customsSections: [],
customsBrokers: [],
clients: [],
codePedimentoRegimens: [],
error: 'Error al cargar catálogos. Verifique la conexión con el backend.'
};
}
}
const pedimentoId = parseInt(params.id);

View File

@@ -1,8 +1,8 @@
<script lang="ts">
import { goto, invalidateAll } from '$app/navigation';
import * as Tabs from '$lib/components/ui/tabs';
import * as Alert from '$lib/components/ui/alert';
import { Button } from '$lib/components/ui/button';
import { toast } from 'svelte-sonner';
import { Badge } from '$lib/components/ui/badge';
import { Separator } from '$lib/components/ui/separator';
import {
@@ -88,8 +88,6 @@
});
let generalTabInstance = $state<any>(null); // Todavía útil para otras cosas
let saving = $state(false);
let error = $state<string | null>(null);
let success = $state(false);
useShortcuts(
'Pedimento Edit Main Tabs',
@@ -127,6 +125,8 @@
// ID del pedimento
let pedimentoId = $state<number | null>(data.pedimentoId ?? null);
const pedimento = data.pedimento;
// Referencias a los componentes de formulario para obtener sus datos
let generalFormData = $state<any>(null);
let observacionesFormData = $state<any>(null);
@@ -328,8 +328,6 @@
async function handleSaveAll() {
saving = true;
error = null;
success = false;
try {
// Verificar tipo de cambio antes de guardar si hay instancia del tab general y hay fecha de pago
@@ -834,14 +832,15 @@
}
}
success = true;
setTimeout(() => {
success = false;
}, 3000);
toast.success(
isCreating
? 'Pedimento creado exitosamente'
: 'Todos los cambios se guardaron correctamente'
);
} catch (e) {
if (e instanceof Error) {
if (e.message.includes('401')) {
error = 'Sesión expirada. Recargando página...';
toast.error('Sesión expirada. Recargando página...');
setTimeout(() => {
window.location.reload();
}, 1500);
@@ -867,7 +866,6 @@
) {
// Interceptar error de tipo de cambio
console.log('Interceptor: Exchange rate missing error caught (Pedimento).');
error = null;
const dateMatch = errorStr.match(/(\d{4}-\d{2}-\d{2})/);
const missingDate = dateMatch ? dateMatch[0] : generalFormData?.payment_date || '';
@@ -879,22 +877,24 @@
}
// Fallback normal: intentar mostrar algo legible
let displayError = '';
if (errorStr.includes('{')) {
// Si parece JSON, intentar formatearlo un poco o mostrar mensaje genérico
try {
const errObj = JSON.parse(errorStr);
// Si es del formato {"field": ["msg"]}
const values = Object.values(errObj).flat();
error = values.join(', ');
displayError = values.join(', ');
} catch {
error = 'Error al guardar (ver consola)';
displayError = 'Error al guardar (ver consola)';
}
} else {
error = errorStr;
displayError = errorStr;
}
toast.error(displayError);
}
} else {
error = 'Error al guardar los cambios';
toast.error('Error al guardar los cambios');
}
console.error('Error saving all:', e);
} finally {
@@ -915,7 +915,7 @@
{#if data.isCreate}
Nuevo Pedimento
{:else}
Pedimento #{data.pedimento.id}
Pedimento #{data.pedimento?.id ?? ''}
{/if}
</h1>
{#if data.isCreate}
@@ -931,8 +931,8 @@
{#if generalFormData?.year && generalFormData?.customs_office && generalFormData?.license && generalFormData?.pedimento_number}
Número: {generalFormData.year}-{generalFormData.customs_office}-{generalFormData.license}-{generalFormData.pedimento_number}
{:else if !data.isCreate && data.pedimento?.pedimento_number}
Número: {data.pedimento.year}-{data.pedimento.customs_office}-{data.pedimento
.license}-{data.pedimento.pedimento_number}
Número: {data.pedimento?.year ?? ''}-{data.pedimento?.customs_office ?? ''}-{data
.pedimento?.license ?? ''}-{data.pedimento?.pedimento_number ?? ''}
{:else}
Edita los detalles del pedimento
{/if}
@@ -942,23 +942,6 @@
<Separator />
<!-- Alertas globales -->
{#if error}
<Alert.Root variant="destructive">
<CircleAlert size={16} />
<Alert.Title>Error</Alert.Title>
<Alert.Description>{error}</Alert.Description>
</Alert.Root>
{/if}
{#if success}
<Alert.Root>
<CircleCheck size={16} />
<Alert.Title>Éxito</Alert.Title>
<Alert.Description>Todos los cambios se guardaron correctamente</Alert.Description>
</Alert.Root>
{/if}
<!-- Tabs con footer fijo -->
<Tabs.Root
value={activeTab}
@@ -968,7 +951,7 @@
class="space-y-4"
>
<!-- Tab content with bottom padding for floating footer -->
<div id="main-form-content" class="pb-56">
<div id="main-form-content" class="pb-24">
<Tabs.Content value="general">
<GeneralTabForm
bind:this={generalTabInstance}
@@ -996,7 +979,7 @@
generalFormData?.pedimento_number
? `${generalFormData.year}-${generalFormData.customs_office}-${generalFormData.license}-${generalFormData.pedimento_number}`
: !data.isCreate && data.pedimento?.pedimento_number
? `${data.pedimento.year}-${data.pedimento.customs_office}-${data.pedimento.license}-${data.pedimento.pedimento_number}`
? `${data.pedimento?.year ?? ''}-${data.pedimento?.customs_office ?? ''}-${data.pedimento?.license ?? ''}-${data.pedimento?.pedimento_number ?? ''}`
: ''}
/>
</Tabs.Content>