feature/limit-characters-customs-brockers

This commit is contained in:
hreyes
2026-02-09 14:01:52 -06:00
parent 24c59bb2b3
commit 45edc56820
4 changed files with 451 additions and 264 deletions

View File

@@ -1,6 +1,6 @@
from typing import Optional
from pydantic import BaseModel
from pydantic import BaseModel, Field
class CustomsBrokerBaseDTO(BaseModel):
@@ -19,7 +19,7 @@ class CustomsBrokerBaseDTO(BaseModel):
tax_id: Optional[str] = None
personal_id: Optional[str] = None
position: Optional[str] = None
license: Optional[str] = None
license: Optional[str] = Field(None, max_length=4, pattern=r"^\d*$")
company: Optional[str] = None
contact: Optional[str] = None
@@ -27,7 +27,7 @@ class CustomsBrokerBaseDTO(BaseModel):
class CustomsBrokerCreateDTO(CustomsBrokerBaseDTO):
"""Schema for creating a new CustomsBroker"""
broker_key: str
broker_key: str = Field(..., max_length=5, pattern=r"^[a-zA-Z0-9]+$", description="Clave única del agente aduanal (máx 5 caracteres)")
class CustomsBrokerUpdateDTO(CustomsBrokerBaseDTO):
@@ -51,7 +51,7 @@ class CustomsBrokerResponseDTO(CustomsBrokerBaseDTO):
# Legacy DTO for backwards compatibility (if needed elsewhere)
class CustomsBrokerDTO(BaseModel):
type: Optional[str] = None
broker_key: str
broker_key: str = Field(..., max_length=5, pattern=r"^[a-zA-Z0-9]+$")
name: Optional[str] = None
address: Optional[str] = None
postal_code: Optional[str] = None
@@ -64,7 +64,7 @@ class CustomsBrokerDTO(BaseModel):
tax_id: Optional[str] = None
personal_id: Optional[str] = None
position: Optional[str] = None
license: Optional[str] = None
license: Optional[str] = Field(None, max_length=4, pattern=r"^\d*$")
company: Optional[str] = None
contact: Optional[str] = None
tenant_id: str
@@ -100,13 +100,13 @@ class CustomsBrokerVUCreateDTO(BaseModel):
class CustomsBrokerPersonnelDTO(BaseModel):
broker_key: str
broker_key: str = Field(..., max_length=5, pattern=r"^[a-zA-Z0-9]+$")
line: int
name: Optional[str]
tax_id: Optional[str]
personal_id: Optional[str]
position: Optional[str]
license: Optional[str]
license: Optional[str] = Field(None, max_length=4, pattern=r"^\d*$")
first_name: Optional[str]
last_name: Optional[str]
middle_name: Optional[str]

View File

