Merge branch 'fix/transportes-no-traducido' into fix/catalogo-conversion-filtros
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
import { Input } from '$lib/components/ui/input';
|
||||
import { Label } from '$lib/components/ui/label';
|
||||
import * as Select from '$lib/components/ui/select';
|
||||
import * as Tabs from '$lib/components/ui/tabs';
|
||||
import { vehiclesApi, type Vehicle } from '$lib/api/dashboard/a76/vehicles';
|
||||
import { transportersApi, type Transporter } from '$lib/api/dashboard/a76/transporters';
|
||||
import { transportTypesApi } from '$lib/api/dashboard/reference_data/transport_types';
|
||||
@@ -72,6 +73,29 @@
|
||||
{ value: 'B', label: 'B' }
|
||||
];
|
||||
|
||||
const TRANSPORT_TYPE_TRANSLATIONS: Record<string, string> = {
|
||||
AR: 'Camión Blindado',
|
||||
AU: 'Automóviles',
|
||||
BT: 'Camión de Caja',
|
||||
BU: 'Autobús',
|
||||
BV: 'Camión de Bebidas',
|
||||
BY: 'Bicicleta',
|
||||
CO: 'Vehículo de Construcción',
|
||||
EV: 'Vehículo de Emergencia',
|
||||
FE: 'Ferry',
|
||||
FM: 'Tractor Agrícola',
|
||||
GB: 'Camión de Basura',
|
||||
MC: 'Motocicleta',
|
||||
OC: 'Otro',
|
||||
PM: 'Camioneta con cabina',
|
||||
PN: 'Camión Panel',
|
||||
PU: 'Camioneta (Pick-up)',
|
||||
PV: 'Pasajero',
|
||||
RV: 'Vehículo Recreativo (RV)',
|
||||
TR: 'Tractocamión',
|
||||
TV: 'Van'
|
||||
};
|
||||
|
||||
const countryM3 = $derived(
|
||||
countries.find((c) => c.ame_key === formData.country)?.m3_key ?? null
|
||||
);
|
||||
@@ -209,7 +233,7 @@
|
||||
</script>
|
||||
|
||||
<Dialog.Root bind:open>
|
||||
<Dialog.Content class="max-h-[90vh] max-w-4xl overflow-y-auto">
|
||||
<Dialog.Content class="max-h-[95vh] max-w-3xl overflow-y-auto">
|
||||
<Dialog.Header>
|
||||
<Dialog.Title>{title}</Dialog.Title>
|
||||
<Dialog.Description>
|
||||
@@ -232,221 +256,263 @@
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="grid grid-cols-1 gap-6 md:grid-cols-2">
|
||||
<!-- Información del Vehículo -->
|
||||
<section class="space-y-4">
|
||||
<h3 class="border-b pb-2 text-sm font-semibold">Identificación del Vehículo</h3>
|
||||
<Tabs.Root value="general" class="w-full">
|
||||
<Tabs.List class="grid w-full grid-cols-2">
|
||||
<Tabs.Trigger value="general">Información General</Tabs.Trigger>
|
||||
<Tabs.Trigger value="details">Seguro y Detalles</Tabs.Trigger>
|
||||
</Tabs.List>
|
||||
|
||||
<div class="grid gap-2">
|
||||
<Label for="vehicle_key"
|
||||
>Clave del Vehículo <span class="text-destructive">*</span></Label
|
||||
>
|
||||
<Input
|
||||
id="vehicle_key"
|
||||
bind:value={formData.vehicle_key}
|
||||
disabled={isEdit}
|
||||
required
|
||||
maxlength={14}
|
||||
/>
|
||||
</div>
|
||||
<Tabs.Content value="general" class="mt-6 space-y-8">
|
||||
<!-- Información del Vehículo -->
|
||||
<section class="space-y-4">
|
||||
<h3 class="border-b pb-2 text-sm font-bold uppercase tracking-wider text-muted-foreground">
|
||||
Identificación del Vehículo
|
||||
</h3>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="grid gap-2">
|
||||
<Label for="brand">Marca</Label>
|
||||
<Input id="brand" bind:value={formData.brand} maxlength={20} />
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<div class="grid gap-2 md:col-span-2">
|
||||
<Label for="vehicle_key"
|
||||
>Clave del Vehículo <span class="text-destructive">*</span></Label
|
||||
>
|
||||
<Input
|
||||
id="vehicle_key"
|
||||
bind:value={formData.vehicle_key}
|
||||
disabled={isEdit}
|
||||
required
|
||||
maxlength={14}
|
||||
placeholder="Ej: VH001"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-2">
|
||||
<Label for="brand">Marca</Label>
|
||||
<Input id="brand" bind:value={formData.brand} maxlength={20} placeholder="Ej: Kenworth" />
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="year">Año</Label>
|
||||
<Input id="year" bind:value={formData.year} maxlength={4} placeholder="YYYY" />
|
||||
</div>
|
||||
|
||||
<div class="grid gap-2">
|
||||
<Label for="plate_number">Placas</Label>
|
||||
<Input id="plate_number" bind:value={formData.plate_number} maxlength={17} placeholder="Placas actuales" />
|
||||
</div>
|
||||
|
||||
<div class="grid gap-2">
|
||||
<Label for="series">Serie / VIN</Label>
|
||||
<Input id="series" bind:value={formData.series} maxlength={30} placeholder="Número de serie" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="year">Año</Label>
|
||||
<Input id="year" bind:value={formData.year} maxlength={4} placeholder="YYYY" />
|
||||
</section>
|
||||
|
||||
<!-- Datos de Transporte -->
|
||||
<section class="space-y-4">
|
||||
<h3 class="border-b pb-2 text-sm font-bold uppercase tracking-wider text-muted-foreground">
|
||||
Datos de Transporte
|
||||
</h3>
|
||||
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<div class="grid gap-2 md:col-span-2">
|
||||
<Label for="transporter_key">Transportista</Label>
|
||||
<Select.Root type="single" bind:value={formData.transporter_key} disabled={refsLoading}>
|
||||
<Select.Trigger class="w-full">
|
||||
{refsLoading
|
||||
? '...'
|
||||
: formData.transporter_key
|
||||
? `${formData.transporter_key} — ${transporters.find((t) => t.transporter_key === formData.transporter_key)?.name ?? ''}`
|
||||
: '— Seleccionar transportista —'}
|
||||
</Select.Trigger>
|
||||
<Select.Content class="max-h-60">
|
||||
<Select.Item value="" label="Vacío">— Vacío —</Select.Item>
|
||||
{#each transporters as t}
|
||||
<Select.Item value={t.transporter_key} label={t.transporter_key}>
|
||||
{t.transporter_key} — {t.name || t.short_name || ''}
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-2">
|
||||
<Label for="transport_identifier">ID Transporte</Label>
|
||||
<Input
|
||||
id="transport_identifier"
|
||||
bind:value={formData.transport_identifier}
|
||||
maxlength={30}
|
||||
placeholder="Identificador único"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-2">
|
||||
<Label for="transport_type">Tipo de Transporte</Label>
|
||||
<Select.Root type="single" bind:value={formData.transport_type} disabled={refsLoading}>
|
||||
<Select.Trigger class="w-full" id="transport_type">
|
||||
{refsLoading
|
||||
? '...'
|
||||
: formData.transport_type
|
||||
? `${formData.transport_type} — ${TRANSPORT_TYPE_TRANSLATIONS[formData.transport_type] || transportTypes.find((x) => x.transport_code === formData.transport_type)?.description || ''}`
|
||||
: '— Opcional —'}
|
||||
</Select.Trigger>
|
||||
<Select.Content>
|
||||
<Select.Item value="" label="Vacío">— Vacío —</Select.Item>
|
||||
{#each transportTypes as x}
|
||||
<Select.Item value={x.transport_code} label={x.transport_code}>
|
||||
{x.transport_code} — {TRANSPORT_TYPE_TRANSLATIONS[x.transport_code] || x.description}
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-2">
|
||||
<Label for="entity_code">Código de Entidad</Label>
|
||||
<Select.Root type="single" bind:value={formData.entity_code}>
|
||||
<Select.Trigger class="w-full" id="entity_code">
|
||||
{ENTITY_OPTS.find((o) => o.value === (formData.entity_code || ''))?.label ??
|
||||
'— Sin código —'}
|
||||
</Select.Trigger>
|
||||
<Select.Content>
|
||||
{#each ENTITY_OPTS as o}
|
||||
<Select.Item value={o.value} label={o.label}>{o.label}</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
<p class="text-[10px] text-muted-foreground uppercase">Obligatorio al crear (reglas CSV)</p>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-2">
|
||||
<Label for="sct_permission">Permiso SCT</Label>
|
||||
<Input id="sct_permission" bind:value={formData.sct_permission} maxlength={40} placeholder="Número de permiso" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</Tabs.Content>
|
||||
|
||||
<div class="grid gap-2">
|
||||
<Label for="plate_number">Placas</Label>
|
||||
<Input id="plate_number" bind:value={formData.plate_number} maxlength={17} />
|
||||
</div>
|
||||
<Tabs.Content value="details" class="mt-6 space-y-8">
|
||||
<!-- Seguro y Otros -->
|
||||
<section class="space-y-4">
|
||||
<h3 class="border-b pb-2 text-sm font-bold uppercase tracking-wider text-muted-foreground">
|
||||
Seguro y Otros
|
||||
</h3>
|
||||
|
||||
<div class="grid gap-2">
|
||||
<Label for="series">Serie / VIN</Label>
|
||||
<Input id="series" bind:value={formData.series} maxlength={30} />
|
||||
</div>
|
||||
</section>
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<div class="grid gap-2 md:col-span-2">
|
||||
<Label for="insurance_company_name">Aseguradora</Label>
|
||||
<Input
|
||||
id="insurance_company_name"
|
||||
bind:value={formData.insurance_company_name}
|
||||
maxlength={30}
|
||||
placeholder="Nombre de la compañía"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Datos de Transporte -->
|
||||
<section class="space-y-4">
|
||||
<h3 class="border-b pb-2 text-sm font-semibold">Datos de Transporte</h3>
|
||||
<div class="grid gap-2">
|
||||
<Label for="insurance_number">Póliza</Label>
|
||||
<Input
|
||||
id="insurance_number"
|
||||
bind:value={formData.insurance_number}
|
||||
maxlength={20}
|
||||
placeholder="Número de póliza"
|
||||
/>
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="insurance_amount">Monto</Label>
|
||||
<Input
|
||||
id="insurance_amount"
|
||||
type="number"
|
||||
bind:value={formData.insurance_amount}
|
||||
placeholder="0.00"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-2">
|
||||
<Label for="transporter_key">Clave Transportista</Label>
|
||||
<Select.Root type="single" bind:value={formData.transporter_key} disabled={refsLoading}>
|
||||
<Select.Trigger class="w-full">
|
||||
{refsLoading
|
||||
? '...'
|
||||
: formData.transporter_key
|
||||
? `${formData.transporter_key} — ${transporters.find((t) => t.transporter_key === formData.transporter_key)?.name ?? ''}`
|
||||
: '— Opcional —'}
|
||||
</Select.Trigger>
|
||||
<Select.Content class="max-h-60">
|
||||
<Select.Item value="" label="Vacío">— Vacío —</Select.Item>
|
||||
{#each transporters as t}
|
||||
<Select.Item value={t.transporter_key} label={t.transporter_key}>
|
||||
{t.transporter_key} — {t.name || t.short_name || ''}
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-2">
|
||||
<Label for="transport_identifier">Identificador Transporte</Label>
|
||||
<Input
|
||||
id="transport_identifier"
|
||||
bind:value={formData.transport_identifier}
|
||||
maxlength={30}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-2">
|
||||
<Label for="transport_type">Tipo de Transporte</Label>
|
||||
<Select.Root type="single" bind:value={formData.transport_type} disabled={refsLoading}>
|
||||
<Select.Trigger class="w-full" id="transport_type">
|
||||
{refsLoading
|
||||
? '...'
|
||||
: formData.transport_type
|
||||
? `${formData.transport_type} — ${transportTypes.find((x) => x.transport_code === formData.transport_type)?.description ?? ''}`
|
||||
: '— Opcional —'}
|
||||
</Select.Trigger>
|
||||
<Select.Content>
|
||||
<Select.Item value="" label="Vacío">— Vacío —</Select.Item>
|
||||
{#each transportTypes as x}
|
||||
<Select.Item value={x.transport_code} label={x.transport_code}>
|
||||
{x.transport_code} — {x.description}
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-2">
|
||||
<Label for="entity_code">Código de entidad</Label>
|
||||
<Select.Root type="single" bind:value={formData.entity_code}>
|
||||
<Select.Trigger class="w-full" id="entity_code">
|
||||
{ENTITY_OPTS.find((o) => o.value === (formData.entity_code || ''))?.label ??
|
||||
'— Sin código —'}
|
||||
</Select.Trigger>
|
||||
<Select.Content>
|
||||
{#each ENTITY_OPTS as o}
|
||||
<Select.Item value={o.value} label={o.label}>{o.label}</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
<p class="text-xs text-muted-foreground">Obligatorio al crear (reglas CSV).</p>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-2">
|
||||
<Label for="sct_permission">Permiso SCT</Label>
|
||||
<Input id="sct_permission" bind:value={formData.sct_permission} maxlength={40} />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Seguro y Otros -->
|
||||
<section class="space-y-4">
|
||||
<h3 class="border-b pb-2 text-sm font-semibold">Seguro y Otros</h3>
|
||||
|
||||
<div class="grid gap-2">
|
||||
<Label for="insurance_company_name">Aseguradora</Label>
|
||||
<Input
|
||||
id="insurance_company_name"
|
||||
bind:value={formData.insurance_company_name}
|
||||
maxlength={30}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="grid gap-2">
|
||||
<Label for="insurance_number">Póliza</Label>
|
||||
<Input id="insurance_number" bind:value={formData.insurance_number} maxlength={20} />
|
||||
<div class="grid gap-2">
|
||||
<Label for="dot_number">Número DOT</Label>
|
||||
<Input id="dot_number" bind:value={formData.dot_number} maxlength={8} placeholder="DOT ID" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="insurance_amount">Monto Seguro</Label>
|
||||
<Input id="insurance_amount" type="number" bind:value={formData.insurance_amount} />
|
||||
</section>
|
||||
|
||||
<!-- Ubicación y Detalles -->
|
||||
<section class="space-y-4">
|
||||
<h3 class="border-b pb-2 text-sm font-bold uppercase tracking-wider text-muted-foreground">
|
||||
Ubicación y Detalles
|
||||
</h3>
|
||||
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<div class="grid gap-2">
|
||||
<Label for="country">País (AME)</Label>
|
||||
<Select.Root type="single" bind:value={formData.country} disabled={refsLoading}>
|
||||
<Select.Trigger class="w-full" id="country">
|
||||
{refsLoading
|
||||
? '...'
|
||||
: formData.country
|
||||
? `${formData.country} — ${countries.find((c) => c.ame_key === formData.country)?.description_es ?? ''}`
|
||||
: '— Opcional —'}
|
||||
</Select.Trigger>
|
||||
<Select.Content class="max-h-60">
|
||||
<Select.Item value="" label="Vacío">— Vacío —</Select.Item>
|
||||
{#each countries as c}
|
||||
<Select.Item value={c.ame_key} label={c.ame_key}>
|
||||
{c.ame_key} — {c.description_es}
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="state">Estado</Label>
|
||||
<Select.Root type="single" bind:value={formData.state} disabled={refsLoading}>
|
||||
<Select.Trigger class="w-full" id="state">
|
||||
{refsLoading
|
||||
? '...'
|
||||
: formData.state ||
|
||||
(formData.country ? '— Selecciona —' : '— Primero el país —')}
|
||||
</Select.Trigger>
|
||||
<Select.Content class="max-h-60">
|
||||
<Select.Item value="" label="Vacío">— Vacío —</Select.Item>
|
||||
{#each statesFiltered as s}
|
||||
<Select.Item value={s.description} label={s.description}>
|
||||
{s.description}
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-2">
|
||||
<Label for="city">Ciudad</Label>
|
||||
<Input id="city" bind:value={formData.city} maxlength={30} placeholder="Ciudad/Localidad" />
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="color">Color</Label>
|
||||
<Input id="color" bind:value={formData.color} maxlength={20} placeholder="Color del vehículo" />
|
||||
</div>
|
||||
|
||||
<div class="grid gap-2">
|
||||
<Label for="container_key">Tipo Contenedor</Label>
|
||||
<Input
|
||||
id="container_key"
|
||||
bind:value={formData.container_key}
|
||||
maxlength={3}
|
||||
placeholder="Ej: 40G"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="grid gap-2">
|
||||
<Label for="dot_number">Número DOT</Label>
|
||||
<Input id="dot_number" bind:value={formData.dot_number} maxlength={8} />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Ubicación y Detalles -->
|
||||
<section class="space-y-4">
|
||||
<h3 class="border-b pb-2 text-sm font-semibold">Ubicación y Detalles</h3>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<section class="space-y-4 pt-4">
|
||||
<div class="grid gap-2">
|
||||
<Label for="country">País (clave americana)</Label>
|
||||
<Select.Root type="single" bind:value={formData.country} disabled={refsLoading}>
|
||||
<Select.Trigger class="w-full" id="country">
|
||||
{refsLoading
|
||||
? '...'
|
||||
: formData.country
|
||||
? `${formData.country} — ${countries.find((c) => c.ame_key === formData.country)?.description_es ?? ''}`
|
||||
: '— Opcional —'}
|
||||
</Select.Trigger>
|
||||
<Select.Content class="max-h-60">
|
||||
<Select.Item value="" label="Vacío">— Vacío —</Select.Item>
|
||||
{#each countries as c}
|
||||
<Select.Item value={c.ame_key} label={c.ame_key}>
|
||||
{c.ame_key} — {c.description_es}
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="state">Estado / provincia</Label>
|
||||
<Select.Root type="single" bind:value={formData.state} disabled={refsLoading}>
|
||||
<Select.Trigger class="w-full" id="state">
|
||||
{refsLoading
|
||||
? '...'
|
||||
: formData.state ||
|
||||
(formData.country ? '— Selecciona —' : '— Primero el país —')}
|
||||
</Select.Trigger>
|
||||
<Select.Content class="max-h-60">
|
||||
<Select.Item value="" label="Vacío">— Vacío —</Select.Item>
|
||||
{#each statesFiltered as s}
|
||||
<Select.Item value={s.description} label={s.description}>
|
||||
{s.description}
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-2">
|
||||
<Label for="description">Descripción</Label>
|
||||
<Input id="description" bind:value={formData.description} maxlength={100} />
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="grid gap-2">
|
||||
<Label for="color">Color</Label>
|
||||
<Input id="color" bind:value={formData.color} maxlength={20} />
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="container_key">Contenedor</Label>
|
||||
<Label for="description">Descripción Adicional</Label>
|
||||
<Input
|
||||
id="container_key"
|
||||
bind:value={formData.container_key}
|
||||
maxlength={3}
|
||||
placeholder="3 car."
|
||||
id="description"
|
||||
bind:value={formData.description}
|
||||
maxlength={100}
|
||||
placeholder="Notas adicionales sobre el vehículo..."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
</Tabs.Content>
|
||||
</Tabs.Root>
|
||||
|
||||
<Dialog.Footer>
|
||||
<Button type="button" variant="outline" onclick={handleCancel} disabled={loading}>
|
||||
|
||||
Reference in New Issue
Block a user