From bc34a073ada44cbe7c6ebec3c90e73075e5ba184 Mon Sep 17 00:00:00 2001 From: David Arranz Date: Mon, 19 Oct 2026 16:38:44 +0200 Subject: [PATCH] feat: implement MariaDB integration testing suite and related fixtures - Updated `capitulos-esquema.md` to reflect the current state of models and integration testing setup. - Added `jest.mariadb.config.js` for MariaDB specific test configurations. - Introduced `mariadb.fixture.mock.ts` to handle isolated MariaDB connections for testing. - Created integration tests for quote chapters in `quoteChapters.mariadb.test.ts` to validate business logic against MariaDB. - Implemented `mariadbSafety.test.ts` to ensure safety checks and error handling during integration tests. - Added `useChapterCollapse.ts` hook for managing chapter collapse animations in the client. - Introduced `empty.tsx` components for consistent empty state UI across the application. - Updated `jest.config.js` to ignore MariaDB test files in the standard test run. - Enhanced `package.json` with a new script for running MariaDB tests. --- .../components/QuoteChaptersDataTable.tsx | 395 ++++++++++++------ ...uoteItemsSortableDataTableToolbar.test.tsx | 52 ++- .../QuoteItemsSortableDataTableToolbar.tsx | 186 ++++----- .../editors/QuoteChapterEditor.test.tsx | 217 +++++++++- .../editors/QuoteDetailsCardEditor.tsx | 6 +- .../app/quotes/hooks/useChapterCollapse.ts | 96 +++++ .../src/app/quotes/hooks/useChapterEditor.ts | 4 +- .../src/app/quotes/hooks/useDetailColumns.tsx | 28 +- client/src/locales/ca.json | 4 +- client/src/locales/en.json | 4 +- client/src/locales/es.json | 4 +- client/src/locales/fr.json | 4 +- client/src/ui/empty.tsx | 27 ++ client/src/ui/index.ts | 1 + doc/capitulos-cotizaciones.md | 53 +++ doc/capitulos-esquema.md | 51 ++- server/jest.config.js | 2 +- server/jest.mariadb.config.js | 9 + server/package.json | 1 + .../__integration__/mariadb.fixture.mock.ts | 52 +++ .../__integration__/mariadbSafety.test.ts | 68 +++ .../quoteChapters.mariadb.test.ts | 280 +++++++++++++ 22 files changed, 1291 insertions(+), 253 deletions(-) create mode 100644 client/src/app/quotes/hooks/useChapterCollapse.ts create mode 100644 client/src/ui/empty.tsx create mode 100644 server/jest.mariadb.config.js create mode 100644 server/src/contexts/sales/infrastructure/__integration__/mariadb.fixture.mock.ts create mode 100644 server/src/contexts/sales/infrastructure/__integration__/mariadbSafety.test.ts create mode 100644 server/src/contexts/sales/infrastructure/__integration__/quoteChapters.mariadb.test.ts diff --git a/client/src/app/quotes/components/QuoteChaptersDataTable.tsx b/client/src/app/quotes/components/QuoteChaptersDataTable.tsx index 98ec6e0..4c8f298 100644 --- a/client/src/app/quotes/components/QuoteChaptersDataTable.tsx +++ b/client/src/app/quotes/components/QuoteChaptersDataTable.tsx @@ -1,4 +1,5 @@ import { + Badge, Button, Card, CardContent, @@ -11,6 +12,12 @@ import { DialogHeader, DialogTitle, Label, + Empty, + EmptyHeader, + EmptyMedia, + EmptyTitle, + EmptyDescription, + EmptyContent, Select, SelectContent, SelectItem, @@ -19,7 +26,7 @@ import { Switch, Textarea, } from "@/ui"; -import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/ui/table"; +import { Table, TableBody, TableCell, TableHead, TableRow } from "@/ui/table"; import { DndContext, closestCenter, @@ -41,8 +48,14 @@ import { getCoreRowModel, useReactTable, } from "@tanstack/react-table"; -import { ChevronDownIcon, ChevronRightIcon, CopyIcon, GripVerticalIcon, Trash2Icon } from "lucide-react"; -import { Fragment, ReactNode, useState } from "react"; +import { + CopyIcon, + FolderIcon, + FolderOpenIcon, + GripVerticalIcon, + Trash2Icon, +} from "lucide-react"; +import { Fragment, ReactNode, useMemo, useState } from "react"; import { Controller, useFormContext } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { @@ -57,11 +70,16 @@ import { ungroupChapters, } from "../chapterEditor"; import { ChapterEditorController } from "../hooks/useChapterEditor"; +import { useChapterCollapse } from "../hooks/useChapterCollapse"; import { QuoteItemsInlineRowActions } from "./QuoteItemsInlineRowActions"; import { QuoteItemsSortableDataTableToolbar } from "./QuoteItemsSortableDataTableToolbar"; import { QuoteItemsSortableTableRow } from "./QuoteItemsSortableTableRow"; import { RowIdData } from "./QuoteItemsSortableDataTable"; +import { AppendEmptyRowButton } from "./AppendEmptyRowButton"; +import { AppendCatalogArticleRowButton } from "./AppendCatalogArticleRowButton"; +import { AppendBlockRowButton } from "./AppendBlockRowButton"; + type Selection = { chapters: string[]; indexes: number[] }; const isChapter = (id: string | number) => String(id).startsWith("chapter:"); @@ -91,13 +109,51 @@ const keyboardCoordinates: KeyboardCoordinateGetter = ( }; }; -function EmptyChapterTarget({ id, columns, hidden }: { id: string; columns: number; hidden: boolean }) { +function ChapterContent({ children }: { children: ReactNode }) { + return
{children}
; +} + +// Reserve the expand column in detail rows so all controls stay aligned. +function ChapterSpacerCell() { + return ; +} + +function EmptyChapterTarget({ id, columns, hidden, collapsed, onAddRow, onAddArticle, onAddBlock }: { + id: string; + columns: number; + hidden: boolean; + collapsed: boolean; + onAddRow: () => void; + onAddArticle: () => void; + onAddBlock: () => void; +}) { const { t } = useTranslation(); - const { setNodeRef, isOver } = useDroppable({ id: `empty:${id}`, disabled: hidden }); + const { setNodeRef, isOver } = useDroppable({ id: `empty:${id}`, disabled: collapsed }); return ( -