diff --git a/frontend/src/lib/components/dashboard/common/infinite-data-table.svelte b/frontend/src/lib/components/dashboard/common/infinite-data-table.svelte
index 3449e32e..598dffe3 100644
--- a/frontend/src/lib/components/dashboard/common/infinite-data-table.svelte
+++ b/frontend/src/lib/components/dashboard/common/infinite-data-table.svelte
@@ -11,8 +11,9 @@
hasMore: boolean;
loadMore: () => void;
emptyMessage?: string;
- selectedIds?: number[];
- onSelectedIdsChange?: (ids: number[]) => void;
+ selectedIds?: (number | string)[];
+ onSelectedIdsChange?: (ids: (number | string)[]) => void;
+ getRowId?: (row: TData) => string | number;
onRowClick?: (row: TData) => void;
onRowDoubleClick?: (row: TData) => void;
};
@@ -26,6 +27,7 @@
emptyMessage = 'No hay resultados.',
selectedIds = [],
onSelectedIdsChange,
+ getRowId,
onRowClick,
onRowDoubleClick,
}: InfiniteDataTableProps = $props();
@@ -36,7 +38,7 @@
},
columns,
getCoreRowModel: getCoreRowModel(),
- getRowId: (row: any) => row.id?.toString(),
+ getRowId: (row: any) => (getRowId ? getRowId(row).toString() : (row.id || row.transporter_key || row.driver_id || row.trailer_number || row.vehicle_key || '').toString()),
state: {
get rowSelection() {
const selection: Record = {};
@@ -60,8 +62,7 @@
const nextSelectedIds = Object.entries(rowSelection)
.filter(([, selected]) => Boolean(selected))
- .map(([id]) => Number(id))
- .filter((id) => Number.isFinite(id));
+ .map(([id]) => (isNaN(Number(id)) ? id : Number(id)));
onSelectedIdsChange(nextSelectedIds);
},
@@ -189,6 +190,7 @@
.filter(Boolean)
.join(' ')}
onclick={() => onRowClick?.(row.original)}
+ ondblclick={() => onRowDoubleClick?.(row.original)}
>
{#each visibleCells as cell (cell.id)}
{@const colId = cell.column.id}
diff --git a/frontend/src/lib/components/dashboard/transportation/drivers/columns.ts b/frontend/src/lib/components/dashboard/transportation/drivers/columns.ts
index 35bc7c79..655d4808 100644
--- a/frontend/src/lib/components/dashboard/transportation/drivers/columns.ts
+++ b/frontend/src/lib/components/dashboard/transportation/drivers/columns.ts
@@ -37,16 +37,6 @@ export function createColumns(onSuccess?: () => void): ColumnDef[] {
accessorKey: 'last_name',
header: 'Apellido',
cell: ({ row }) => row.original.last_name || '-'
- },
- {
- id: 'actions',
- header: 'Acciones',
- cell: ({ row }) => {
- return renderComponent(DataTableActions, {
- item: row.original,
- onSuccess
- });
- }
}
];
}
diff --git a/frontend/src/lib/components/dashboard/transportation/trailers/columns.ts b/frontend/src/lib/components/dashboard/transportation/trailers/columns.ts
index e00011c6..50eb1231 100644
--- a/frontend/src/lib/components/dashboard/transportation/trailers/columns.ts
+++ b/frontend/src/lib/components/dashboard/transportation/trailers/columns.ts
@@ -50,15 +50,5 @@ export function createColumns(onSuccess?: () => void): ColumnDef[] {
return row.original.country || '-';
}
},
- {
- id: 'actions',
- header: 'Acciones',
- cell: ({ row }) => {
- return renderComponent(DataTableActions, {
- item: row.original,
- onSuccess
- });
- }
- }
];
}
diff --git a/frontend/src/lib/components/dashboard/transportation/trailers/create-edit-dialog.svelte b/frontend/src/lib/components/dashboard/transportation/trailers/create-edit-dialog.svelte
index c2ecb42b..0bbb617d 100644
--- a/frontend/src/lib/components/dashboard/transportation/trailers/create-edit-dialog.svelte
+++ b/frontend/src/lib/components/dashboard/transportation/trailers/create-edit-dialog.svelte
@@ -160,11 +160,10 @@
void handleSubmit();
}
- onMount(() => {
+ $effect(() => {
+ if (!browser) return;
document.addEventListener('save-form', onSaveFormEvent);
- });
- onDestroy(() => {
- document.removeEventListener('save-form', onSaveFormEvent);
+ return () => document.removeEventListener('save-form', onSaveFormEvent);
});
diff --git a/frontend/src/lib/components/dashboard/transportation/transporters/create-edit-dialog.svelte b/frontend/src/lib/components/dashboard/transportation/transporters/create-edit-dialog.svelte
index a39db20b..81d953b1 100644
--- a/frontend/src/lib/components/dashboard/transportation/transporters/create-edit-dialog.svelte
+++ b/frontend/src/lib/components/dashboard/transportation/transporters/create-edit-dialog.svelte
@@ -169,11 +169,10 @@
void handleSubmit();
}
- onMount(() => {
+ $effect(() => {
+ if (!browser) return;
document.addEventListener('save-form', onSaveFormEvent);
- });
- onDestroy(() => {
- document.removeEventListener('save-form', onSaveFormEvent);
+ return () => document.removeEventListener('save-form', onSaveFormEvent);
});
diff --git a/frontend/src/lib/components/dashboard/transportation/transporters/transporter-columns.ts b/frontend/src/lib/components/dashboard/transportation/transporters/transporter-columns.ts
index 32e69333..e457eaf7 100644
--- a/frontend/src/lib/components/dashboard/transportation/transporters/transporter-columns.ts
+++ b/frontend/src/lib/components/dashboard/transportation/transporters/transporter-columns.ts
@@ -50,15 +50,5 @@ export function createColumns(onSuccess?: () => void): ColumnDef[]
return row.original.transport_code || '-';
}
},
- {
- id: 'actions',
- header: 'Acciones',
- cell: ({ row }) => {
- return renderComponent(DataTableActions, {
- item: row.original,
- onSuccess
- });
- }
- }
];
}
diff --git a/frontend/src/lib/components/dashboard/transportation/vehicles/columns.ts b/frontend/src/lib/components/dashboard/transportation/vehicles/columns.ts
index 2bacea83..34b85486 100644
--- a/frontend/src/lib/components/dashboard/transportation/vehicles/columns.ts
+++ b/frontend/src/lib/components/dashboard/transportation/vehicles/columns.ts
@@ -50,15 +50,5 @@ export function createColumns(onSuccess?: () => void): ColumnDef[] {
return row.original.transport_type || '-';
}
},
- {
- id: 'actions',
- header: 'Acciones',
- cell: ({ row }) => {
- return renderComponent(DataTableActions, {
- item: row.original,
- onSuccess
- });
- }
- }
];
}
diff --git a/frontend/src/lib/components/dashboard/transportation/vehicles/create-edit-dialog.svelte b/frontend/src/lib/components/dashboard/transportation/vehicles/create-edit-dialog.svelte
index 1080828b..e288a1a4 100644
--- a/frontend/src/lib/components/dashboard/transportation/vehicles/create-edit-dialog.svelte
+++ b/frontend/src/lib/components/dashboard/transportation/vehicles/create-edit-dialog.svelte
@@ -11,7 +11,6 @@
import { statesApi, type State } from '$lib/api/dashboard/reference_data/states';
import { companyStore } from '$lib/stores/company.svelte';
import { browser } from '$app/environment';
- import { onMount, onDestroy } from 'svelte';
let {
open = $bindable(false),
@@ -202,11 +201,10 @@
void handleSubmit();
}
- onMount(() => {
+ $effect(() => {
+ if (!browser) return;
document.addEventListener('save-form', onSaveFormEvent);
- });
- onDestroy(() => {
- document.removeEventListener('save-form', onSaveFormEvent);
+ return () => document.removeEventListener('save-form', onSaveFormEvent);
});
diff --git a/frontend/src/routes/dashboard/general_catalogs/drivers/+page.svelte b/frontend/src/routes/dashboard/general_catalogs/drivers/+page.svelte
index 8f19bbcd..b0214aa2 100644
--- a/frontend/src/routes/dashboard/general_catalogs/drivers/+page.svelte
+++ b/frontend/src/routes/dashboard/general_catalogs/drivers/+page.svelte
@@ -2,7 +2,7 @@
import * as Card from '$lib/components/ui/card';
import { Button } from '$lib/components/ui/button';
import { Input } from '$lib/components/ui/input';
- import { Plus, RefreshCw } from 'lucide-svelte';
+ import { Plus, RefreshCw, Pencil, Trash2 } from 'lucide-svelte';
import InfiniteDataTable from '$lib/components/dashboard/common/infinite-data-table.svelte';
import CreateEditDialog from '$lib/components/dashboard/transportation/drivers/create-edit-dialog.svelte';
@@ -19,6 +19,12 @@
let currentPage = $state(1);
let pageSize = $state(50);
let createDialogOpen = $state(false);
+ let editingItem = $state(null);
+ let selectedIds = $state<(string | number)[]>([]);
+ const selectedDriver = $derived(
+ selectedIds.length === 1 ? data.find((d) => (d.transporter_key + '-' + d.line) === selectedIds[0]) : null
+ );
+ const hasSelection = $derived(selectedIds.length > 0);
let searchTransporterKey = $state('');
let searchDriverName = $state('');
@@ -75,9 +81,70 @@
}
}
+ function handleDialogSuccess() {
+ createDialogOpen = false;
+ editingItem = null;
+ loadData();
+ }
+
+ function handleRowDoubleClick(row: Driver) {
+ editingItem = row;
+ createDialogOpen = true;
+ }
+
+ function handleRowClick(row: Driver) {
+ const id = row.transporter_key + '-' + row.line;
+ if (selectedIds.includes(id)) {
+ selectedIds = selectedIds.filter((i) => i !== id);
+ } else {
+ selectedIds = [id];
+ }
+ }
+
+ function handleEditSelected() {
+ if (selectedDriver) {
+ editingItem = selectedDriver;
+ createDialogOpen = true;
+ }
+ }
+
+ async function handleDeleteSelected() {
+ if (!selectedDriver) return;
+ if (
+ !confirm(
+ `¿Estás seguro de eliminar el conductor "${selectedDriver.driver_name || selectedDriver.transporter_key + '-' + selectedDriver.line}"?\n\nNota: No se puede eliminar si tiene registros relacionados.`
+ )
+ ) {
+ return;
+ }
+
+ if (!companyStore.activeCompany) return;
+
+ try {
+ const response = await driversApi.delete(
+ selectedDriver.transporter_key,
+ selectedDriver.line,
+ companyStore.activeCompany.id
+ );
+ if (response.error) {
+ alert(`❌ Error al eliminar:\n\n${response.error}`);
+ } else {
+ alert(`✅ Conductor eliminado correctamente`);
+ selectedIds = [];
+ loadData();
+ }
+ } catch (e) {
+ console.error('Error deleting:', e);
+ }
+ }
+
$effect(() => {
const _c = companyStore.activeCompany?.id;
loadData();
+
+ if (!createDialogOpen) {
+ editingItem = null;
+ }
});
useShortcuts('Conductores', [
@@ -106,15 +173,47 @@
Conductores
Gestión del catálogo de conductores
-
+
- {#if loading && data.length === 0}Cargando conductores...
{:else}
{/if}
+ {#if loading && data.length === 0}Cargando conductores...
{:else} (selectedIds = ids)} onRowClick={handleRowClick} onRowDoubleClick={handleRowDoubleClick} getRowId={(row) => row.transporter_key + '-' + row.line} />
{/if}
Mostrando {filteredData.length} de {totalItems} registros
-
+
+
+
+
+
+
diff --git a/frontend/src/routes/dashboard/general_catalogs/trailers/+page.svelte b/frontend/src/routes/dashboard/general_catalogs/trailers/+page.svelte
index 5894f317..716448a3 100644
--- a/frontend/src/routes/dashboard/general_catalogs/trailers/+page.svelte
+++ b/frontend/src/routes/dashboard/general_catalogs/trailers/+page.svelte
@@ -4,7 +4,7 @@
import * as Card from '$lib/components/ui/card';
import { Button } from '$lib/components/ui/button';
import { Input } from '$lib/components/ui/input';
- import { Plus, RefreshCw } from 'lucide-svelte';
+ import { Plus, RefreshCw, Pencil, Trash2 } from 'lucide-svelte';
// Importar componentes de la librería
import InfiniteDataTable from '$lib/components/dashboard/common/infinite-data-table.svelte';
@@ -25,6 +25,12 @@
let pageSize = $state(50);
let hasMore = $derived(data.length < totalItems);
let createDialogOpen = $state(false);
+ let editingItem = $state(null);
+ let selectedIds = $state<(string | number)[]>([]);
+ const selectedTrailer = $derived(
+ selectedIds.length === 1 ? data.find((t) => t.trailer_number === selectedIds[0]) : null
+ );
+ const hasSelection = $derived(selectedIds.length > 0);
// Filtros
let searchNumber = $state($page.url.searchParams.get('trailer_number') || '');
@@ -78,6 +84,62 @@
}
}
+ function handleDialogSuccess() {
+ createDialogOpen = false;
+ editingItem = null;
+ loadData();
+ }
+
+ function handleRowDoubleClick(row: Trailer) {
+ editingItem = row;
+ createDialogOpen = true;
+ }
+
+ function handleRowClick(row: Trailer) {
+ const id = row.trailer_number;
+ if (selectedIds.includes(id)) {
+ selectedIds = selectedIds.filter((i) => i !== id);
+ } else {
+ selectedIds = [id];
+ }
+ }
+
+ function handleEditSelected() {
+ if (selectedTrailer) {
+ editingItem = selectedTrailer;
+ createDialogOpen = true;
+ }
+ }
+
+ async function handleDeleteSelected() {
+ if (!selectedTrailer) return;
+ if (
+ !confirm(
+ `¿Estás seguro de eliminar el trailer "${selectedTrailer.trailer_number}"?\n\nNota: No se puede eliminar si tiene registros relacionados.`
+ )
+ ) {
+ return;
+ }
+
+ if (!companyStore.activeCompany) return;
+
+ try {
+ const response = await trailersApi.delete(
+ selectedTrailer.trailer_number,
+ companyStore.activeCompany.id
+ );
+ if (response.error) {
+ alert(`❌ Error al eliminar:\n\n${response.error}`);
+ } else {
+ alert(`✅ Trailer eliminado correctamente`);
+ selectedIds = [];
+ loadData();
+ }
+ } catch (e) {
+ console.error('Error deleting:', e);
+ }
+ }
+
function handleSearch() {
if (!browser) return;
clearTimeout(searchTimeout);
@@ -98,6 +160,10 @@
$effect(() => {
const _ = { p: $page.url.href, c: companyStore.activeCompany?.id };
loadData();
+
+ if (!createDialogOpen) {
+ editingItem = null;
+ }
});
useShortcuts('Trailers', [
@@ -126,15 +192,47 @@
Trailers
Gestión del catálogo de trailers de la compañía
-
+
- {#if loading && data.length === 0}Cargando trailers...
{:else}
{/if}
+ {#if loading && data.length === 0}Cargando trailers...
{:else} (selectedIds = ids)} onRowClick={handleRowClick} onRowDoubleClick={handleRowDoubleClick} getRowId={(row) => row.trailer_number} />
{/if}
Mostrando {data.length} de {totalItems} registros
-
+
+
+
+
+
+
diff --git a/frontend/src/routes/dashboard/general_catalogs/transporters/+page.svelte b/frontend/src/routes/dashboard/general_catalogs/transporters/+page.svelte
index eb986eb3..e2586d06 100644
--- a/frontend/src/routes/dashboard/general_catalogs/transporters/+page.svelte
+++ b/frontend/src/routes/dashboard/general_catalogs/transporters/+page.svelte
@@ -4,7 +4,7 @@
import * as Card from '$lib/components/ui/card';
import { Button } from '$lib/components/ui/button';
import { Input } from '$lib/components/ui/input';
- import { Plus, RefreshCw } from 'lucide-svelte';
+ import { Plus, RefreshCw, Pencil, Trash2 } from 'lucide-svelte';
// Importar componentes de la librería
import InfiniteDataTable from '$lib/components/dashboard/common/infinite-data-table.svelte';
@@ -25,6 +25,12 @@
let pageSize = $state(50);
let hasMore = $derived(data.length < totalItems);
let createDialogOpen = $state(false);
+ let editingItem = $state(null);
+ let selectedIds = $state<(string | number)[]>([]);
+ const selectedTransporter = $derived(
+ selectedIds.length === 1 ? data.find((t) => t.transporter_key === selectedIds[0]) : null
+ );
+ const hasSelection = $derived(selectedIds.length > 0);
// Filtros
let searchKey = $state($page.url.searchParams.get('transporter_key') || '');
@@ -77,6 +83,62 @@
loading = false;
}
}
+
+ function handleDialogSuccess() {
+ createDialogOpen = false;
+ editingItem = null;
+ loadData();
+ }
+
+ function handleRowDoubleClick(row: Transporter) {
+ editingItem = row;
+ createDialogOpen = true;
+ }
+
+ function handleRowClick(row: Transporter) {
+ const id = row.transporter_key;
+ if (selectedIds.includes(id)) {
+ selectedIds = selectedIds.filter((i) => i !== id);
+ } else {
+ selectedIds = [id]; // Por ahora selección única para simplificar
+ }
+ }
+
+ function handleEditSelected() {
+ if (selectedTransporter) {
+ editingItem = selectedTransporter;
+ createDialogOpen = true;
+ }
+ }
+
+ async function handleDeleteSelected() {
+ if (!selectedTransporter) return;
+ if (
+ !confirm(
+ `¿Estás seguro de eliminar el transportista "${selectedTransporter.transporter_key}"?\n\nNota: No se puede eliminar si tiene registros relacionados.`
+ )
+ ) {
+ return;
+ }
+
+ if (!companyStore.activeCompany) return;
+
+ try {
+ const response = await transportersApi.delete(
+ selectedTransporter.transporter_key,
+ companyStore.activeCompany.id
+ );
+ if (response.error) {
+ alert(`❌ Error al eliminar:\n\n${response.error}`);
+ } else {
+ alert(`✅ Transportista eliminado correctamente`);
+ selectedIds = [];
+ loadData();
+ }
+ } catch (e) {
+ console.error('Error deleting:', e);
+ }
+ }
function handleSearch() {
if (!browser) return;
@@ -98,6 +160,10 @@
$effect(() => {
const _ = { p: $page.url.href, c: companyStore.activeCompany?.id };
loadData();
+
+ if (!createDialogOpen) {
+ editingItem = null;
+ }
});
useShortcuts('Transportistas', [
@@ -126,15 +192,47 @@
Transportistas
Gestión del catálogo de líneas transportistas
-
+
- {#if loading && data.length === 0}Cargando transportistas...
{:else}
{/if}
+ {#if loading && data.length === 0}Cargando transportistas...
{:else} (selectedIds = ids)} onRowClick={handleRowClick} onRowDoubleClick={handleRowDoubleClick} />
{/if}
Mostrando {data.length} de {totalItems} registros
-
+
+
+
+
+
+
diff --git a/frontend/src/routes/dashboard/general_catalogs/vehicles/+page.svelte b/frontend/src/routes/dashboard/general_catalogs/vehicles/+page.svelte
index 1e193fe7..660e044d 100644
--- a/frontend/src/routes/dashboard/general_catalogs/vehicles/+page.svelte
+++ b/frontend/src/routes/dashboard/general_catalogs/vehicles/+page.svelte
@@ -4,7 +4,7 @@
import * as Card from '$lib/components/ui/card';
import { Button } from '$lib/components/ui/button';
import { Input } from '$lib/components/ui/input';
- import { Plus, RefreshCw } from 'lucide-svelte';
+ import { Plus, RefreshCw, Pencil, Trash2 } from 'lucide-svelte';
// Importar componentes de la librería
import InfiniteDataTable from '$lib/components/dashboard/common/infinite-data-table.svelte';
@@ -25,6 +25,12 @@
let pageSize = $state(50);
let hasMore = $derived(data.length < totalItems);
let createDialogOpen = $state(false);
+ let editingItem = $state(null);
+ let selectedIds = $state<(string | number)[]>([]);
+ const selectedVehicle = $derived(
+ selectedIds.length === 1 ? data.find((v) => v.vehicle_key === selectedIds[0]) : null
+ );
+ const hasSelection = $derived(selectedIds.length > 0);
// Filtros
let searchKey = $state($page.url.searchParams.get('vehicle_key') || '');
@@ -78,6 +84,62 @@
}
}
+ function handleDialogSuccess() {
+ createDialogOpen = false;
+ editingItem = null;
+ loadData();
+ }
+
+ function handleRowDoubleClick(row: Vehicle) {
+ editingItem = row;
+ createDialogOpen = true;
+ }
+
+ function handleRowClick(row: Vehicle) {
+ const id = row.vehicle_key;
+ if (selectedIds.includes(id)) {
+ selectedIds = selectedIds.filter((i) => i !== id);
+ } else {
+ selectedIds = [id];
+ }
+ }
+
+ function handleEditSelected() {
+ if (selectedVehicle) {
+ editingItem = selectedVehicle;
+ createDialogOpen = true;
+ }
+ }
+
+ async function handleDeleteSelected() {
+ if (!selectedVehicle) return;
+ if (
+ !confirm(
+ `¿Estás seguro de eliminar el vehículo "${selectedVehicle.vehicle_key}"?\n\nNota: No se puede eliminar si tiene registros relacionados.`
+ )
+ ) {
+ return;
+ }
+
+ if (!companyStore.activeCompany) return;
+
+ try {
+ const response = await vehiclesApi.delete(
+ selectedVehicle.vehicle_key,
+ companyStore.activeCompany.id
+ );
+ if (response.error) {
+ alert(`❌ Error al eliminar:\n\n${response.error}`);
+ } else {
+ alert(`✅ Vehículo eliminado correctamente`);
+ selectedIds = [];
+ loadData();
+ }
+ } catch (e) {
+ console.error('Error deleting:', e);
+ }
+ }
+
function handleSearch() {
if (!browser) return;
clearTimeout(searchTimeout);
@@ -98,6 +160,10 @@
$effect(() => {
const _ = { p: $page.url.href, c: companyStore.activeCompany?.id };
loadData();
+
+ if (!createDialogOpen) {
+ editingItem = null;
+ }
});
useShortcuts('Vehículos (transporte)', [
@@ -128,15 +194,47 @@
Gestión del catálogo de camiones y vehículos de transporte
-
+
- {#if loading && data.length === 0}Cargando vehículos...
{:else}
{/if}
+ {#if loading && data.length === 0}Cargando vehículos...
{:else} (selectedIds = ids)} onRowClick={handleRowClick} onRowDoubleClick={handleRowDoubleClick} getRowId={(row) => row.vehicle_key} />
{/if}
Mostrando {data.length} de {totalItems} registros
-
+
+
+
+
+
+