diff --git a/client/src/app/quotes/components/QuoteChaptersDataTable.tsx b/client/src/app/quotes/components/QuoteChaptersDataTable.tsx index cd71e5f..4cb6a85 100644 --- a/client/src/app/quotes/components/QuoteChaptersDataTable.tsx +++ b/client/src/app/quotes/components/QuoteChaptersDataTable.tsx @@ -11,12 +11,6 @@ import { DialogFooter, DialogHeader, DialogTitle, - Empty, - EmptyContent, - EmptyDescription, - EmptyHeader, - EmptyMedia, - EmptyTitle, Label, Select, SelectContent, @@ -73,14 +67,11 @@ import { import { useChapterCollapse } from "../hooks/useChapterCollapse"; import { ChapterEditorController } from "../hooks/useChapterEditor"; import { QuoteItemsInlineRowActions } from "./QuoteItemsInlineRowActions"; -import { RowIdData } from "./QuoteItemsSortableDataTable"; +import type { RowIdData } from "./QuoteItemsSortableDataTable.types"; +import { QuoteItemsEmptyState } from "./QuoteItemsEmptyState"; import { QuoteItemsSortableDataTableToolbar } from "./QuoteItemsSortableDataTableToolbar"; import { QuoteItemsSortableTableRow } from "./QuoteItemsSortableTableRow"; -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:"); @@ -146,20 +137,14 @@ function EmptyChapterTarget({ > - - - - - {t("quotes.chapters.empty_title")} - {t("quotes.chapters.empty")} - - - - - - - + diff --git a/client/src/app/quotes/components/QuoteItemsAddActions.tsx b/client/src/app/quotes/components/QuoteItemsAddActions.tsx new file mode 100644 index 0000000..b972ee7 --- /dev/null +++ b/client/src/app/quotes/components/QuoteItemsAddActions.tsx @@ -0,0 +1,23 @@ +import type { ButtonProps } from "@/ui"; +import { AppendEmptyRowButton } from "./AppendEmptyRowButton"; +import { AppendCatalogArticleRowButton } from "./AppendCatalogArticleRowButton"; +import { AppendBlockRowButton } from "./AppendBlockRowButton"; + +export interface QuoteItemsAddActionsProps { + onAddRow: () => void; + onAddArticle: () => void; + onAddBlock: () => void; + size?: ButtonProps["size"]; +} + +export function QuoteItemsAddActions({ + onAddRow, onAddArticle, onAddBlock, size, +}: QuoteItemsAddActionsProps) { + return ( + <> + + + + + ); +} diff --git a/client/src/app/quotes/components/QuoteItemsDragOverlay.tsx b/client/src/app/quotes/components/QuoteItemsDragOverlay.tsx new file mode 100644 index 0000000..5dce586 --- /dev/null +++ b/client/src/app/quotes/components/QuoteItemsDragOverlay.tsx @@ -0,0 +1,49 @@ +import { Badge } from "@/ui"; +import { DragOverlay, defaultDropAnimation } from "@dnd-kit/core"; +import type { DropAnimation, UniqueIdentifier } from "@dnd-kit/core"; +import { CSS } from "@dnd-kit/utilities"; +import { createPortal } from "react-dom"; + +const dropAnimationConfig: DropAnimation = { + keyframes({ transform }) { + return [ + { opacity: 1, transform: CSS.Transform.toString(transform.initial) }, + { + opacity: 0, + transform: CSS.Transform.toString({ + ...transform.final, + x: transform.final.x + 5, + y: transform.final.y + 5, + }), + }, + ]; + }, + easing: "ease-out", + sideEffects({ active }) { + active.node.animate([{ opacity: 0 }, { opacity: 1 }], { + duration: defaultDropAnimation.duration, + easing: defaultDropAnimation.easing, + }); + }, +}; + + +export function QuoteItemsDragOverlay({ activeId, selectedCount }: { + activeId: UniqueIdentifier | null | undefined; + selectedCount: number; +}) { + return createPortal( + + {activeId && ( +
+ {selectedCount > 0 && ( + + {selectedCount} + + )} +
+ )} +
, + document.body + ); +} diff --git a/client/src/app/quotes/components/QuoteItemsEmptyState.tsx b/client/src/app/quotes/components/QuoteItemsEmptyState.tsx new file mode 100644 index 0000000..26715a9 --- /dev/null +++ b/client/src/app/quotes/components/QuoteItemsEmptyState.tsx @@ -0,0 +1,32 @@ +import { cn } from "@/lib/utils"; +import { + Empty, EmptyHeader, EmptyMedia, EmptyTitle, EmptyDescription, EmptyContent, +} from "@/ui"; +import { ListPlusIcon } from "lucide-react"; +import type { ReactNode } from "react"; +import { QuoteItemsAddActions } from "./QuoteItemsAddActions"; +import type { QuoteItemsAddActionsProps } from "./QuoteItemsAddActions"; + +interface QuoteItemsEmptyStateProps extends Omit { + title: ReactNode; + description: ReactNode; + icon?: ReactNode; + className?: string; +} + +export function QuoteItemsEmptyState({ + title, description, icon =