diff --git a/modules/customer-invoices/src/web/components/customer-invoice-taxes-multi-select.tsx b/modules/customer-invoices/src/web/components/customer-invoice-taxes-multi-select.tsx index ce16555e..b8285cdd 100644 --- a/modules/customer-invoices/src/web/components/customer-invoice-taxes-multi-select.tsx +++ b/modules/customer-invoices/src/web/components/customer-invoice-taxes-multi-select.tsx @@ -9,11 +9,12 @@ interface CustomerInvoiceTaxesMultiSelect { value?: string[]; onChange: (selectedValues: string[]) => void; className?: string; + inputId?: string; [key: string]: any; // Allow other props to be passed } export const CustomerInvoiceTaxesMultiSelect = (props: CustomerInvoiceTaxesMultiSelect) => { - const { value, onChange, className, ...otherProps } = props; + const { value, onChange, className, inputId, ...otherProps } = props; const { t } = useTranslation(); const taxCatalog = useMemo(() => SpainTaxCatalogProvider(), []); @@ -38,6 +39,7 @@ export const CustomerInvoiceTaxesMultiSelect = (props: CustomerInvoiceTaxesMulti return (
(); return ( -
+
@@ -32,7 +32,7 @@ export const InvoiceEditForm = ({
- +
diff --git a/modules/customer-invoices/src/web/components/editor/items/debug-id-col.tsx b/modules/customer-invoices/src/web/components/editor/items/debug-id-col.tsx new file mode 100644 index 00000000..38104467 --- /dev/null +++ b/modules/customer-invoices/src/web/components/editor/items/debug-id-col.tsx @@ -0,0 +1,22 @@ +import { ColumnDef } from '@tanstack/react-table'; + +// columna de depuración: muestra el row.id interno de TanStack +export const debugIdCol: ColumnDef = ({ + id: "__debug_row_id", + header: () => row.id, + cell: ({ row }) => ( + + {row.id}
+
+ ), + enableSorting: false, + enableHiding: false, // ponlo en true si quieres que sea ocultable en ViewOptions + size: 160, + minSize: 120, + maxSize: 260, +}); diff --git a/modules/customer-invoices/src/web/components/editor/items/item-row-editor.tsx b/modules/customer-invoices/src/web/components/editor/items/item-row-editor.tsx index cb64653a..48845380 100644 --- a/modules/customer-invoices/src/web/components/editor/items/item-row-editor.tsx +++ b/modules/customer-invoices/src/web/components/editor/items/item-row-editor.tsx @@ -1,8 +1,16 @@ import { Button, Input, Label, Textarea } from "@repo/shadcn-ui/components"; import { useFormContext } from "react-hook-form"; -import { InvoiceFormData } from '../../../schemas'; +import { InvoiceFormData, InvoiceItemFormData } from '../../../schemas'; -export function ItemRowEditor({ index, close }: { index: number; close: () => void }) { +export function ItemRowEditor({ + row, + index, + onClose, +}: { + row: InvoiceItemFormData + index: number + onClose: () => void +}) { // Editor simple reutilizando el mismo RHF const { register } = useFormContext(); return ( @@ -27,7 +35,7 @@ export function ItemRowEditor({ index, close }: { index: number; close: () => vo
- +
); diff --git a/modules/customer-invoices/src/web/components/editor/items/items-data-table-row-actions.tsx b/modules/customer-invoices/src/web/components/editor/items/items-data-table-row-actions.tsx index 66b82a67..ba752171 100644 --- a/modules/customer-invoices/src/web/components/editor/items/items-data-table-row-actions.tsx +++ b/modules/customer-invoices/src/web/components/editor/items/items-data-table-row-actions.tsx @@ -3,6 +3,7 @@ import { Row, Table } from "@tanstack/react-table"; +import { DataTableRowOps } from '@repo/rdx-ui/components'; import { Button, Tooltip, @@ -19,64 +20,95 @@ interface DataTableRowActionsProps { export function ItemDataTableRowActions({ row, table }: DataTableRowActionsProps) { - const ops = (table.options.meta as any)?.rowOps as { - duplicate?: (i: number) => void; - remove?: (i: number) => void; - move?: (f: number, t: number) => void; - canMoveUp?: (i: number) => boolean; - canMoveDown?: (i: number, last: number) => boolean; - }; - const openEditor = (table.options.meta as any)?.openEditor as (i: number) => void; + const ops = (table.options.meta as any)?.rowOps as DataTableRowOps; + const openEditor = (table.options.meta as any)?.openEditor as (i: number, table: Table) => void; const lastRow = table.getRowModel().rows.length - 1; const rowIndex = row.index; return ( -
+
- + {openEditor && ( + + )} Edit - + {ops?.duplicate && ( + + )} Copy - + {ops?.move && ( + + )} Up - + + } Down - + {ops?.remove && ( + + )} Delete diff --git a/modules/customer-invoices/src/web/components/editor/items/items-editor.tsx b/modules/customer-invoices/src/web/components/editor/items/items-editor.tsx index 66c0b53a..b3a065da 100644 --- a/modules/customer-invoices/src/web/components/editor/items/items-editor.tsx +++ b/modules/customer-invoices/src/web/components/editor/items/items-editor.tsx @@ -1,9 +1,12 @@ -import { DataTable } from '@repo/rdx-ui/components'; +import { DataTable, useWithRowSelection } from '@repo/rdx-ui/components'; +import { Table } from '@tanstack/react-table'; +import { useMemo } from 'react'; import { useFieldArray, useFormContext } from "react-hook-form"; import { useInvoiceContext } from '../../../context'; import { useInvoiceAutoRecalc } from '../../../hooks'; import { useTranslation } from '../../../i18n'; import { InvoiceFormData, defaultCustomerInvoiceItemFormData } from '../../../schemas'; +import { debugIdCol } from './debug-id-col'; import { ItemRowEditor } from './item-row-editor'; import { useItemsColumns } from './use-items-columns'; @@ -18,31 +21,70 @@ export const ItemsEditor = () => { useInvoiceAutoRecalc(form, context); - const { fields, append, remove, move, insert } = useFieldArray({ + const { fields, append, remove, move, insert, update } = useFieldArray({ control, name: "items", }); - const columns = useItemsColumns(); + console.log(fields); + + const baseColumns = useWithRowSelection(useItemsColumns(), true); + const columns = useMemo( + () => [...baseColumns, debugIdCol], + [baseColumns] + ); return (
- (r as any).id} + String(row?.index)} meta={{ - rowOps: { - //duplicate: (indexRow: number) => insert(indexRow + 1, { ...getValues(`items.${indexRow}`) /*, id: crypto.randomUUID()*/ }), - remove: (indexRow: number) => remove(indexRow), - move: (fromIndex: number, toIndex: number) => { - if (toIndex < 0 || toIndex >= fields.length) return; - move(fromIndex, toIndex); - }, - canMoveUp: (indexRow: number) => indexRow > 0, - canMoveDown: (indexRow: number, lastIndexRow: number) => indexRow < lastIndexRow, + tableOps: { + onAdd: () => append({ ...createEmptyItem() }), + appendItem: (item: any) => append(item), }, + rowOps: { + remove: (i: number) => remove(i), + move: (from: number, to: number) => move(from, to), + insertItem: (index: number, item: any) => insert(index, item), + duplicateItems: (indexes: number[], table: Table) => { + const items = getValues("items") || []; + // duplicate in descending order to keep indexes stable + [...indexes].sort((a, b) => b - a).forEach(i => { + const curr = items[i] as any; + if (curr) { + const { id, ...rest } = curr; + append({ ...rest }); + } + }); + }, + deleteItems: (indexes: number[]) => { + // remove in descending order to avoid shifting issues + [...indexes].sort((a, b) => b - a).forEach(i => remove(i)); + }, + updateItem: (index: number, item: any) => update(index, item), + }, + bulkOps: { + duplicateSelected: (indexes, table) => { + const originalData = indexes.map((i) => { + const { id, ...original } = table.getRowModel().rows[i].original; + return original; + }); + + insert(indexes[indexes.length - 1] + 1, originalData, { shouldFocus: true }); + table.resetRowSelection(); + }, + removeSelected: (indexes) => indexes.sort((a, b) => b - a).forEach(remove), + moveSelectedUp: (indexes) => indexes.forEach((i) => move(i, i - 1)), + moveSelectedDown: (indexes) => [...indexes].reverse().forEach((i) => move(i, i + 1)), + } }} - renderRowEditor={(index, close) => } /> + enableRowSelection + enablePagination={false} + pageSize={999} + readOnly={false} + EditorComponent={ItemRowEditor} + />
); } diff --git a/modules/customer-invoices/src/web/components/editor/items/quantity-input-field.tsx b/modules/customer-invoices/src/web/components/editor/items/quantity-input-field.tsx index b5a8aabc..7c997637 100644 --- a/modules/customer-invoices/src/web/components/editor/items/quantity-input-field.tsx +++ b/modules/customer-invoices/src/web/components/editor/items/quantity-input-field.tsx @@ -30,7 +30,7 @@ export function QuantityInputField({ name={name} render={({ field }) => { const { value, onChange } = field; - console.log(value); + return {label ? ( diff --git a/modules/customer-invoices/src/web/components/editor/items/use-items-columns.tsx b/modules/customer-invoices/src/web/components/editor/items/use-items-columns.tsx index 7e91adf2..05a8b964 100644 --- a/modules/customer-invoices/src/web/components/editor/items/use-items-columns.tsx +++ b/modules/customer-invoices/src/web/components/editor/items/use-items-columns.tsx @@ -1,5 +1,5 @@ import { DataTableColumnHeader } from '@repo/rdx-ui/components'; -import { Checkbox, Textarea } from "@repo/shadcn-ui/components"; +import { InputGroup, InputGroupTextarea } from "@repo/shadcn-ui/components"; import { cn } from "@repo/shadcn-ui/lib/utils"; import type { ColumnDef } from "@tanstack/react-table"; import * as React from "react"; @@ -30,29 +30,6 @@ export function useItemsColumns(): ColumnDef[] { // Atención: Memoizar siempre para evitar reconstrucciones y resets de estado de tabla return React.useMemo[]>(() => [ - { - id: "select", - header: ({ table }) => ( - table.toggleAllPageRowsSelected(!!v)} - aria-label="Select all" - className="translate-y-[2px]" - /> - ), - cell: ({ row }) => ( - row.toggleSelected(!!v)} - aria-label="Select row" - className="translate-y-[2px]" - /> - ), - enableSorting: false, - enableHiding: false, - size: 48, minSize: 40, maxSize: 64, - meta: { className: "w-[4ch]" }, // ancho aprox. por dígitos - }, { id: 'position', header: ({ column }) => ( @@ -72,26 +49,39 @@ export function useItemsColumns(): ColumnDef[] { control={control} name={`items.${row.index}.description`} render={({ field }) => ( -