diff --git a/client/src/app/quotes/components/QuoteChaptersDataTable.tsx b/client/src/app/quotes/components/QuoteChaptersDataTable.tsx
index 4c8f298..42102fa 100644
--- a/client/src/app/quotes/components/QuoteChaptersDataTable.tsx
+++ b/client/src/app/quotes/components/QuoteChaptersDataTable.tsx
@@ -11,13 +11,13 @@ import {
DialogFooter,
DialogHeader,
DialogTitle,
- Label,
Empty,
+ EmptyContent,
+ EmptyDescription,
EmptyHeader,
EmptyMedia,
EmptyTitle,
- EmptyDescription,
- EmptyContent,
+ Label,
Select,
SelectContent,
SelectItem,
@@ -29,12 +29,12 @@ import {
import { Table, TableBody, TableCell, TableHead, TableRow } from "@/ui/table";
import {
DndContext,
- closestCenter,
- pointerWithin,
DragEndEvent,
KeyboardCoordinateGetter,
KeyboardSensor,
PointerSensor,
+ closestCenter,
+ pointerWithin,
useDroppable,
useSensor,
useSensors,
@@ -48,37 +48,31 @@ import {
getCoreRowModel,
useReactTable,
} from "@tanstack/react-table";
-import {
- CopyIcon,
- FolderIcon,
- FolderOpenIcon,
- GripVerticalIcon,
- Trash2Icon,
-} from "lucide-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 {
- chapterSubtotal,
- duplicateChapterSelection,
EditorChapter,
EditorItem,
+ chapterSubtotal,
+ duplicateChapterSelection,
insertChapterItem,
moveChapterItems,
moveChapters,
removeChapterSelection,
ungroupChapters,
} from "../chapterEditor";
-import { ChapterEditorController } from "../hooks/useChapterEditor";
import { useChapterCollapse } from "../hooks/useChapterCollapse";
+import { ChapterEditorController } from "../hooks/useChapterEditor";
import { QuoteItemsInlineRowActions } from "./QuoteItemsInlineRowActions";
+import { RowIdData } from "./QuoteItemsSortableDataTable";
import { QuoteItemsSortableDataTableToolbar } from "./QuoteItemsSortableDataTableToolbar";
import { QuoteItemsSortableTableRow } from "./QuoteItemsSortableTableRow";
-import { RowIdData } from "./QuoteItemsSortableDataTable";
-import { AppendEmptyRowButton } from "./AppendEmptyRowButton";
-import { AppendCatalogArticleRowButton } from "./AppendCatalogArticleRowButton";
import { AppendBlockRowButton } from "./AppendBlockRowButton";
+import { AppendCatalogArticleRowButton } from "./AppendCatalogArticleRowButton";
+import { AppendEmptyRowButton } from "./AppendEmptyRowButton";
type Selection = { chapters: string[]; indexes: number[] };
const isChapter = (id: string | number) => String(id).startsWith("chapter:");
@@ -110,7 +104,11 @@ const keyboardCoordinates: KeyboardCoordinateGetter = (
};
function ChapterContent({ children }: { children: ReactNode }) {
- return
{children}
;
+ return (
+
+ {children}
+
+ );
}
// Reserve the expand column in detail rows so all controls stay aligned.
@@ -118,7 +116,15 @@ function ChapterSpacerCell() {
return ;
}
-function EmptyChapterTarget({ id, columns, hidden, collapsed, onAddRow, onAddArticle, onAddBlock }: {
+function EmptyChapterTarget({
+ id,
+ columns,
+ hidden,
+ collapsed,
+ onAddRow,
+ onAddArticle,
+ onAddBlock,
+}: {
id: string;
columns: number;
hidden: boolean;
@@ -147,7 +153,7 @@ function EmptyChapterTarget({ id, columns, hidden, collapsed, onAddRow, onAddArt
{t("quotes.chapters.empty_title")}
{t("quotes.chapters.empty")}
-
+
@@ -164,24 +170,18 @@ function ChapterGroup({
index,
columns,
children,
- selected,
- onSelect,
onActivate,
onDuplicate,
onRemove,
- mixed,
subtotal,
}: {
chapter: EditorChapter;
index: number;
columns: number;
children: (collapsed: boolean, hidden: boolean) => ReactNode;
- selected: boolean;
- onSelect: (selected: boolean) => void;
onActivate: () => void;
onDuplicate: () => void;
onRemove: () => void;
- mixed: boolean;
subtotal: string;
}) {
const { t } = useTranslation();
@@ -195,14 +195,23 @@ function ChapterGroup({
transform,
transition,
isDragging,
- } = useSortable({ id: `chapter:${chapter.id}`, disabled: mixed });
+ } = useSortable({ id: `chapter:${chapter.id}` });
return (
{
setNodeRef(node);
ref.current = node;
}}
- className='bg-background [&>tr:not([data-chapter-gap])>td]:border-b [&>tr:not([data-chapter-gap])>td]:border-b-border'
+ className={`bg-background
+ [&>tr:not([data-chapter-gap]):not([data-active-item])>td]:border-b
+ [&>tr:not([data-chapter-gap]):not([data-active-item])>td]:border-b-border
+ [&>tr[data-active-item]>td]:border-destructive/30
+ [&>tr[data-active-item]>td:first-child]:border-l-4
+ [&>tr[data-active-item]>td:first-child]:border-l-destructive
+ [&>tr[data-active-item]>td:last-child]:border-r
+ [&>tr[data-active-item=detail]>td]:border-t
+ [&>tr[data-active-item=detail]>td]:border-b-0
+ [&>tr[data-active-item=actions]>td]:border-b`}
style={{
transform: CSS.Translate.toString(transform),
transition,
@@ -215,16 +224,16 @@ function ChapterGroup({
)}
-
-
+
+
-
-
-
-
- onSelect(Boolean(value))}
- aria-label={t("quotes.chapters.select", { number: index + 1 })}
- />
-
-
+
(
-
+
{fieldState.error && (
@@ -301,23 +287,38 @@ function ChapterGroup({
)}
/>
+
@@ -331,7 +332,7 @@ function ChapterGroup({
{t("quotes.chapters.subtotal", { number: index + 1, description: chapter.description })}
- {subtotal}
+ {subtotal}
);
@@ -365,19 +366,14 @@ export function QuoteChaptersDataTable({
const { t } = useTranslation();
const { watch, setValue } = useFormContext();
const [rowSelection, setRowSelection] = useState({});
- const [chapterSelection, setChapterSelection] = useState([]);
const [activeRow, setActiveRow] = useState(null);
const [deleting, setDeleting] = useState(null);
const [ungrouping, setUngrouping] = useState(false);
const [targetId, setTargetId] = useState("");
const { chapters, items } = controller;
const selectedIndexes = data.flatMap((row, index) => (rowSelection[row.id] ? [index] : []));
- const mixed =
- chapterSelection.length > 0 &&
- selectedIndexes.some((index) => !chapterSelection.includes(items[index]?.chapter_id ?? ""));
const clear = () => {
setRowSelection({});
- setChapterSelection([]);
setActiveRow(null);
};
const apply = (state: Parameters[0]) => {
@@ -427,11 +423,8 @@ export function QuoteChaptersDataTable({
apply(next);
setDeleting(null);
};
- const selection = { chapters: chapterSelection, indexes: selectedIndexes };
- const selectedCount =
- chapterSelection.length +
- selectedIndexes.filter((index) => !chapterSelection.includes(items[index]?.chapter_id ?? ""))
- .length;
+ const selection: Selection = { chapters: [], indexes: selectedIndexes };
+ const selectedCount = selectedIndexes.length;
const insert = (index: number, after = false) => {
const state = controller.read();
apply(
@@ -444,17 +437,20 @@ export function QuoteChaptersDataTable({
)
);
};
- const chapterColumns = useMemo[]>(() => [
- {
- id: "chapter_tree",
- header: () => null,
- cell: () => null,
- size: 44,
- enableHiding: false,
- enableSorting: false,
- },
- ...columns,
- ], [columns]);
+ const chapterColumns = useMemo[]>(
+ () => [
+ {
+ id: "chapter_tree",
+ header: () => null,
+ cell: () => null,
+ size: 56,
+ enableHiding: false,
+ enableSorting: false,
+ },
+ ...columns,
+ ],
+ [columns]
+ );
const table = useReactTable({
data,
columns: chapterColumns,
@@ -494,17 +490,16 @@ export function QuoteChaptersDataTable({
useSensor(KeyboardSensor, { coordinateGetter: keyboardCoordinates })
);
const dragEnd = ({ active, over }: DragEndEvent) => {
- if (!over || active.id === over.id || mixed) return;
+ if (!over || active.id === over.id) return;
const state = controller.read();
if (isChapter(active.id)) {
if (!isChapter(over.id)) return;
const from = String(active.id).slice(8);
const to = String(over.id).slice(8);
- const moving = chapterSelection.includes(from) ? chapterSelection : [from];
apply(
moveChapters(
state,
- moving,
+ [from],
to,
state.chapters.findIndex((chapter) => chapter.id === from) <
state.chapters.findIndex((chapter) => chapter.id === to)
@@ -543,12 +538,7 @@ export function QuoteChaptersDataTable({
sensors={sensors}
onDragEnd={dragEnd}
onDragStart={({ active }) => {
- if (
- isChapter(active.id)
- ? !chapterSelection.includes(String(active.id).slice(8))
- : !rowSelection[active.id]
- )
- clear();
+ if (isChapter(active.id) || !rowSelection[active.id]) clear();
}}
collisionDetection={(args) => {
const valid = {
@@ -587,16 +577,19 @@ export function QuoteChaptersDataTable({
-