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:
@@ -31,8 +31,8 @@ export interface ClientProvider {
|
||||
domicile_fiscal?: string | null;
|
||||
foreign_tax_id?: string | null;
|
||||
client_or_provider?: string | null;
|
||||
enabled_disabled?: number;
|
||||
tenant_id: number;
|
||||
is_active?: boolean;
|
||||
tenant_id: number;
|
||||
address?: ClientProviderAddress | null;
|
||||
programs?: ClientProviderPrograms | null;
|
||||
}
|
||||
@@ -46,7 +46,7 @@ export interface ClientProviderBasic {
|
||||
domicile_fiscal?: string | null;
|
||||
foreign_tax_id?: string | null;
|
||||
client_or_provider?: string | null;
|
||||
enabled_disabled?: number;
|
||||
is_active?: boolean;
|
||||
tenant_id: number;
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ export interface CreateClientProviderData {
|
||||
domicile_fiscal?: string | null;
|
||||
foreign_tax_id?: string | null;
|
||||
client_or_provider?: string | null;
|
||||
enabled_disabled?: number;
|
||||
is_active?: boolean;
|
||||
address?: Omit<ClientProviderAddress, 'id' | 'client_id'> | null;
|
||||
programs?: Omit<ClientProviderPrograms, 'id' | 'client_id'> | null;
|
||||
}
|
||||
@@ -78,7 +78,7 @@ export interface UpdateClientProviderData {
|
||||
domicile_fiscal?: string | null;
|
||||
foreign_tax_id?: string | null;
|
||||
client_or_provider?: string | null;
|
||||
enabled_disabled?: number;
|
||||
is_active?: boolean;
|
||||
address?: Partial<ClientProviderAddress> | null;
|
||||
programs?: Partial<ClientProviderPrograms> | null;
|
||||
}
|
||||
@@ -100,7 +100,7 @@ export const clientsProvidersApi = {
|
||||
page: page.toString(),
|
||||
page_size: pageSize.toString()
|
||||
});
|
||||
|
||||
|
||||
if (filters) {
|
||||
Object.entries(filters).forEach(([key, value]) => {
|
||||
if (value !== undefined && value !== null && value !== '') {
|
||||
@@ -108,7 +108,7 @@ export const clientsProvidersApi = {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
return api.get<ClientProviderListResponse>(
|
||||
`/v1/a76/clients-providers?${params.toString()}`
|
||||
);
|
||||
@@ -166,7 +166,7 @@ export const clientsProvidersApi = {
|
||||
|
||||
/**
|
||||
* Crea un nuevo cliente/proveedor
|
||||
* @param companyId - ID de la compañía
|
||||
* @param companyId - ID de la compañía
|
||||
* @param data - Datos del cliente/proveedor a crear
|
||||
*/
|
||||
create: (companyId: number, data: CreateClientProviderData) =>
|
||||
|
||||
@@ -19,7 +19,7 @@ export interface CustomsBroker {
|
||||
tax_id?: string | null;
|
||||
personal_id?: string | null;
|
||||
position?: string | null;
|
||||
license?: string | null;
|
||||
license: string;
|
||||
company?: string | null;
|
||||
contact?: string | null;
|
||||
tenant_id: string;
|
||||
@@ -117,6 +117,14 @@ export const customsBrokersApi = {
|
||||
return api.delete<CustomsBroker>(`/v1/a76/customs-brokers/${brokerKey}?company_id=${companyId}`);
|
||||
},
|
||||
|
||||
/**
|
||||
* Actualiza la información de un agente aduanal
|
||||
*/
|
||||
update: (brokerKey: string, data: CreateCustomsBrokerData) => {
|
||||
const companyId = data.company_id;
|
||||
return api.put<CustomsBroker>(`/v1/a76/customs-brokers/${brokerKey}?company_id=${companyId}`, data);
|
||||
},
|
||||
|
||||
/**
|
||||
* Actualiza la información de VU de un agente aduanal
|
||||
*/
|
||||
|
||||
@@ -17,8 +17,6 @@ export interface PedimentoDates {
|
||||
original_date?: string | null;
|
||||
start_date?: string | null;
|
||||
end_date?: string | null;
|
||||
capture_date?: string | null;
|
||||
capture_time?: string | null;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
@@ -33,8 +31,6 @@ export interface CreatePedimentoDatesData {
|
||||
original_date?: string | null;
|
||||
start_date?: string | null;
|
||||
end_date?: string | null;
|
||||
capture_date?: string | null;
|
||||
capture_time?: string | null;
|
||||
}
|
||||
|
||||
export interface UpdatePedimentoDatesData {
|
||||
@@ -48,8 +44,6 @@ export interface UpdatePedimentoDatesData {
|
||||
original_date?: string | null;
|
||||
start_date?: string | null;
|
||||
end_date?: string | null;
|
||||
capture_date?: string | null;
|
||||
capture_time?: string | null;
|
||||
}
|
||||
|
||||
export const pedimentoDatesApi = {
|
||||
|
||||
@@ -15,9 +15,7 @@ export interface PedimentoDates {
|
||||
eucan_date?: string | null;
|
||||
original_date?: string | null;
|
||||
start_date?: string | null;
|
||||
end_date?: string | null;
|
||||
capture_date?: string | null;
|
||||
capture_time?: string | null;
|
||||
end_date?: string | null;
|
||||
}
|
||||
|
||||
export interface PedimentoPayments {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
import { materialTypesApi, type MaterialType } from "$lib/api/dashboard/refrence_data/material_types";
|
||||
import { companyStore } from "$lib/stores/company.svelte";
|
||||
import { onMount } from 'svelte';
|
||||
import { LoaderCircle, Home } from 'lucide-svelte';
|
||||
|
||||
let {
|
||||
open = $bindable(false),
|
||||
@@ -248,21 +249,7 @@
|
||||
{#if companyStore.activeCompany}
|
||||
<div class="rounded-md bg-blue-50 border border-blue-200 p-3">
|
||||
<div class="flex items-center gap-2">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="text-blue-600"
|
||||
>
|
||||
<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" />
|
||||
<polyline points="9 22 9 12 15 12 15 22" />
|
||||
</svg>
|
||||
<Home class="h-4 w-4 text-blue-600" />
|
||||
<div>
|
||||
<p class="text-sm font-medium text-blue-900">
|
||||
{companyStore.activeCompany.name}
|
||||
@@ -426,26 +413,7 @@
|
||||
</Button>
|
||||
<Button type="submit" disabled={loading}>
|
||||
{#if loading}
|
||||
<svg
|
||||
class="mr-2 h-4 w-4 animate-spin"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<circle
|
||||
class="opacity-25"
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="10"
|
||||
stroke="currentColor"
|
||||
stroke-width="4"
|
||||
></circle>
|
||||
<path
|
||||
class="opacity-75"
|
||||
fill="currentColor"
|
||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
||||
></path>
|
||||
</svg>
|
||||
<LoaderCircle class="mr-2 h-4 w-4 animate-spin" />
|
||||
Guardando...
|
||||
{:else}
|
||||
{isEdit ? 'Actualizar' : 'Crear'}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import * as DropdownMenu from "$lib/components/ui/dropdown-menu";
|
||||
import { classesApi, type A76Class } from "$lib/api/dashboard/a76/classes";
|
||||
import { companyStore } from "$lib/stores/company.svelte";
|
||||
import { EllipsisVertical, Pencil, LoaderCircle, Trash2 } from 'lucide-svelte';
|
||||
import CreateEditDialog from "./create-edit-dialog.svelte";
|
||||
|
||||
let {
|
||||
@@ -80,21 +81,7 @@
|
||||
{#snippet child({ props })}
|
||||
<Button {...props} variant="ghost" size="icon" class="relative size-8 p-0">
|
||||
<span class="sr-only">Abrir menú</span>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<circle cx="12" cy="12" r="1" />
|
||||
<circle cx="12" cy="5" r="1" />
|
||||
<circle cx="12" cy="19" r="1" />
|
||||
</svg>
|
||||
<EllipsisVertical size={16} />
|
||||
</Button>
|
||||
{/snippet}
|
||||
</DropdownMenu.Trigger>
|
||||
@@ -102,63 +89,15 @@
|
||||
<DropdownMenu.Label>Acciones</DropdownMenu.Label>
|
||||
<DropdownMenu.Separator />
|
||||
<DropdownMenu.Item onclick={handleEdit}>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="mr-2"
|
||||
>
|
||||
<path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z" />
|
||||
<path d="m15 5 4 4" />
|
||||
</svg>
|
||||
<Pencil size={16} class="mr-2" />
|
||||
Editar
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Separator />
|
||||
<DropdownMenu.Item onclick={handleDelete} class="text-destructive" disabled={loading}>
|
||||
{#if loading}
|
||||
<svg
|
||||
class="mr-2 h-4 w-4 animate-spin"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<circle
|
||||
class="opacity-25"
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="10"
|
||||
stroke="currentColor"
|
||||
stroke-width="4"
|
||||
></circle>
|
||||
<path
|
||||
class="opacity-75"
|
||||
fill="currentColor"
|
||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
||||
></path>
|
||||
</svg>
|
||||
<LoaderCircle class="mr-2 h-4 w-4 animate-spin" />
|
||||
{:else}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="mr-2"
|
||||
>
|
||||
<path d="M3 6h18" />
|
||||
<path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6" />
|
||||
<path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2" />
|
||||
</svg>
|
||||
<Trash2 size={16} class="mr-2" />
|
||||
{/if}
|
||||
Eliminar
|
||||
</DropdownMenu.Item>
|
||||
|
||||
@@ -56,10 +56,10 @@ export function createColumns(onSuccess?: () => void): ColumnDef<ClientProvider>
|
||||
const typeSnippet = createRawSnippet<[{ type: string | null | undefined }]>((getType) => {
|
||||
const { type } = getType();
|
||||
const displayType = type === 'client' ? 'Cliente' : type === 'provider' ? 'Proveedor' : type === 'both' ? 'Ambos' : 'N/A';
|
||||
const colorClass = type === 'client' ? 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-300'
|
||||
const colorClass = type === 'client' ? 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-300'
|
||||
: type === 'provider' ? 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300'
|
||||
: type === 'both' ? 'bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-300'
|
||||
: 'bg-gray-100 text-gray-800 dark:bg-gray-900 dark:text-gray-300';
|
||||
: type === 'both' ? 'bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-300'
|
||||
: 'bg-gray-100 text-gray-800 dark:bg-gray-900 dark:text-gray-300';
|
||||
return {
|
||||
render: () => `<span class="inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium ${colorClass}">${displayType}</span>`
|
||||
};
|
||||
@@ -81,21 +81,21 @@ export function createColumns(onSuccess?: () => void): ColumnDef<ClientProvider>
|
||||
}
|
||||
},
|
||||
{
|
||||
accessorKey: "enabled_disabled",
|
||||
accessorKey: "is_active",
|
||||
header: "Estado",
|
||||
cell: ({ row }) => {
|
||||
const statusSnippet = createRawSnippet<[{ status: number | undefined }]>((getStatus) => {
|
||||
const { status } = getStatus();
|
||||
const isEnabled = status === 1;
|
||||
const statusText = isEnabled ? 'Activo' : 'Inactivo';
|
||||
const colorClass = isEnabled
|
||||
? 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300'
|
||||
const colorClass = isEnabled
|
||||
? 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300'
|
||||
: 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-300';
|
||||
return {
|
||||
render: () => `<span class="inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium ${colorClass}">${statusText}</span>`
|
||||
};
|
||||
});
|
||||
return renderSnippet(statusSnippet, { status: row.original.enabled_disabled });
|
||||
return renderSnippet(statusSnippet, { status: row.original.is_active });
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import { Label } from "$lib/components/ui/label";
|
||||
import { clientsProvidersApi, type ClientProvider, type CreateClientProviderData, type UpdateClientProviderData } from "$lib/api/dashboard/a76/clients-providers";
|
||||
import { companyStore } from "$lib/stores/company.svelte";
|
||||
import { LoaderCircle } from 'lucide-svelte';
|
||||
|
||||
let {
|
||||
open = $bindable(false),
|
||||
@@ -24,7 +25,7 @@
|
||||
domicile_fiscal: "",
|
||||
foreign_tax_id: "",
|
||||
client_or_provider: "client",
|
||||
enabled_disabled: 1,
|
||||
is_active: true,
|
||||
// Address fields
|
||||
street: "",
|
||||
neighborhood: "",
|
||||
@@ -51,7 +52,7 @@
|
||||
domicile_fiscal: item.domicile_fiscal || "",
|
||||
foreign_tax_id: item.foreign_tax_id || "",
|
||||
client_or_provider: item.client_or_provider || "client",
|
||||
enabled_disabled: item.enabled_disabled ?? 1,
|
||||
is_active: item.is_active ?? true,
|
||||
street: item.address?.street || "",
|
||||
neighborhood: item.address?.neighborhood || "",
|
||||
city: item.address?.city || "",
|
||||
@@ -70,7 +71,7 @@
|
||||
domicile_fiscal: "",
|
||||
foreign_tax_id: "",
|
||||
client_or_provider: "client",
|
||||
enabled_disabled: 1,
|
||||
is_active: true,
|
||||
street: "",
|
||||
neighborhood: "",
|
||||
city: "",
|
||||
@@ -107,7 +108,7 @@
|
||||
domicile_fiscal: formData.domicile_fiscal || null,
|
||||
foreign_tax_id: formData.foreign_tax_id || null,
|
||||
client_or_provider: formData.client_or_provider as "client" | "provider" | "both" | null,
|
||||
enabled_disabled: formData.enabled_disabled,
|
||||
is_active: formData.is_active,
|
||||
address: {
|
||||
street: formData.street || null,
|
||||
neighborhood: formData.neighborhood || null,
|
||||
@@ -132,7 +133,7 @@
|
||||
domicile_fiscal: formData.domicile_fiscal || null,
|
||||
foreign_tax_id: formData.foreign_tax_id || null,
|
||||
client_or_provider: formData.client_or_provider as "client" | "provider" | "both" | null,
|
||||
enabled_disabled: formData.enabled_disabled,
|
||||
is_active: formData.is_active,
|
||||
address: {
|
||||
street: formData.street || null,
|
||||
neighborhood: formData.neighborhood || null,
|
||||
@@ -185,7 +186,7 @@
|
||||
domicile_fiscal: "",
|
||||
foreign_tax_id: "",
|
||||
client_or_provider: "client",
|
||||
enabled_disabled: 1,
|
||||
is_active: true,
|
||||
street: "",
|
||||
neighborhood: "",
|
||||
city: "",
|
||||
@@ -432,26 +433,7 @@
|
||||
</Button>
|
||||
<Button type="submit" disabled={loading}>
|
||||
{#if loading}
|
||||
<svg
|
||||
class="mr-2 h-4 w-4 animate-spin"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<circle
|
||||
class="opacity-25"
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="10"
|
||||
stroke="currentColor"
|
||||
stroke-width="4"
|
||||
></circle>
|
||||
<path
|
||||
class="opacity-75"
|
||||
fill="currentColor"
|
||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
||||
></path>
|
||||
</svg>
|
||||
<LoaderCircle class="mr-2 h-4 w-4 animate-spin" />
|
||||
{/if}
|
||||
{isEditing ? "Guardar cambios" : "Crear"}
|
||||
</Button>
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
<DropdownMenu.Item onclick={handleViewDetails}>Ver detalles</DropdownMenu.Item>
|
||||
<DropdownMenu.Item onclick={handleEdit}>Editar</DropdownMenu.Item>
|
||||
<DropdownMenu.Item onclick={handleToggleStatus} disabled={isToggling}>
|
||||
{isToggling ? 'Cambiando...' : item.enabled_disabled === 1 ? 'Desactivar' : 'Activar'}
|
||||
{isToggling ? 'Cambiando...' : item.is_active === true ? 'Desactivar' : 'Activar'}
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Separator />
|
||||
<DropdownMenu.Item class="text-destructive" onclick={handleDelete}>Eliminar</DropdownMenu.Item>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import * as AlertDialog from "$lib/components/ui/alert-dialog";
|
||||
import { clientsProvidersApi, type ClientProvider } from "$lib/api/dashboard/a76/clients-providers";
|
||||
import { companyStore } from "$lib/stores/company.svelte";
|
||||
import { LoaderCircle } from 'lucide-svelte';
|
||||
|
||||
let {
|
||||
open = $bindable(false),
|
||||
@@ -97,26 +98,7 @@
|
||||
class="bg-destructive text-destructive-foreground hover:bg-destructive/90"
|
||||
>
|
||||
{#if loading}
|
||||
<svg
|
||||
class="mr-2 h-4 w-4 animate-spin"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<circle
|
||||
class="opacity-25"
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="10"
|
||||
stroke="currentColor"
|
||||
stroke-width="4"
|
||||
></circle>
|
||||
<path
|
||||
class="opacity-75"
|
||||
fill="currentColor"
|
||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
||||
></path>
|
||||
</svg>
|
||||
<LoaderCircle class="mr-2 h-4 w-4 animate-spin" />
|
||||
{/if}
|
||||
Eliminar
|
||||
</AlertDialog.Action>
|
||||
|
||||
@@ -85,7 +85,7 @@
|
||||
|
||||
<div class="flex flex-col gap-1">
|
||||
<span class="text-xs font-medium text-muted-foreground">Estado</span>
|
||||
{#if item.enabled_disabled === 1}
|
||||
{#if item.is_active === true}
|
||||
<span class="inline-flex w-fit items-center rounded-full bg-green-100 px-2.5 py-0.5 text-xs font-medium text-green-800 dark:bg-green-900 dark:text-green-300">
|
||||
Activo
|
||||
</span>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import type { CustomsBroker } from "./columns.js";
|
||||
import DetailsDialog from "./details-dialog.svelte";
|
||||
import DeleteDialog from "./delete-dialog.svelte";
|
||||
import EditDialog from "./edit-dialog.svelte";
|
||||
|
||||
let {
|
||||
broker,
|
||||
@@ -16,6 +17,7 @@
|
||||
|
||||
let showDetailsDialog = $state(false);
|
||||
let showDeleteDialog = $state(false);
|
||||
let showEditDialog = $state(false);
|
||||
|
||||
function handleCopyKey() {
|
||||
navigator.clipboard.writeText(broker.broker_key);
|
||||
@@ -31,6 +33,10 @@
|
||||
showDetailsDialog = true;
|
||||
}
|
||||
|
||||
function handleEdit() {
|
||||
showEditDialog = true;
|
||||
}
|
||||
|
||||
function handleDelete() {
|
||||
showDeleteDialog = true;
|
||||
}
|
||||
@@ -64,6 +70,9 @@
|
||||
<DropdownMenu.Item onclick={handleViewDetails}>
|
||||
Ver detalles
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item onclick={handleEdit}>
|
||||
Editar
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item onclick={handleDelete} class="text-destructive">
|
||||
Eliminar
|
||||
</DropdownMenu.Item>
|
||||
@@ -72,4 +81,5 @@
|
||||
</DropdownMenu.Root>
|
||||
|
||||
<DetailsDialog bind:open={showDetailsDialog} {broker} />
|
||||
<EditDialog bind:open={showEditDialog} {broker} {onSuccess} />
|
||||
<DeleteDialog bind:open={showDeleteDialog} {broker} {onSuccess} />
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { Button } from "$lib/components/ui/button";
|
||||
import * as AlertDialog from "$lib/components/ui/alert-dialog";
|
||||
import { customsBrokersApi, type CustomsBroker } from "$lib/api/dashboard/a76/customs-brokers";
|
||||
import { LoaderCircle } from 'lucide-svelte';
|
||||
|
||||
let {
|
||||
open = $bindable(false),
|
||||
@@ -92,26 +93,7 @@
|
||||
class="bg-destructive text-destructive-foreground hover:bg-destructive/90"
|
||||
>
|
||||
{#if loading}
|
||||
<svg
|
||||
class="mr-2 h-4 w-4 animate-spin"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<circle
|
||||
class="opacity-25"
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="10"
|
||||
stroke="currentColor"
|
||||
stroke-width="4"
|
||||
></circle>
|
||||
<path
|
||||
class="opacity-75"
|
||||
fill="currentColor"
|
||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
||||
></path>
|
||||
</svg>
|
||||
<LoaderCircle class="mr-2 h-4 w-4 animate-spin" />
|
||||
Eliminando...
|
||||
{:else}
|
||||
Eliminar
|
||||
|
||||
@@ -0,0 +1,384 @@
|
||||
<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";
|
||||
|
||||
let {
|
||||
open = $bindable(false),
|
||||
broker,
|
||||
onSuccess
|
||||
}: {
|
||||
open: boolean;
|
||||
broker: CustomsBroker;
|
||||
onSuccess?: () => void;
|
||||
} = $props();
|
||||
|
||||
let formData = $state({
|
||||
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);
|
||||
|
||||
// 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 || ""
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
async function handleSubmit(e: Event) {
|
||||
e.preventDefault();
|
||||
|
||||
if (!companyStore.activeCompany) {
|
||||
error = "No hay compañía seleccionada";
|
||||
return;
|
||||
}
|
||||
|
||||
loading = true;
|
||||
error = null;
|
||||
|
||||
try {
|
||||
const payload: CreateCustomsBrokerData = {
|
||||
broker_key: broker.broker_key, // La clave no se edita
|
||||
name: formData.name || null,
|
||||
type: formData.type || null,
|
||||
address: formData.address || null,
|
||||
postal_code: formData.postal_code || null,
|
||||
city: formData.city || null,
|
||||
state: formData.state || null,
|
||||
phone: formData.phone || null,
|
||||
fax: formData.fax || null,
|
||||
email: formData.email || null,
|
||||
country: formData.country || null,
|
||||
tax_id: formData.tax_id || null,
|
||||
personal_id: formData.personal_id || null,
|
||||
position: formData.position || null,
|
||||
license: formData.license || null,
|
||||
company: formData.company || null,
|
||||
contact: formData.contact || null,
|
||||
tenant_id: broker.tenant_id,
|
||||
company_id: companyStore.activeCompany.id.toString()
|
||||
};
|
||||
|
||||
const response = await customsBrokersApi.update(broker.broker_key, payload);
|
||||
|
||||
if (response.error) {
|
||||
if (response.status === 401) {
|
||||
error = 'Sesión expirada. Recargando página...';
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 1500);
|
||||
} else {
|
||||
error = response.error;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Éxito
|
||||
open = false;
|
||||
if (onSuccess) {
|
||||
onSuccess();
|
||||
}
|
||||
} catch (e) {
|
||||
error = e instanceof Error ? e.message : "Error al guardar";
|
||||
console.error("Error updating:", e);
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
function handleOpenChange(newOpen: boolean) {
|
||||
open = newOpen;
|
||||
if (!newOpen) {
|
||||
error = null;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<Dialog.Root bind:open onOpenChange={handleOpenChange}>
|
||||
<Dialog.Content class="sm:max-w-[700px] max-h-[90vh] overflow-y-auto">
|
||||
<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>.
|
||||
</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">
|
||||
{error}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- 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>
|
||||
<Input
|
||||
id="edit-broker_key"
|
||||
value={broker?.broker_key}
|
||||
disabled
|
||||
class="bg-muted"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label for="edit-type">Tipo</Label>
|
||||
<Input
|
||||
id="edit-type"
|
||||
bind:value={formData.type}
|
||||
placeholder="Tipo de agente"
|
||||
maxlength={9}
|
||||
disabled={loading}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label for="edit-name">Nombre</Label>
|
||||
<Input
|
||||
id="edit-name"
|
||||
bind:value={formData.name}
|
||||
placeholder="Nombre del agente aduanal"
|
||||
maxlength={80}
|
||||
disabled={loading}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="edit-license">Patente</Label>
|
||||
<Input
|
||||
id="edit-license"
|
||||
bind:value={formData.license}
|
||||
placeholder="Número de patente"
|
||||
maxlength={4}
|
||||
disabled={loading}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label for="edit-company">Empresa</Label>
|
||||
<Input
|
||||
id="edit-company"
|
||||
bind:value={formData.company}
|
||||
placeholder="Empresa del agente"
|
||||
maxlength={200}
|
||||
disabled={loading}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 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>
|
||||
<Input
|
||||
id="edit-phone"
|
||||
bind:value={formData.phone}
|
||||
placeholder="Número telefónico"
|
||||
maxlength={30}
|
||||
disabled={loading}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label for="edit-fax">Fax</Label>
|
||||
<Input
|
||||
id="edit-fax"
|
||||
bind:value={formData.fax}
|
||||
placeholder="Número de fax"
|
||||
maxlength={30}
|
||||
disabled={loading}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label for="edit-email">Email</Label>
|
||||
<Input
|
||||
id="edit-email"
|
||||
type="email"
|
||||
bind:value={formData.email}
|
||||
placeholder="correo@ejemplo.com"
|
||||
maxlength={100}
|
||||
disabled={loading}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label for="edit-contact">Contacto</Label>
|
||||
<Input
|
||||
id="edit-contact"
|
||||
bind:value={formData.contact}
|
||||
placeholder="Nombre del contacto"
|
||||
maxlength={80}
|
||||
disabled={loading}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 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
|
||||
id="edit-address"
|
||||
bind:value={formData.address}
|
||||
placeholder="Calle y número"
|
||||
maxlength={1500}
|
||||
disabled={loading}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-3 gap-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="edit-postal_code">Código Postal</Label>
|
||||
<Input
|
||||
id="edit-postal_code"
|
||||
bind:value={formData.postal_code}
|
||||
placeholder="C.P."
|
||||
maxlength={15}
|
||||
disabled={loading}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label for="edit-city">Ciudad</Label>
|
||||
<Input
|
||||
id="edit-city"
|
||||
bind:value={formData.city}
|
||||
placeholder="Ciudad"
|
||||
maxlength={30}
|
||||
disabled={loading}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label for="edit-state">Estado</Label>
|
||||
<Input
|
||||
id="edit-state"
|
||||
bind:value={formData.state}
|
||||
placeholder="Estado"
|
||||
maxlength={30}
|
||||
disabled={loading}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label for="edit-country">País</Label>
|
||||
<Input
|
||||
id="edit-country"
|
||||
bind:value={formData.country}
|
||||
placeholder="País"
|
||||
maxlength={3}
|
||||
disabled={loading}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 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>
|
||||
<Input
|
||||
id="edit-tax_id"
|
||||
bind:value={formData.tax_id}
|
||||
placeholder="RFC"
|
||||
maxlength={30}
|
||||
disabled={loading}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label for="edit-personal_id">CURP</Label>
|
||||
<Input
|
||||
id="edit-personal_id"
|
||||
bind:value={formData.personal_id}
|
||||
placeholder="CURP"
|
||||
maxlength={20}
|
||||
disabled={loading}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label for="edit-position">Posición</Label>
|
||||
<Input
|
||||
id="edit-position"
|
||||
bind:value={formData.position}
|
||||
placeholder="Cargo o posición"
|
||||
maxlength={30}
|
||||
disabled={loading}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Dialog.Footer>
|
||||
<Button type="button" variant="outline" onclick={() => (open = false)} disabled={loading}>
|
||||
Cancelar
|
||||
</Button>
|
||||
<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>
|
||||
Guardando...
|
||||
</div>
|
||||
{:else}
|
||||
Guardar Cambios
|
||||
{/if}
|
||||
</Button>
|
||||
</Dialog.Footer>
|
||||
</form>
|
||||
</Dialog.Content>
|
||||
</Dialog.Root>
|
||||
@@ -2,6 +2,7 @@
|
||||
import { Button } from "$lib/components/ui/button";
|
||||
import * as DropdownMenu from "$lib/components/ui/dropdown-menu";
|
||||
import { pedimentosApi, type Pedimento } from "$lib/api/dashboard/a76/pedimentos";
|
||||
import { EllipsisVertical, Pencil, LoaderCircle, Trash2 } from 'lucide-svelte';
|
||||
|
||||
let {
|
||||
item,
|
||||
@@ -62,21 +63,7 @@
|
||||
{#snippet child({ props })}
|
||||
<Button {...props} variant="ghost" size="icon" class="relative size-8 p-0">
|
||||
<span class="sr-only">Abrir menú</span>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<circle cx="12" cy="12" r="1" />
|
||||
<circle cx="12" cy="5" r="1" />
|
||||
<circle cx="12" cy="19" r="1" />
|
||||
</svg>
|
||||
<EllipsisVertical class="h-4 w-4" />
|
||||
</Button>
|
||||
{/snippet}
|
||||
</DropdownMenu.Trigger>
|
||||
@@ -84,63 +71,15 @@
|
||||
<DropdownMenu.Label>Acciones</DropdownMenu.Label>
|
||||
<DropdownMenu.Separator />
|
||||
<DropdownMenu.Item onclick={handleEdit}>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="mr-2"
|
||||
>
|
||||
<path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z" />
|
||||
<path d="m15 5 4 4" />
|
||||
</svg>
|
||||
<Pencil class="mr-2 h-4 w-4" />
|
||||
Editar
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Separator />
|
||||
<DropdownMenu.Item onclick={handleDelete} class="text-destructive" disabled={loading}>
|
||||
{#if loading}
|
||||
<svg
|
||||
class="mr-2 h-4 w-4 animate-spin"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<circle
|
||||
class="opacity-25"
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="10"
|
||||
stroke="currentColor"
|
||||
stroke-width="4"
|
||||
></circle>
|
||||
<path
|
||||
class="opacity-75"
|
||||
fill="currentColor"
|
||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
||||
></path>
|
||||
</svg>
|
||||
<LoaderCircle class="mr-2 h-4 w-4 animate-spin" />
|
||||
{:else}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="mr-2"
|
||||
>
|
||||
<path d="M3 6h18" />
|
||||
<path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6" />
|
||||
<path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2" />
|
||||
</svg>
|
||||
<Trash2 class="mr-2 h-4 w-4" />
|
||||
{/if}
|
||||
Eliminar
|
||||
</DropdownMenu.Item>
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { Button } from "$lib/components/ui/button";
|
||||
import * as AlertDialog from "$lib/components/ui/alert-dialog";
|
||||
import { codePedimentoRegimensApi, type CodePedimentoRegimen } from "$lib/api/dashboard/refrence_data/code_pedimento_regimens";
|
||||
import { LoaderCircle } from 'lucide-svelte';
|
||||
|
||||
let {
|
||||
open = $bindable(false),
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { Button } from "$lib/components/ui/button";
|
||||
import * as AlertDialog from "$lib/components/ui/alert-dialog";
|
||||
import { containersApi, type Container } from "$lib/api/dashboard/refrence_data/containers";
|
||||
import { LoaderCircle } from 'lucide-svelte';
|
||||
|
||||
let {
|
||||
open = $bindable(false),
|
||||
@@ -84,26 +85,7 @@
|
||||
class="bg-destructive text-destructive-foreground hover:bg-destructive/90"
|
||||
>
|
||||
{#if loading}
|
||||
<svg
|
||||
class="mr-2 h-4 w-4 animate-spin"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<circle
|
||||
class="opacity-25"
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="10"
|
||||
stroke="currentColor"
|
||||
stroke-width="4"
|
||||
></circle>
|
||||
<path
|
||||
class="opacity-75"
|
||||
fill="currentColor"
|
||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
||||
></path>
|
||||
</svg>
|
||||
<LoaderCircle class="mr-2 h-4 w-4 animate-spin" />
|
||||
{/if}
|
||||
Eliminar
|
||||
</AlertDialog.Action>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { Button } from "$lib/components/ui/button";
|
||||
import * as AlertDialog from "$lib/components/ui/alert-dialog";
|
||||
import { customsSectionsApi, type CustomsSection } from "$lib/api/dashboard/refrence_data/customs_sections";
|
||||
import { LoaderCircle } from 'lucide-svelte';
|
||||
|
||||
let {
|
||||
open = $bindable(false),
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { Button } from "$lib/components/ui/button";
|
||||
import * as AlertDialog from "$lib/components/ui/alert-dialog";
|
||||
import { customsWarehousesApi, type CustomsWarehouse } from "$lib/api/dashboard/refrence_data/customs_warehouses";
|
||||
import { LoaderCircle } from 'lucide-svelte';
|
||||
|
||||
let {
|
||||
open = $bindable(false),
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { Button } from "$lib/components/ui/button";
|
||||
import * as AlertDialog from "$lib/components/ui/alert-dialog";
|
||||
import { materialTypesApi, type MaterialType } from "$lib/api/dashboard/refrence_data/material_types";
|
||||
import { LoaderCircle } from 'lucide-svelte';
|
||||
|
||||
let {
|
||||
open = $bindable(false),
|
||||
@@ -88,26 +89,7 @@
|
||||
class="bg-destructive text-destructive-foreground hover:bg-destructive/90"
|
||||
>
|
||||
{#if loading}
|
||||
<svg
|
||||
class="mr-2 h-4 w-4 animate-spin"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<circle
|
||||
class="opacity-25"
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="10"
|
||||
stroke="currentColor"
|
||||
stroke-width="4"
|
||||
></circle>
|
||||
<path
|
||||
class="opacity-75"
|
||||
fill="currentColor"
|
||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
||||
></path>
|
||||
</svg>
|
||||
<LoaderCircle class="mr-2 h-4 w-4 animate-spin" />
|
||||
{/if}
|
||||
Eliminar
|
||||
</AlertDialog.Action>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { Button } from "$lib/components/ui/button";
|
||||
import * as AlertDialog from "$lib/components/ui/alert-dialog";
|
||||
import { paymentMethodsApi, type PaymentMethod } from "$lib/api/dashboard/refrence_data/payment_methods";
|
||||
import { LoaderCircle } from 'lucide-svelte';
|
||||
|
||||
let {
|
||||
open = $bindable(false),
|
||||
@@ -84,26 +85,7 @@
|
||||
class="bg-destructive text-destructive-foreground hover:bg-destructive/90"
|
||||
>
|
||||
{#if loading}
|
||||
<svg
|
||||
class="mr-2 h-4 w-4 animate-spin"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<circle
|
||||
class="opacity-25"
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="10"
|
||||
stroke="currentColor"
|
||||
stroke-width="4"
|
||||
></circle>
|
||||
<path
|
||||
class="opacity-75"
|
||||
fill="currentColor"
|
||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
||||
></path>
|
||||
</svg>
|
||||
<LoaderCircle class="mr-2 h-4 w-4 animate-spin" />
|
||||
{/if}
|
||||
Eliminar
|
||||
</AlertDialog.Action>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { Button } from "$lib/components/ui/button";
|
||||
import * as AlertDialog from "$lib/components/ui/alert-dialog";
|
||||
import { pedimentoRegimensApi, type PedimentoRegimen } from "$lib/api/dashboard/refrence_data/pedimento_regimens";
|
||||
import { LoaderCircle } from 'lucide-svelte';
|
||||
|
||||
let {
|
||||
open = $bindable(false),
|
||||
@@ -84,26 +85,7 @@
|
||||
class="bg-destructive text-destructive-foreground hover:bg-destructive/90"
|
||||
>
|
||||
{#if loading}
|
||||
<svg
|
||||
class="mr-2 h-4 w-4 animate-spin"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<circle
|
||||
class="opacity-25"
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="10"
|
||||
stroke="currentColor"
|
||||
stroke-width="4"
|
||||
></circle>
|
||||
<path
|
||||
class="opacity-75"
|
||||
fill="currentColor"
|
||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
||||
></path>
|
||||
</svg>
|
||||
<LoaderCircle class="mr-2 h-4 w-4 animate-spin" />
|
||||
{/if}
|
||||
Eliminar
|
||||
</AlertDialog.Action>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import { Input } from "$lib/components/ui/input";
|
||||
import { Label } from "$lib/components/ui/label";
|
||||
import { statesApi, type State, type CreateStateData, type UpdateStateData } from "$lib/api/dashboard/refrence_data/states";
|
||||
import { LoaderCircle } from 'lucide-svelte';
|
||||
|
||||
let {
|
||||
open = $bindable(false),
|
||||
@@ -195,26 +196,7 @@
|
||||
</Button>
|
||||
<Button type="submit" disabled={loading}>
|
||||
{#if loading}
|
||||
<svg
|
||||
class="mr-2 h-4 w-4 animate-spin"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<circle
|
||||
class="opacity-25"
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="10"
|
||||
stroke="currentColor"
|
||||
stroke-width="4"
|
||||
></circle>
|
||||
<path
|
||||
class="opacity-75"
|
||||
fill="currentColor"
|
||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
||||
></path>
|
||||
</svg>
|
||||
<LoaderCircle class="mr-2 h-4 w-4 animate-spin" />
|
||||
{/if}
|
||||
{isEditing ? "Guardar cambios" : "Crear"}
|
||||
</Button>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { Button } from "$lib/components/ui/button";
|
||||
import * as AlertDialog from "$lib/components/ui/alert-dialog";
|
||||
import { statesApi, type State } from "$lib/api/dashboard/refrence_data/states";
|
||||
import { LoaderCircle } from 'lucide-svelte';
|
||||
|
||||
let {
|
||||
open = $bindable(false),
|
||||
@@ -96,26 +97,7 @@
|
||||
class="bg-destructive text-destructive-foreground hover:bg-destructive/90"
|
||||
>
|
||||
{#if loading}
|
||||
<svg
|
||||
class="mr-2 h-4 w-4 animate-spin"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<circle
|
||||
class="opacity-25"
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="10"
|
||||
stroke="currentColor"
|
||||
stroke-width="4"
|
||||
></circle>
|
||||
<path
|
||||
class="opacity-75"
|
||||
fill="currentColor"
|
||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
||||
></path>
|
||||
</svg>
|
||||
<LoaderCircle class="mr-2 h-4 w-4 animate-spin" />
|
||||
{/if}
|
||||
Eliminar
|
||||
</AlertDialog.Action>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { Button } from "$lib/components/ui/button";
|
||||
import * as AlertDialog from "$lib/components/ui/alert-dialog";
|
||||
import { transportModesApi, type TransportMode } from "$lib/api/dashboard/refrence_data/transport_modes";
|
||||
import { LoaderCircle } from 'lucide-svelte';
|
||||
|
||||
let {
|
||||
open = $bindable(false),
|
||||
@@ -84,26 +85,7 @@
|
||||
class="bg-destructive text-destructive-foreground hover:bg-destructive/90"
|
||||
>
|
||||
{#if loading}
|
||||
<svg
|
||||
class="mr-2 h-4 w-4 animate-spin"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<circle
|
||||
class="opacity-25"
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="10"
|
||||
stroke="currentColor"
|
||||
stroke-width="4"
|
||||
></circle>
|
||||
<path
|
||||
class="opacity-75"
|
||||
fill="currentColor"
|
||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
||||
></path>
|
||||
</svg>
|
||||
<LoaderCircle class="mr-2 h-4 w-4 animate-spin" />
|
||||
{/if}
|
||||
Eliminar
|
||||
</AlertDialog.Action>
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
import { Input } from "$lib/components/ui/input/index.js";
|
||||
import { Button } from "$lib/components/ui/button/index.js";
|
||||
import { cn } from "$lib/utils.js";
|
||||
import { FileText, ShieldCheck } from 'lucide-svelte';
|
||||
import type { HTMLAttributes } from "svelte/elements";
|
||||
import { page } from '$app/state';
|
||||
import { enhance } from '$app/forms';
|
||||
@@ -203,14 +204,10 @@
|
||||
<div class="relative w-full h-full flex flex-col items-center justify-center gap-8">
|
||||
<!-- Ícono principal - Escudo con documento -->
|
||||
<div class="relative">
|
||||
<svg class="w-48 h-48 text-blue-600 dark:text-blue-400 opacity-90" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
|
||||
</svg>
|
||||
<FileText class="w-48 h-48 text-blue-600 dark:text-blue-400 opacity-90" strokeWidth={1.5} />
|
||||
<!-- Escudo de protección superpuesto -->
|
||||
<div class="absolute -bottom-2 -right-2 bg-white dark:bg-slate-800 rounded-full p-2 shadow-lg">
|
||||
<svg class="w-12 h-12 text-green-600 dark:text-green-400" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" d="M2.166 4.999A11.954 11.954 0 0010 1.944 11.954 11.954 0 0017.834 5c.11.65.166 1.32.166 2.001 0 5.225-3.34 9.67-8 11.317C5.34 16.67 2 12.225 2 7c0-.682.057-1.35.166-2.001zm11.541 3.708a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
<ShieldCheck class="w-12 h-12 text-green-600 dark:text-green-400" fill="currentColor" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user