@@ -1,221 +1,310 @@
<script lang="ts">
import { Button } from "$lib/components/ui/button";
import * as Dialog from "$lib/components/ui/dialog";
import { Input } from "$lib/components/ui/input";
import { Label } from "$lib/components/ui/label";
import { Separator } from "$lib/components/ui/separator";
import { Loader2 } from "lucide-svelte";
import type { CreateCustomsBrokerData, CustomsBroker } from "$lib/api/dashboard/a76/customs-brokers"; // Ajusta la ruta
import { toast } from "svelte-sonner";
import { Button } from '$lib/components/ui/button';
import * as Dialog from '$lib/components/ui/dialog';
import { Input } from '$lib/components/ui/input';
import { Label } from '$lib/components/ui/label';
import { Separator } from '$lib/components/ui/separator';
import { Loader2 } from 'lucide-svelte';
import type {
CreateCustomsBrokerData,
CustomsBroker
} from '$lib/api/dashboard/a76/customs-brokers'; // Ajusta la ruta
import { toast } from 'svelte-sonner';
// --- Props ---
export let open = false;
export let mode: "create" | "edit" = "create";
export let initialData: CustomsBroker | null = null;
export let companyId: string; // Necesario según tu API
// --- Props ---
export let open = false;
export let mode: 'create' | 'edit' = 'create';
export let initialData: CustomsBroker | null = null;
export let companyId: string; // Necesario según tu API
// La función onSave ahora devuelve una promesa para manejar el loading aquí
export let onSave: (data: CreateCustomsBrokerData) => Promise<void>;
// La función onSave ahora devuelve una promesa para manejar el loading aquí
export let onSave: (data: CreateCustomsBrokerData) => Promise<void>;
// --- Estado ---
let loading = false;
// --- Estado ---
let loading = false;
// Estado del formulario
let formData: CreateCustomsBrokerData = {
broker_key: "",
license: "",
name: "",
tax_id: "",
email: "",
phone: "",
fax: "",
contact: "",
address: "",
postal_code: "",
city: "",
state: "",
country: "",
tenant_id: "", // Se llenará en el submit o por defecto
company_id: ""
};
// Estado del formulario
let formData: CreateCustomsBrokerData = {
broker_key: '',
license: '',
name: '',
tax_id: '',
email: '',
phone: '',
fax: '',
contact: '',
address: '',
postal_code: '',
city: '',
state: '',
country: '',
tenant_id: '', // Se llenará en el submit o por defecto
company_id: ''
};
// --- Reactividad ---
$: if (open) {
if (mode === "edit" && initialData) {
// Cargar datos existentes
formData = {
...initialData,
// Aseguramos que no sean null/undefined para los inputs
name: initialData.name || "",
tax_id: initialData.tax_id || "",
email: initialData.email || "",
phone: initialData.phone || "",
fax: initialData.fax || "",
contact: initialData.contact || "",
address: initialData.address || "",
postal_code: initialData.postal_code || "",
city: initialData.city || "",
state: initialData.state || "",
country: initialData.country || "",
license: initialData.license || ""
};
} else {
// Reset para crear
formData = {
broker_key: "",
license: "",
name: "",
tax_id: "",
email: "",
phone: "",
fax: "",
contact: "",
address: "",
postal_code: "",
city: "",
state: "",
country: "MEX", // Valor por defecto sugerido
tenant_id: "default", // Ajustar según lógica de tu app
company_id: companyId
};
}
}
let brokerKeyError = false;
let licenseError = false;
let brokerKeyTimeout: ReturnType<typeof setTimeout>;
let licenseTimeout: ReturnType<typeof setTimeout>;
// --- Handlers ---
async function handleSubmit() {
try {
loading = true;
// --- Reactividad ---
$: if (open) {
if (mode === 'edit' && initialData) {
// Cargar datos existentes
formData = {
...initialData,
// Aseguramos que no sean null/undefined para los inputs
name: initialData.name || '',
tax_id: initialData.tax_id || '',
email: initialData.email || '',
phone: initialData.phone || '',
fax: initialData.fax || '',
contact: initialData.contact || '',
address: initialData.address || '',
postal_code: initialData.postal_code || '',
city: initialData.city || '',
state: initialData.state || '',
country: initialData.country || '',
license: initialData.license || ''
};
} else {
// Reset para crear
formData = {
broker_key: '',
license: '',
name: '',
tax_id: '',
email: '',
phone: '',
fax: '',
contact: '',
address: '',
postal_code: '',
city: '',
state: '',
country: 'MEX', // Valor por defecto sugerido
tenant_id: 'default', // Ajustar según lógica de tu app
company_id: companyId
};
}
}
// Validaciones básicas
if (!formData.broker_key) {
toast.error("La Clave del Agente es obligatoria");
loading = false;
return;
}
if (!formData.license) {
toast.error("La Patente es obligatoria");
loading = false;
return;
}
// --- Handlers ---
async function handleSubmit() {
try {
loading = true;
// Inyectar company_id si no viene
const payload = { ...formData, company_id: companyId };
// Validaciones básicas
if (!formData.broker_key) {
toast.error('La Clave del Agente es obligatoria');
loading = false;
return;
}
if (!formData.license) {
toast.error('La Patente es obligatoria');
loading = false;
return;
}
await onSave(payload);
open = false;
toast.success(mode === 'create' ? "Agente creado correctamente" : "Agente actualizado correctamente");
} catch (error) {
console.error(error);
toast.error("Error al guardar el agente aduanal");
} finally {
loading = false;
}
}
if (formData.broker_key.length > 5) {
toast.error('La Clave del Agente no debe superar los 5 caracteres');
loading = false;
return;
}
if (formData.license && formData.license.length > 4) {
toast.error('La Patente no debe superar los 4 dígitos');
loading = false;
return;
}
// Inyectar company_id si no viene
const payload = { ...formData, company_id: companyId };
await onSave(payload);
open = false;
toast.success(
mode === 'create' ? 'Agente creado correctamente' : 'Agente actualizado correctamente'
);
} catch (error) {
console.error(error);
toast.error('Error al guardar el agente aduanal');
} finally {
loading = false;
}
}
</script>
<Dialog.Root bind:open>
<Dialog.Content class="sm:max-w-[700px] max-h-[90vh] overflow-y-auto">
<Dialog.Header>
<Dialog.Title>
{mode === "create" ? "Nuevo Agente Aduanal" : "Editar Agente Aduanal"}
</Dialog.Title>
<Dialog.Description>
Ingresa los datos generales del agente. La configuración de VU y Personal se gestiona aparte.
</Dialog.Description>
</Dialog.Header>
<Dialog.Content class="sm:max-w-[700px] max-h-[90vh] overflow-y-auto">
<Dialog.Header>
<Dialog.Title>
{mode === 'create' ? 'Nuevo Agente Aduanal' : 'Editar Agente Aduanal'}
</Dialog.Title>
<Dialog.Description>
Ingresa los datos generales del agente. La configuración de VU y Personal se gestiona
aparte.
</Dialog.Description>
</Dialog.Header>
<div class="grid gap-6 py-4">
<div class="space-y-4">
<h4 class="text-sm font-medium leading-none text-muted-foreground">Identificación</h4>
<div class="grid grid-cols-2 gap-4">
<div class="space-y-2">
<Label for="broker_key">Clave Agente *</Label>
<Input id="broker_key" bind:value={formData.broker_key} placeholder="Ej. 550" disabled={mode === 'edit' || loading} />
</div>
<div class="space-y-2">
<Label for="license">Patente *</Label>
<Input id="license" bind:value={formData.license} placeholder="Ej. 3421" disabled={loading} />
</div>
</div>
<div class="grid grid-cols-2 gap-4">
<div class="space-y-2 col-span-2">
<Label for="name">Nombre / Razón Social</Label>
<Input id="name" bind:value={formData.name} placeholder="Nombre del Agente o Agencia" disabled={loading} />
</div>
<div class="space-y-2">
<Label for="tax_id">RFC</Label>
<Input id="tax_id" bind:value={formData.tax_id} placeholder="RFC de la agencia" disabled={loading} />
</div>
<div class="space-y-2">
<Label for="contact">Nombre Contacto</Label>
<Input id="contact" bind:value={formData.contact} placeholder="Persona de contacto" disabled={loading} />
</div>
</div>
</div>
<div class="grid gap-6 py-4">
<div class="space-y-4">
<h4 class="text-sm font-medium leading-none text-muted-foreground">Identificación</h4>
<div class="grid grid-cols-2 gap-4">
<div class="space-y-2">
<Label for="broker_key">Clave Agente *</Label>
<Input
id="broker_key"
value={formData.broker_key}
oninput={(e) => {
const val = e.currentTarget.value.toUpperCase();
if (val.length > 5) {
brokerKeyError = true;
formData.broker_key = val.slice(0, 5);
e.currentTarget.value = formData.broker_key;
<Separator />
clearTimeout(brokerKeyTimeout);
brokerKeyTimeout = setTimeout(() => {
brokerKeyError = false;
}, 3000);
} else {
brokerKeyError = false;
formData.broker_key = val;
}
}}
placeholder="Ej. 550"
maxlength="6"
class={brokerKeyError ? 'border-red-500 focus-visible:ring-red-500' : ''}
disabled={mode === 'edit' || loading}
/>
{#if brokerKeyError}
<p class="text-[0.8rem] font-medium text-destructive">
La clave no debe superar los 5 caracteres
</p>
{/if}
</div>
<div class="space-y-2">
<Label for="license">Patente *</Label>
<Input
id="license"
value={formData.license}
oninput={(e) => {
const val = e.currentTarget.value.replace(/\D/g, '');
if (val.length > 4) {
licenseError = true;
formData.license = val.slice(0, 4);
e.currentTarget.value = formData.license;
<div class="space-y-4">
<h4 class="text-sm font-medium leading-none text-muted-foreground">Contacto</h4>
<div class="grid grid-cols-3 gap-4">
<div class="space-y-2 col-span-1">
<Label for="phone">Teléfono</Label>
<Input id="phone" bind:value={formData.phone} disabled={loading} />
</div>
<div class="space-y-2 col-span-2">
<Label for="email">Correo Electrónico</Label>
<Input id="email" type="email" bind:value={formData.email} disabled={loading} />
</div>
</div>
</div>
clearTimeout(licenseTimeout);
licenseTimeout = setTimeout(() => {
licenseError = false;
}, 3000);
} else {
licenseError = false;
formData.license = val;
}
}}
placeholder="Ej. 3421"
maxlength="5"
class={licenseError ? 'border-red-500 focus-visible:ring-red-500' : ''}
disabled={loading}
/>
{#if licenseError}
<p class="text-[0.8rem] font-medium text-destructive">
La patente no debe superar los 4 dígitos
</p>
{/if}
</div>
</div>
<div class="grid grid-cols-2 gap-4">
<div class="space-y-2 col-span-2">
<Label for="name">Nombre / Razón Social</Label>
<Input
id="name"
bind:value={formData.name}
placeholder="Nombre del Agente o Agencia"
disabled={loading}
/>
</div>
<div class="space-y-2">
<Label for="tax_id">RFC</Label>
<Input
id="tax_id"
bind:value={formData.tax_id}
placeholder="RFC de la agencia"
disabled={loading}
/>
</div>
<div class="space-y-2">
<Label for="contact">Nombre Contacto</Label>
<Input
id="contact"
bind:value={formData.contact}
placeholder="Persona de contacto"
disabled={loading}
/>
</div>
</div>
</div>
<Separator />
<Separator />
<div class="space-y-4">
<h4 class="text-sm font-medium leading-none text-muted-foreground">Dirección Fiscal</h4>
<div class="space-y-2">
<Label for="address">Calle y Número</Label>
<Input id="address" bind:value={formData.address} disabled={loading} />
</div>
<div class="space-y-4">
<h4 class="text-sm font-medium leading-none text-muted-foreground">Contacto</h4>
<div class="grid grid-cols-3 gap-4">
<div class="space-y-2 col-span-1">
<Label for="phone">Teléfono</Label>
<Input id="phone" bind:value={formData.phone} disabled={loading} />
</div>
<div class="space-y-2 col-span-2">
<Label for="email">Correo Electrónico</Label>
<Input id="email" type="email" bind:value={formData.email} disabled={loading} />
</div>
</div>
</div>
<div class="grid grid-cols-4 gap-4">
<div class="space-y-2">
<Label for="postal_code">C.P.</Label>
<Input id="postal_code" bind:value={formData.postal_code} disabled={loading} />
</div>
<div class="space-y-2 col-span-2">
<Label for="city">Ciudad</Label>
<Input id="city" bind:value={formData.city} disabled={loading} />
</div>
<div class="space-y-2">
<Label for="state">Estado</Label>
<Input id="state" bind:value={formData.state} disabled={loading} />
</div>
</div>
<div class="grid grid-cols-4 gap-4">
<div class="space-y-2">
<Label for="country">País</Label>
<Input id="country" bind:value={formData.country} disabled={loading} />
</div>
</div>
</div>
<Separator />
</div>
<div class="space-y-4">
<h4 class="text-sm font-medium leading-none text-muted-foreground">Dirección Fiscal</h4>
<Dialog.Footer>
<Button variant="outline" onclick={() => (open = false)} disabled={loading}>
Cancelar
</Button>
<Button onclick={handleSubmit} disabled={loading}>
{#if loading}
<Loader2 class="mr-2 h-4 w-4 animate-spin" />
Guardando
{:else}
Guardar Agente
{/if}
</Button>
</Dialog.Footer>
</Dialog.Content>
</Dialog.Root>
<div class="space-y-2">
<Label for="address">Calle y Número</Label>
<Input id="address" bind:value={formData.address} disabled={loading} />
</div>
<div class="grid grid-cols-4 gap-4">
<div class="space-y-2">
<Label for="postal_code">C.P.</Label>
<Input id="postal_code" bind:value={formData.postal_code} disabled={loading} />
</div>
<div class="space-y-2 col-span-2">
<Label for="city">Ciudad</Label>
<Input id="city" bind:value={formData.city} disabled={loading} />
</div>
<div class="space-y-2">
<Label for="state">Estado</Label>
<Input id="state" bind:value={formData.state} disabled={loading} />
</div>
</div>
<div class="grid grid-cols-4 gap-4">
<div class="space-y-2">
<Label for="country">País</Label>
<Input id="country" bind:value={formData.country} disabled={loading} />
</div>
</div>
</div>
</div>
<Dialog.Footer>
<Button variant="outline" onclick={() => (open = false)} disabled={loading}>Cancelar</Button>
<Button onclick={handleSubmit} disabled={loading}>
{#if loading}
<Loader2 class="mr-2 h-4 w-4 animate-spin" />
Guardando
{:else}
Guardar Agente
{/if}
</Button>
</Dialog.Footer>
</Dialog.Content>
</Dialog.Root>

View File

@@ -1,10 +1,14 @@
<script lang="ts">
import { Button } from "$lib/components/ui/button";
import * as Dialog from "$lib/components/ui/dialog";
import { Input } from "$lib/components/ui/input";
import { Label } from "$lib/components/ui/label";
import { customsBrokersApi, type CreateCustomsBrokerData, type CustomsBroker } from "$lib/api/dashboard/a76/customs-brokers";
import { companyStore } from "$lib/stores/company.svelte";
import { Button } from '$lib/components/ui/button';
import * as Dialog from '$lib/components/ui/dialog';
import { Input } from '$lib/components/ui/input';
import { Label } from '$lib/components/ui/label';
import {
customsBrokersApi,
type CreateCustomsBrokerData,
type CustomsBroker
} from '$lib/api/dashboard/a76/customs-brokers';
import { companyStore } from '$lib/stores/company.svelte';
let {
open = $bindable(false),
@@ -17,56 +21,58 @@
} = $props();
let formData = $state({
name: "",
type: "",
address: "",
postal_code: "",
city: "",
state: "",
phone: "",
fax: "",
email: "",
country: "",
tax_id: "",
personal_id: "",
position: "",
license: "",
company: "",
contact: ""
name: '',
type: '',
address: '',
postal_code: '',
city: '',
state: '',
phone: '',
fax: '',
email: '',
country: '',
tax_id: '',
personal_id: '',
position: '',
license: '',
company: '',
contact: ''
});
let loading = $state(false);
let error = $state<string | null>(null);
let licenseError = $state(false);
let licenseTimeout: ReturnType<typeof setTimeout>;
// Inicializar formulario cuando cambia el broker
$effect(() => {
if (open && broker) {
formData = {
name: broker.name || "",
type: broker.type || "",
address: broker.address || "",
postal_code: broker.postal_code || "",
city: broker.city || "",
state: broker.state || "",
phone: broker.phone || "",
fax: broker.fax || "",
email: broker.email || "",
country: broker.country || "",
tax_id: broker.tax_id || "",
personal_id: broker.personal_id || "",
position: broker.position || "",
license: broker.license || "",
company: broker.company || "",
contact: broker.contact || ""
name: broker.name || '',
type: broker.type || '',
address: broker.address || '',
postal_code: broker.postal_code || '',
city: broker.city || '',
state: broker.state || '',
phone: broker.phone || '',
fax: broker.fax || '',
email: broker.email || '',
country: broker.country || '',
tax_id: broker.tax_id || '',
personal_id: broker.personal_id || '',
position: broker.position || '',
license: broker.license || '',
company: broker.company || '',
contact: broker.contact || ''
};
}
});
async function handleSubmit(e: Event) {
e.preventDefault();
if (!companyStore.activeCompany) {
error = "No hay compañía seleccionada";
error = 'No hay compañía seleccionada';
return;
}
@@ -116,8 +122,8 @@
onSuccess();
}
} catch (e) {
error = e instanceof Error ? e.message : "Error al guardar";
console.error("Error updating:", e);
error = e instanceof Error ? e.message : 'Error al guardar';
console.error('Error updating:', e);
} finally {
loading = false;
}
@@ -136,13 +142,17 @@
<Dialog.Header>
<Dialog.Title>Editar Agente Aduanal</Dialog.Title>
<Dialog.Description>
Modifica los datos del agente aduanal <span class="font-mono font-semibold">{broker?.broker_key}</span>.
Modifica los datos del agente aduanal <span class="font-mono font-semibold"
>{broker?.broker_key}</span
>.
</Dialog.Description>
</Dialog.Header>
<form onsubmit={handleSubmit} class="space-y-6">
{#if error}
<div class="rounded-lg border border-destructive bg-destructive/10 p-3 text-sm text-destructive">
<div
class="rounded-lg border border-destructive bg-destructive/10 p-3 text-sm text-destructive"
>
{error}
</div>
{/if}
@@ -150,7 +160,7 @@
<!-- Información básica -->
<div class="space-y-4">
<h3 class="text-sm font-semibold">Información Básica</h3>
<div class="grid grid-cols-2 gap-4">
<div class="space-y-2">
<Label for="edit-broker_key">Clave</Label>
@@ -158,7 +168,7 @@
id="edit-broker_key"
value={broker?.broker_key}
disabled
class="bg-muted"
class="bg-muted {broker?.broker_key?.length > 5 ? 'border-red-500' : ''}"
/>
</div>
@@ -190,11 +200,33 @@
<Label for="edit-license">Patente</Label>
<Input
id="edit-license"
bind:value={formData.license}
value={formData.license}
oninput={(e) => {
const val = e.currentTarget.value.replace(/\D/g, '');
if (val.length > 4) {
licenseError = true;
formData.license = val.slice(0, 4);
e.currentTarget.value = formData.license;
clearTimeout(licenseTimeout);
licenseTimeout = setTimeout(() => {
licenseError = false;
}, 3000);
} else {
licenseError = false;
formData.license = val;
}
}}
placeholder="Número de patente"
maxlength={4}
maxlength="5"
class={licenseError ? 'border-red-500 focus-visible:ring-red-500' : ''}
disabled={loading}
/>
{#if licenseError}
<p class="text-[0.8rem] font-medium text-destructive">
La patente no debe superar los 4 dígitos
</p>
{/if}
</div>
<div class="space-y-2">
@@ -213,7 +245,7 @@
<!-- Información de contacto -->
<div class="space-y-4">
<h3 class="text-sm font-semibold">Información de Contacto</h3>
<div class="grid grid-cols-2 gap-4">
<div class="space-y-2">
<Label for="edit-phone">Teléfono</Label>
@@ -265,7 +297,7 @@
<!-- Dirección -->
<div class="space-y-4">
<h3 class="text-sm font-semibold">Dirección</h3>
<div class="space-y-2">
<Label for="edit-address">Dirección</Label>
<Input
@@ -327,7 +359,7 @@
<!-- Información fiscal -->
<div class="space-y-4">
<h3 class="text-sm font-semibold">Información Fiscal</h3>
<div class="grid grid-cols-2 gap-4">
<div class="space-y-2">
<Label for="edit-tax_id">RFC</Label>
@@ -371,7 +403,9 @@
<Button type="submit" disabled={loading}>
{#if loading}
<div class="flex items-center gap-2">
<div class="h-4 w-4 animate-spin rounded-full border-2 border-primary-foreground border-t-transparent"></div>
<div
class="h-4 w-4 animate-spin rounded-full border-2 border-primary-foreground border-t-transparent"
></div>
Guardando...
</div>
{:else}

View File

@@ -62,6 +62,11 @@
company_id: ''
});
let brokerKeyError = $state(false);
let licenseError = $state(false);
let brokerKeyTimeout: ReturnType<typeof setTimeout>;
let licenseTimeout: ReturnType<typeof setTimeout>;
// --- 3. CARGA DE DATOS REACTIVA ---
$effect(() => {
const company = companyStore.activeCompany;
@@ -126,6 +131,18 @@
return;
}
if (formData.broker_key.length > 5) {
error = 'La Clave del Agente no debe superar los 5 caracteres';
toast.error(error);
return;
}
if (formData.license && formData.license.length > 4) {
error = 'La Patente no debe superar los 4 dígitos';
toast.error(error);
return;
}
loading = true;
error = null;
try {
@@ -213,19 +230,66 @@
>Clave Agente <span class="text-destructive">*</span></Label
>
<Input
bind:value={formData.broker_key}
value={formData.broker_key}
oninput={(e) => {
const val = e.currentTarget.value.toUpperCase();
if (val.length > 5) {
brokerKeyError = true;
formData.broker_key = val.slice(0, 5);
e.currentTarget.value = formData.broker_key;
clearTimeout(brokerKeyTimeout);
brokerKeyTimeout = setTimeout(() => {
brokerKeyError = false;
}, 3000);
} else {
brokerKeyError = false;
formData.broker_key = val;
}
}}
placeholder="Ej. 550"
maxlength="6"
class={brokerKeyError ? 'border-red-500 focus-visible:ring-red-500' : ''}
disabled={isEdit || loading}
/>
<p class="text-xs text-muted-foreground">
Clave interna o número de patente único.
</p>
{#if brokerKeyError}
<p class="text-[0.8rem] font-medium text-destructive">
La clave no debe superar los 5 caracteres
</p>
{/if}
</div>
<div class="grid gap-2">
<Label class="required"
>Patente / Autorización <span class="text-destructive">*</span></Label
>
<Input bind:value={formData.license} placeholder="Ej. 3421" disabled={loading} />
<Input
value={formData.license}
oninput={(e) => {
const val = e.currentTarget.value.replace(/\D/g, '');
if (val.length > 4) {
licenseError = true;
formData.license = val.slice(0, 4);
e.currentTarget.value = formData.license;
clearTimeout(licenseTimeout);
licenseTimeout = setTimeout(() => {
licenseError = false;
}, 3000);
} else {
licenseError = false;
formData.license = val;
}
}}
placeholder="Ej. 3421"
maxlength="5"
class={licenseError ? 'border-red-500 focus-visible:ring-red-500' : ''}
disabled={loading}
/>
{#if licenseError}
<p class="text-[0.8rem] font-medium text-destructive">
La patente no debe superar los 4 dígitos
</p>
{/if}
</div>
</div>