From 9783f487f3b7231ad203e83bc989195cd0b3f314 Mon Sep 17 00:00:00 2001 From: hreyes Date: Tue, 21 Apr 2026 11:23:35 -0600 Subject: [PATCH] fix/doble-click-parte --- .../common/infinite-data-table.svelte | 2 + .../general_catalogs/doda/data-table.svelte | 5 +- .../dashboard/goods/classes/columns.ts | 27 +++++- .../dashboard/goods/classes/data-table.svelte | 43 +++++++-- .../goods/parts/class-selector-dialog.svelte | 24 ++--- .../dashboard/goods/parts/data-table.svelte | 7 +- .../company_information/+page.svelte | 1 + .../general_catalogs/doda/+page.svelte | 1 + .../goods/fixed-asset-classes/+page.svelte | 89 ++++++++++++------- .../routes/dashboard/goods/parts/+page.svelte | 1 + 10 files changed, 140 insertions(+), 60 deletions(-) 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 2f7dbbcb..3449e32e 100644 --- a/frontend/src/lib/components/dashboard/common/infinite-data-table.svelte +++ b/frontend/src/lib/components/dashboard/common/infinite-data-table.svelte @@ -14,6 +14,7 @@ selectedIds?: number[]; onSelectedIdsChange?: (ids: number[]) => void; onRowClick?: (row: TData) => void; + onRowDoubleClick?: (row: TData) => void; }; let { @@ -26,6 +27,7 @@ selectedIds = [], onSelectedIdsChange, onRowClick, + onRowDoubleClick, }: InfiniteDataTableProps = $props(); const table = createSvelteTable({ diff --git a/frontend/src/lib/components/dashboard/general_catalogs/doda/data-table.svelte b/frontend/src/lib/components/dashboard/general_catalogs/doda/data-table.svelte index 6ef37688..dbc92789 100644 --- a/frontend/src/lib/components/dashboard/general_catalogs/doda/data-table.svelte +++ b/frontend/src/lib/components/dashboard/general_catalogs/doda/data-table.svelte @@ -12,6 +12,7 @@ loadMore: () => void; selectedId?: number | null; onRowClick?: (row: TData) => void; + onRowDoubleClick?: (row: TData) => void; }; let { @@ -21,7 +22,8 @@ hasMore, loadMore, selectedId = null, - onRowClick + onRowClick, + onRowDoubleClick }: DataTableProps = $props(); const table = createSvelteTable({ @@ -94,6 +96,7 @@ {#each table.getRowModel().rows as row (row.id)} onRowClick?.(row.original)} + ondblclick={() => onRowDoubleClick?.(row.original)} class="cursor-pointer {row.getIsSelected() ? 'catalog-table-row-selected' : 'catalog-table-row'}" > {#each row.getVisibleCells() as cell (cell.id)} diff --git a/frontend/src/lib/components/dashboard/goods/classes/columns.ts b/frontend/src/lib/components/dashboard/goods/classes/columns.ts index b552e335..44e390a9 100644 --- a/frontend/src/lib/components/dashboard/goods/classes/columns.ts +++ b/frontend/src/lib/components/dashboard/goods/classes/columns.ts @@ -16,15 +16,34 @@ export function createColumns(): ColumnDef[] { }, cell: ({ row }) => { const isSelected = row.getIsSelected(); - const checkboxSnippet = createRawSnippet<[{ selected: boolean }]>((getProps) => { - const { selected } = getProps(); + const checkboxSnippet = createRawSnippet<[ + { selected: boolean; onchange: (e: Event) => void } + ]>((getProps) => { + const { selected, onchange } = getProps(); return { render: () => `
-
` + `, + setup: (node) => { + const input = node.querySelector('input') as HTMLInputElement | null; + input?.addEventListener('change', onchange); + } }; }); - return renderSnippet(checkboxSnippet, { selected: isSelected }); + return renderSnippet(checkboxSnippet, { + selected: isSelected, + onchange: (e: Event) => { + e.stopPropagation(); + const rowId = row.original.id; + if (typeof rowId === 'number') { + ( + row.table.options.meta as + | { toggleSelectedId?: (id: number, checked?: boolean) => void } + | undefined + )?.toggleSelectedId?.(rowId, (e.target as HTMLInputElement).checked); + } + } + }); }, size: 40, enableSorting: false, diff --git a/frontend/src/lib/components/dashboard/goods/classes/data-table.svelte b/frontend/src/lib/components/dashboard/goods/classes/data-table.svelte index 47d5f7eb..39481b76 100644 --- a/frontend/src/lib/components/dashboard/goods/classes/data-table.svelte +++ b/frontend/src/lib/components/dashboard/goods/classes/data-table.svelte @@ -10,8 +10,9 @@ columns: ColumnDef[]; data: TData[]; loading: boolean; - selectedId?: number | null; - onRowClick?: (row: TData) => void; + selectedIds?: number[]; + onSelectedIdsChange?: (selectedIds: number[]) => void; + onRowDoubleClick?: (row: TData) => void; sorting?: import("@tanstack/table-core").SortingState; onSortingChange?: (sorting: import("@tanstack/table-core").SortingState) => void; }; @@ -20,26 +21,50 @@ data, columns, loading, - selectedId = null, - onRowClick, + selectedIds = [], + onSelectedIdsChange, + onRowDoubleClick, sorting = [], onSortingChange }: DataTableProps = $props(); + function getRowIdValue(row: TData): number | null { + const candidate = (row as { id?: unknown })?.id; + return typeof candidate === 'number' ? candidate : null; + } + + function toggleSelectedId(rowId: number, forceSelected?: boolean) { + const isSelected = selectedIds.includes(rowId); + const shouldSelect = forceSelected ?? !isSelected; + + if (shouldSelect && !isSelected) { + onSelectedIdsChange?.([...selectedIds, rowId]); + return; + } + + if (!shouldSelect && isSelected) { + onSelectedIdsChange?.(selectedIds.filter((id) => id !== rowId)); + } + } + const table = createSvelteTable({ get data() { return data; }, columns, getCoreRowModel: getCoreRowModel(), + getRowId: (row: any) => row.id?.toString(), state: { get rowSelection() { - return selectedId ? { [selectedId]: true } : {}; + return Object.fromEntries(selectedIds.map((id) => [id.toString(), true])); }, get sorting() { return sorting; } }, + meta: { + toggleSelectedId + }, onSortingChange: (updater) => { if (onSortingChange) { const nextSorting = typeof updater === 'function' ? updater(sorting) : updater; @@ -48,7 +73,7 @@ }, manualSorting: true, enableRowSelection: true, - enableMultiRowSelection: false + enableMultiRowSelection: true }); @@ -129,10 +154,12 @@ { - if (onRowClick) { - onRowClick(row.original); + const rowId = getRowIdValue(row.original); + if (rowId !== null) { + toggleSelectedId(rowId); } }} + ondblclick={() => onRowDoubleClick?.(row.original)} class="cursor-pointer hover:bg-muted/50 transition-colors {row.getIsSelected() ? 'bg-primary/10' : ''}" > {#each row.getVisibleCells() as cell (cell.id)} diff --git a/frontend/src/lib/components/dashboard/goods/parts/class-selector-dialog.svelte b/frontend/src/lib/components/dashboard/goods/parts/class-selector-dialog.svelte index cfa9e69c..f4725cda 100644 --- a/frontend/src/lib/components/dashboard/goods/parts/class-selector-dialog.svelte +++ b/frontend/src/lib/components/dashboard/goods/parts/class-selector-dialog.svelte @@ -140,10 +140,18 @@ /> -
+
+ + + + + + + + + +
ClaveDescripciónUM
+
{#if loading}
@@ -155,13 +163,6 @@
{:else} - - - - - - - {#each items as item} {/if} {/if} + diff --git a/frontend/src/lib/components/dashboard/goods/parts/data-table.svelte b/frontend/src/lib/components/dashboard/goods/parts/data-table.svelte index 6c3212bb..5c7277cd 100644 --- a/frontend/src/lib/components/dashboard/goods/parts/data-table.svelte +++ b/frontend/src/lib/components/dashboard/goods/parts/data-table.svelte @@ -18,6 +18,7 @@ rowSelection?: import("@tanstack/table-core").RowSelectionState; onRowSelectionChange?: (rowSelection: import("@tanstack/table-core").RowSelectionState) => void; onRowClick?: (row: TData) => void; + onRowDoubleClick?: (row: TData) => void; }; let { @@ -30,7 +31,8 @@ onSortingChange, rowSelection = {}, onRowSelectionChange, - onRowClick + onRowClick, + onRowDoubleClick }: DataTableProps = $props(); const table = createSvelteTable({ @@ -170,7 +172,8 @@ onRowClick?.(row.original)} - class={onRowClick ? "cursor-pointer" : ""} + ondblclick={() => onRowDoubleClick?.(row.original)} + class={(onRowClick || onRowDoubleClick) ? "cursor-pointer" : ""} > {#each row.getVisibleCells() as cell (cell.id)} diff --git a/frontend/src/routes/dashboard/general_catalogs/company_information/+page.svelte b/frontend/src/routes/dashboard/general_catalogs/company_information/+page.svelte index cf322d0d..6ce44afb 100644 --- a/frontend/src/routes/dashboard/general_catalogs/company_information/+page.svelte +++ b/frontend/src/routes/dashboard/general_catalogs/company_information/+page.svelte @@ -186,6 +186,7 @@ {loading} {hasMore} {loadMore} + onRowDoubleClick={(item) => goto(`/dashboard/general_catalogs/company_information/edit/${item.id}`)} /> diff --git a/frontend/src/routes/dashboard/general_catalogs/doda/+page.svelte b/frontend/src/routes/dashboard/general_catalogs/doda/+page.svelte index 258c3647..ab57e42d 100644 --- a/frontend/src/routes/dashboard/general_catalogs/doda/+page.svelte +++ b/frontend/src/routes/dashboard/general_catalogs/doda/+page.svelte @@ -276,6 +276,7 @@ {loadMore} {selectedId} onRowClick={handleRowClick} + onRowDoubleClick={(item) => goto(`/dashboard/general_catalogs/doda/edit/${item.id}`)} /> diff --git a/frontend/src/routes/dashboard/goods/fixed-asset-classes/+page.svelte b/frontend/src/routes/dashboard/goods/fixed-asset-classes/+page.svelte index a2bcc5e2..215ea02f 100644 --- a/frontend/src/routes/dashboard/goods/fixed-asset-classes/+page.svelte +++ b/frontend/src/routes/dashboard/goods/fixed-asset-classes/+page.svelte @@ -31,6 +31,7 @@ // Estado de la lista de clases let classes = $state([]); + let selectedClassIds = $state([]); let selectedClass = $state(null); let isLoading = $state(false); let searchTerm = $state(''); @@ -107,6 +108,9 @@ if (response.data) { // Data already comes with FA fields embedded classes = response.data as FixedAssetClassExtended[]; + handleSelectedIdsChange( + selectedClassIds.filter((id) => classes.some((item) => item.id === id)) + ); } } catch (error) { console.error('Error cargando clases:', error); @@ -116,7 +120,30 @@ } } - function selectClass(cls: A76Class) { + function clearSelectedClass() { + selectedClass = null; + formData = { + class_code: '', + description_es: '', + description_en: '', + material_key: '', + unit_of_measure: '', + fraction: '', + us_fraction: '', + unit_measure_trade: '', + depreciation_rate: null as number | null, + fda_code: '', + eccn_code: '', + bom: '' + }; + } + + function setActiveClass(cls: FixedAssetClassExtended | null) { + if (!cls) { + clearSelectedClass(); + return; + } + selectedClass = cls; formData = { class_code: cls.class_code, @@ -134,22 +161,27 @@ }; } + function handleSelectedIdsChange(ids: number[]) { + selectedClassIds = ids; + const primaryId = ids[0]; + const primaryClass = primaryId + ? classes.find((item) => item.id === primaryId) ?? null + : null; + + setActiveClass(primaryClass); + } + + function handleRowDoubleClick(cls: A76Class) { + const fixedClass = cls as FixedAssetClassExtended; + selectedClassIds = [fixedClass.id, ...selectedClassIds.filter((id) => id !== fixedClass.id)]; + setActiveClass(fixedClass); + validationError = ''; + showInsertDialog = true; + } + function handleNew() { - selectedClass = null; - formData = { - class_code: '', - description_es: '', - description_en: '', - material_key: '', - unit_of_measure: '', - fraction: '', - us_fraction: '', - unit_measure_trade: '', - depreciation_rate: null as number | null, - fda_code: '', - eccn_code: '', - bom: '' - }; + selectedClassIds = []; + clearSelectedClass(); } async function handleRefresh() { @@ -185,22 +217,9 @@ // Recargar lista await loadClasses(); - selectedClass = null; + selectedClassIds = []; + clearSelectedClass(); showDeleteDialog = false; - formData = { - class_code: '', - description_es: '', - description_en: '', - material_key: '', - unit_of_measure: '', - fraction: '', - us_fraction: '', - unit_measure_trade: '', - depreciation_rate: null as number | null, - fda_code: '', - eccn_code: '', - bom: '' - }; } catch (error) { console.error('Error deleting class:', error); toast.error('Error al eliminar la clase'); @@ -318,8 +337,9 @@ data={filteredClasses} {columns} loading={isLoading} - selectedId={selectedClass?.id} - onRowClick={selectClass} + selectedIds={selectedClassIds} + onSelectedIdsChange={handleSelectedIdsChange} + onRowDoubleClick={handleRowDoubleClick} {sorting} onSortingChange={(newSorting) => (sorting = newSorting)} /> @@ -610,7 +630,8 @@ const wasUpdate = !!selectedClass?.id; await loadClasses(); showInsertDialog = false; - selectedClass = null; + selectedClassIds = []; + clearSelectedClass(); validationError = ''; toast.success( wasUpdate ? 'Clase actualizada correctamente' : 'Clase creada correctamente' diff --git a/frontend/src/routes/dashboard/goods/parts/+page.svelte b/frontend/src/routes/dashboard/goods/parts/+page.svelte index 4fee9c01..ec709e86 100644 --- a/frontend/src/routes/dashboard/goods/parts/+page.svelte +++ b/frontend/src/routes/dashboard/goods/parts/+page.svelte @@ -303,6 +303,7 @@ {sorting} onSortingChange={(newSorting) => (sorting = newSorting)} onRowClick={selectPart} + onRowDoubleClick={(part) => goto(`/dashboard/goods/parts/edit/${part.id}`)} />
ClaveDescripciónUM