-
+ {openEditor && (
+
+ )}
Edit
-
+ {ops?.duplicate && (
+
+ )}
Copy
-
+ {ops?.move && (
+
+ )}
Up
- }
Down
- ops?.remove?.(rowIndex)}>
-
-
+ {ops?.remove && (
+ ops?.remove?.(rowIndex, table)}
+ >
+
+
+ )}
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 }) => (
-