From 463c0662ed3b5a89009e5206dd926ed30c85c4c1 Mon Sep 17 00:00:00 2001 From: David Arranz Date: Wed, 16 Sep 2026 16:51:31 +0200 Subject: [PATCH] =?UTF-8?q?A=C3=B1adir=20funcionalidad=20de=20selecci?= =?UTF-8?q?=C3=B3n=20y=20gesti=C3=B3n=20de=20cantidades=20en=20CatalogPick?= =?UTF-8?q?erDataTable,=20incluyendo=20pruebas=20unitarias.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CatalogPickerDataTable.test.tsx | 124 ++++++++++++ .../components/CatalogPickerDataTable.tsx | 185 +++++++++++------- .../lib/hooks/useDataTable/useDataTable.tsx | 4 + 3 files changed, 243 insertions(+), 70 deletions(-) create mode 100644 client/src/app/quotes/components/CatalogPickerDataTable.test.tsx diff --git a/client/src/app/quotes/components/CatalogPickerDataTable.test.tsx b/client/src/app/quotes/components/CatalogPickerDataTable.test.tsx new file mode 100644 index 0000000..b987c45 --- /dev/null +++ b/client/src/app/quotes/components/CatalogPickerDataTable.test.tsx @@ -0,0 +1,124 @@ +import { DataTableContext, IDataTableContextState } from "@/lib/hooks/useDataTable/DataTableContext"; +import { act, Simulate } from "react-dom/test-utils"; +import { render as renderDom, unmountComponentAtNode } from "react-dom"; +import { MemoryRouter } from "react-router-dom"; +import type { ButtonHTMLAttributes, InputHTMLAttributes } from "react"; +import { CatalogPickerDataTable } from "./CatalogPickerDataTable"; + +const useCatalogList = jest.fn(); + +jest.mock("@/app/catalog/hooks", () => ({ useCatalogList: (...args: unknown[]) => useCatalogList(...args) })); +jest.mock("@/app/catalog/components/CatalogDataTableFilter", () => ({ + CatalogDataTableFilter: () => null, +})); +jest.mock("i18next", () => ({ t: (key: string) => key })); +jest.mock("@/ui", () => { + const mockReact = require("react") as typeof import("react"); + return { + Button: (props: ButtonHTMLAttributes) => + ); +} + export const CatalogPickerDataTable = ({ onSelect, }: { @@ -20,15 +81,15 @@ export const CatalogPickerDataTable = ({ const navigate = useNavigate(); const { pagination, globalFilter, isFiltered } = useDataTableContext(); - const [quantities, setQuantities] = useState([]); + const [quantities, setQuantities] = useState([]); - const handleQuantity = (index: number, quantity: number) => { + const handleQuantity = useCallback((index: number, quantity: QuantityValue) => { setQuantities((prev) => { const newQuantities = [...prev]; newQuantities[index] = quantity; return newQuantities; }); - }; + }, []); const resetQuantities = () => { setQuantities([]); @@ -46,73 +107,56 @@ export const CatalogPickerDataTable = ({ resetQuantities(); }, [pagination, globalFilter, isFiltered]); - console.log(quantities); - - const columnHelper = createColumnHelper(); - - const columns = [ - columnHelper.accessor("description", { - id: "description", - header: () => <>{t("catalog.list.columns.description")}, - }), - columnHelper.accessor("points", { - id: "points", - header: () =>
{t("catalog.list.columns.points")}
, - cell: ({ renderValue }: { renderValue: () => any }) => ( -
{renderValue()}
- ), - }), - columnHelper.accessor("retail_price", { - id: "retail_price", - header: () =>
{t("catalog.list.columns.retail_price")}
, - cell: ({ row }: { row: Row }) => { - const price = MoneyValue.create(row.original.retail_price).object; - return
{price.toFormat()}
; + const tableMeta = useMemo( + () => ({ + catalogPicker: { + quantities, + setQuantity: handleQuantity, + selectArticle: (article, index) => + onSelect(article, quantities[index] === "" ? 1 : (quantities[index] ?? 1)), }, }), - columnHelper.display({ - id: "quantity", - header: () => ( -
- {t("catalog.list.columns.quantity")} -
- ), - cell: ({ row: { index } }) => { - return ( - { - event.preventDefault(); - handleQuantity(index, parseInt(event.target.value)); - }} - /> - ); - }, - }), - columnHelper.display({ - id: "row-actions", - header: () => null, - cell: ({ row }: { row: Row }) => ( - - ), - }), - ]; + [handleQuantity, onSelect, quantities] + ); + + const columns = useMemo>>( + () => [ + columnHelper.accessor("description", { + id: "description", + header: () => <>{t("catalog.list.columns.description")}, + }), + columnHelper.accessor("points", { + id: "points", + header: () =>
{t("catalog.list.columns.points")}
, + cell: ({ renderValue }: { renderValue: () => any }) => ( +
{renderValue()}
+ ), + }), + columnHelper.accessor("retail_price", { + id: "retail_price", + header: () =>
{t("catalog.list.columns.retail_price")}
, + cell: ({ row }: { row: Row }) => { + const price = MoneyValue.create(row.original.retail_price).object; + return
{price.toFormat()}
; + }, + }), + columnHelper.display({ + id: "quantity", + header: () => ( +
+ {t("catalog.list.columns.quantity")} +
+ ), + cell: QuantityCell, + }), + columnHelper.display({ + id: "row-actions", + header: () => null, + cell: AddArticleCell, + }), + ], + [] + ); /*const columns2 = useMemo[]>(() => { return [ @@ -188,6 +232,7 @@ export const CatalogPickerDataTable = ({ data: [...(data?.items || [])], columns: columns, pageCount: data?.total_pages ?? -1, + meta: tableMeta, }); if (isError) { diff --git a/client/src/lib/hooks/useDataTable/useDataTable.tsx b/client/src/lib/hooks/useDataTable/useDataTable.tsx index 2e0ce1b..f6670ba 100644 --- a/client/src/lib/hooks/useDataTable/useDataTable.tsx +++ b/client/src/lib/hooks/useDataTable/useDataTable.tsx @@ -2,6 +2,7 @@ import { InitialTableState, OnChangeFn, PaginationState, + TableMeta, getCoreRowModel, getFacetedRowModel, getFacetedUniqueValues, @@ -120,6 +121,7 @@ interface UseDataTableProps { initialState?: InitialTableState; onPaginationChange?: OnChangeFn; + meta?: object; } export function useDataTable({ @@ -131,6 +133,7 @@ export function useDataTable({ enableRowSelection = false, initialState, onPaginationChange, + meta, }: UseDataTableProps) { const { pagination, setPagination, sorting } = useDataTableContext(); @@ -171,6 +174,7 @@ export function useDataTable({ columns: getTableColumns(), //autoResetPageIndex: true, pageCount: pageCount ?? -1, + meta: meta as TableMeta, getCoreRowModel: getCoreRowModel(), initialState,