diff --git a/packages/frontend/src/components/drag&drop-item.tsx b/packages/frontend/src/components/drag&drop-item.tsx deleted file mode 100644 index a2741c61f..000000000 --- a/packages/frontend/src/components/drag&drop-item.tsx +++ /dev/null @@ -1,108 +0,0 @@ -import { useSortable } from '@dnd-kit/sortable'; -import { CSS } from '@dnd-kit/utilities'; -import { ChevronRight, GripVertical } from 'lucide-react'; -import React from 'react'; - -import { cn } from '../helpers/classnames'; -import { Button } from './ui/button'; - -export const ItemDragAndDrop = ({ - active, - children, - childrenLength, - depth = 0, - id, - indentationWidth = 0, - isDropHere, - isOpenChildren, - onCollapse, - className, -}: { - active: boolean; - children: React.ReactNode; - childrenLength?: number; - className?: string; - depth?: number; - id: number | string; - indentationWidth?: number; - isDropHere: boolean; - isOpenChildren?: boolean; - onCollapse: () => void; -}) => { - const { - attributes, - isDragging, - listeners, - setDraggableNodeRef, - setDroppableNodeRef, - transform, - transition, - } = useSortable({ - id, - animateLayoutChanges: ({ isSorting, wasDragging }) => - !(isSorting || wasDragging), - }); - - const allowOpenChildren = !!(childrenLength && onCollapse); - - return ( -
-
-
- - - {allowOpenChildren && ( - - )} -
- - {children} -
-
- ); -}; diff --git a/packages/frontend/src/components/drag&drop/sortable-list/flat.ts b/packages/frontend/src/components/drag&drop/sortable-list/flat.ts new file mode 100644 index 000000000..780de8a12 --- /dev/null +++ b/packages/frontend/src/components/drag&drop/sortable-list/flat.ts @@ -0,0 +1,29 @@ +import { FlattenedItem, TreeItem } from './types'; + +function flatten>( + items: T[], + parentId: null | number | string = null, + depth = 0, +): FlattenedItem[] { + return items.reduce[]>((acc, item, index) => { + const newItem: FlattenedItem = { + ...item, + parentId, + depth, + index, + collapsed: item.collapsed ?? true, + }; + + return [ + ...acc, + newItem, + ...flatten(item.children as T[], item.id, depth + 1), + ]; + }, []); +} + +export function flattenTree>( + items: T[], +): FlattenedItem[] { + return flatten(items); +} diff --git a/packages/frontend/src/components/drag&drop/sortable-list/item.tsx b/packages/frontend/src/components/drag&drop/sortable-list/item.tsx new file mode 100644 index 000000000..c9c2cecdd --- /dev/null +++ b/packages/frontend/src/components/drag&drop/sortable-list/item.tsx @@ -0,0 +1,103 @@ +import { Button } from '@/components/ui/button'; +import { cn } from '@/helpers/classnames'; +import { useSortable } from '@dnd-kit/sortable'; +import { CSS } from '@dnd-kit/utilities'; +import { ChevronRight, GripVertical } from 'lucide-react'; + +import { TreeItem } from './types'; + +export function SortableTreeItem>({ + childCount, + clone, + depth, + indentationWidth, + collapsed, + className, + id, + onCollapse, + children, + ...props +}: { + childCount?: number; + children: React.ReactNode; + clone?: boolean; + collapsed?: boolean; + depth: number; + id: number | string; + indentationWidth: number; + onCollapse?: () => void; +} & Omit, 'id' | 'style'>) { + const { + attributes, + isSorting, + listeners, + setDraggableNodeRef, + setDroppableNodeRef, + transform, + transition, + } = useSortable({ + id, + animateLayoutChanges: ({ isSorting, wasDragging }) => + isSorting || wasDragging ? false : true, + }); + + return ( +
  • +
    + + {onCollapse && ( + + )} + + {children} + {clone && childCount && childCount > 1 ? ( + + {childCount} + + ) : null} +
    +
  • + ); +} diff --git a/packages/frontend/src/components/drag&drop/sortable-list/list.tsx b/packages/frontend/src/components/drag&drop/sortable-list/list.tsx new file mode 100644 index 000000000..e5782f5a8 --- /dev/null +++ b/packages/frontend/src/components/drag&drop/sortable-list/list.tsx @@ -0,0 +1,273 @@ +'use client'; + +import { + closestCenter, + defaultDropAnimation, + DndContext, + DragEndEvent, + DragMoveEvent, + DragOverEvent, + DragOverlay, + DragStartEvent, + MeasuringStrategy, +} from '@dnd-kit/core'; +import { + arrayMove, + SortableContext, + verticalListSortingStrategy, +} from '@dnd-kit/sortable'; +import { CSS } from '@dnd-kit/utilities'; +import { useTranslations } from 'next-intl'; +import React, { useEffect, useId, useMemo, useState } from 'react'; +import { createPortal } from 'react-dom'; + +import { flattenTree } from './flat'; +import { SortableTreeItem } from './item'; +import { FlattenedItem, TreeItem } from './types'; +import { + buildTree, + getChildCount, + getProjection, + removeChildrenOf, + setProperty, +} from './utilities'; + +const indentationWidth = 50; + +export function DragAndDropSortableList< + T extends TreeItem>, +>({ + data, + componentItem, + onCollapse, + maxDepth, + onDragEnd, +}: { + componentItem: (item: T, parentId: null | number | string) => React.ReactNode; + data: T[]; + maxDepth?: number; + onCollapse?: (props: { id: number | string; isOpen: boolean }) => void; + onDragEnd?: (props: { + id: number | string; + indexToMove: number; + parentId: null | number | string; + }) => void; +}) { + const t = useTranslations('core'); + const [isReadyDocument, setIsReadyDocument] = useState(false); + const [items, setItems] = useState(data); + const [activeId, setActiveId] = useState(null); + const [overId, setOverId] = useState(null); + const [offsetLeft, setOffsetLeft] = useState(0); + const id = useId(); + + // Refetch data + useEffect(() => { + setItems(data); + }, [data]); + + const flattenedItems = useMemo(() => { + const flattenedTree = flattenTree(items); + const collapsedItems = flattenedTree.reduce<(number | string)[]>( + (acc, { children, collapsed, id }) => + collapsed && children.length ? [...acc, id] : acc, + [], + ); + + return removeChildrenOf( + flattenedTree, + activeId ? [activeId, ...collapsedItems] : collapsedItems, + ); + }, [activeId, items]); + + const projected = + activeId && overId + ? getProjection( + flattenedItems, + activeId, + overId, + offsetLeft, + indentationWidth, + maxDepth, + ) + : null; + const activeItem = activeId + ? flattenedItems.find(({ id }) => id === activeId) + : null; + + const sortedIds = useMemo( + () => flattenedItems.map(({ id }) => id), + [flattenedItems], + ); + + function handleDragStart({ active: { id: activeId } }: DragStartEvent) { + setActiveId(activeId); + setOverId(activeId); + + document.body.style.setProperty('cursor', 'grabbing'); + } + + function handleDragMove({ delta }: DragMoveEvent) { + setOffsetLeft(delta.x); + } + + function handleDragOver({ over }: DragOverEvent) { + setOverId(over?.id ?? null); + } + + function resetState() { + setOverId(null); + setActiveId(null); + setOffsetLeft(0); + + document.body.style.setProperty('cursor', ''); + } + + function handleDragEnd({ active, over }: DragEndEvent) { + resetState(); + if (!projected || !over) return; + + const { depth, parentId } = projected; + const clonedItems: FlattenedItem>[] = JSON.parse( + JSON.stringify(flattenTree(items)), + ); + const overIndex = clonedItems.findIndex(({ id }) => id === over.id); + const activeIndex = clonedItems.findIndex(({ id }) => id === active.id); + const activeTreeItem = clonedItems[activeIndex]; + + clonedItems[activeIndex] = { ...activeTreeItem, depth, parentId }; + + const sortedItems = arrayMove(clonedItems, activeIndex, overIndex); + const newItems = buildTree>(sortedItems); + + setItems(newItems as T[]); + + onDragEnd?.({ + id: active.id, + parentId: parentId, + indexToMove: overIndex, + }); + } + + function handleDragCancel() { + resetState(); + } + + function handleCollapse(id: number | string) { + const newItems = setProperty>( + items, + id, + 'collapsed', + value => { + if (value === undefined) { + return false; + } + + return !value; + }, + ); + + onCollapse?.({ + id, + isOpen: newItems.find(item => item.id === id)?.collapsed ?? true, + }); + + setItems(newItems as T[]); + } + + useEffect(() => { + setIsReadyDocument(true); + }, []); + + if (!data.length) { + return ( +
    {t('no_results')}
    + ); + } + + return ( + + + {flattenedItems.map(item => ( + { + handleCollapse(item.id); + } + : undefined + } + > + {componentItem(item as unknown as T, item.parentId)} + + ))} + {isReadyDocument && + createPortal( + + {activeId && activeItem ? ( + + {componentItem( + activeItem as unknown as T, + activeItem.parentId, + )} + + ) : null} + , + document.body, + )} + + + ); +} diff --git a/packages/frontend/src/components/drag&drop/sortable-list/types.ts b/packages/frontend/src/components/drag&drop/sortable-list/types.ts new file mode 100644 index 000000000..a706c49f2 --- /dev/null +++ b/packages/frontend/src/components/drag&drop/sortable-list/types.ts @@ -0,0 +1,11 @@ +export interface TreeItem { + children: TreeItem[]; + collapsed?: boolean; + id: number | string; +} + +export type FlattenedItem = { + depth: number; + index: number; + parentId: null | number | string; +} & T; diff --git a/packages/frontend/src/components/drag&drop/sortable-list/utilities.ts b/packages/frontend/src/components/drag&drop/sortable-list/utilities.ts new file mode 100644 index 000000000..b35e70275 --- /dev/null +++ b/packages/frontend/src/components/drag&drop/sortable-list/utilities.ts @@ -0,0 +1,232 @@ +import { arrayMove } from '@dnd-kit/sortable'; + +import { FlattenedItem, TreeItem } from './types'; + +function getDragDepth(offset: number, indentationWidth: number) { + return Math.round(offset / indentationWidth); +} + +export function getProjection>( + items: FlattenedItem[], + activeId: number | string, + overId: number | string, + dragOffset: number, + indentationWidth: number, + maxDepthProp?: number, +) { + const overItemIndex = items.findIndex(({ id }) => id === overId); + const activeItemIndex = items.findIndex(({ id }) => id === activeId); + const activeItem = items[activeItemIndex]; + const newItems = arrayMove(items, activeItemIndex, overItemIndex); + const previousItem: FlattenedItem | undefined = + newItems[overItemIndex - 1]; + const nextItem = newItems[overItemIndex + 1]; + const dragDepth = getDragDepth(dragOffset, indentationWidth); + const projectedDepth = +activeItem.depth + dragDepth; + const maxDepth = + maxDepthProp ?? + getMaxDepth({ + previousItem, + }); + const minDepth = getMinDepth({ nextItem }); + let depth = projectedDepth; + + if (projectedDepth >= maxDepth) { + depth = maxDepth; + } else if (projectedDepth < minDepth) { + depth = minDepth; + } + + return { depth, maxDepth, minDepth, parentId: getParentId() }; + + function getParentId() { + if (depth === 0 || !previousItem) { + return null; + } + + if (depth === previousItem.depth) { + return previousItem.parentId; + } + + if (depth > previousItem.depth) { + return previousItem.id; + } + + const newParent = newItems + .slice(0, overItemIndex) + .reverse() + .find(item => item.depth === depth)?.parentId; + + return newParent ?? null; + } +} + +function getMaxDepth>({ + previousItem, +}: { + previousItem: FlattenedItem | undefined; +}) { + if (previousItem) { + return previousItem.depth + 1; + } + + return 0; +} + +function getMinDepth>({ + nextItem, +}: { + nextItem: FlattenedItem | undefined; +}) { + if (nextItem) { + return nextItem.depth; + } + + return 0; +} + +export function buildTree>( + flattenedItems: FlattenedItem[], +): T[] { + const root: TreeItem = { + id: 'root', + children: [], + collapsed: true, + }; + + const nodes: Record> = { + [root.id]: root, + }; + + // No need to specify type here; let TypeScript infer it + const items = flattenedItems.map(item => ({ + ...item, + children: [], + })); + + for (const item of items) { + const { id, children } = item; + const parentId = item.parentId ?? root.id; + const parent = nodes[parentId] ?? findItem(items, parentId); + + nodes[id] = { ...item, children }; + parent.children.push(item as T); + } + + return root.children as T[]; +} + +export function findItem>( + items: TreeItem[], + itemId: number | string, +) { + return items.find(({ id }) => id === itemId); +} + +export function findItemDeep>( + items: TreeItem[], + itemId: number | string, +): TreeItem | undefined { + for (const item of items) { + const { id, children } = item; + + if (id === itemId) { + return item; + } + + if (children.length) { + const child = findItemDeep(children, itemId); + + if (child) { + return child; + } + } + } + + return undefined; +} + +export function removeItem>( + items: TreeItem[], + id: number | string, +) { + const newItems = []; + + for (const item of items) { + if (item.id === id) { + continue; + } + + if (item.children.length) { + item.children = removeItem(item.children, id); + } + + newItems.push(item as never); + } + + return newItems; +} + +export function setProperty< + T extends TreeItem, + Y extends keyof T = 'collapsed', +>( + items: T[], + id: number | string, + property: Y, + setter: (value: T[Y]) => T[Y], +): T[] { + for (const item of items) { + if (item.id === id) { + item[property] = setter(item[property]); + continue; + } + + if (item.children.length) { + item.children = setProperty(item.children as T[], id, property, setter); + } + } + + return [...items]; +} + +function countChildren>( + items: TreeItem[], + count = 0, +): number { + return items.reduce((acc, { children }) => { + if (children.length) { + return countChildren(children, acc + 1); + } + + return acc + 1; + }, count); +} + +export function getChildCount>( + items: TreeItem[], + id: number | string, +) { + const item = findItemDeep(items, id); + + return item ? countChildren(item.children) : 0; +} + +export function removeChildrenOf>( + items: FlattenedItem[], + ids: (number | string)[], +) { + const excludeParentIds = [...ids]; + + return items.filter(item => { + if (item.parentId && excludeParentIds.includes(item.parentId)) { + if (item.children.length) { + excludeParentIds.push(item.id); + } + + return false; + } + + return true; + }); +} diff --git a/packages/frontend/src/helpers/flatten-tree.ts b/packages/frontend/src/helpers/flatten-tree.ts deleted file mode 100644 index 5f09e56b0..000000000 --- a/packages/frontend/src/helpers/flatten-tree.ts +++ /dev/null @@ -1,41 +0,0 @@ -export type WithChildren = { - children: WithChildren[]; - id: number | string; -} & Omit; - -export type FlatTree = { - depth: number; - parentId: null | number | string; -} & WithChildren; - -export function flattenTree({ - depth = 0, - parentId = null, - tree, -}: { - depth?: number; - parentId?: null | number | string; - tree: WithChildren[]; -}): FlatTree[] { - return tree.reduce[]>((previousValue, currentValue) => { - const children = - currentValue.children.length > 0 - ? flattenTree({ - tree: currentValue.children, - parentId: currentValue.id, - depth: depth + 1, - }) - : []; - - return [ - ...previousValue, - { - ...currentValue, - parentId: parentId, - depth: depth, - children, - }, - ...children, - ]; - }, []); -} diff --git a/packages/frontend/src/hooks/drag&drop/use-functions.ts b/packages/frontend/src/hooks/drag&drop/use-functions.ts deleted file mode 100644 index 07f9c55a3..000000000 --- a/packages/frontend/src/hooks/drag&drop/use-functions.ts +++ /dev/null @@ -1,270 +0,0 @@ -import { - DragEndEvent, - DragMoveEvent, - DragOverEvent, - DragStartEvent, - UniqueIdentifier, -} from '@dnd-kit/core'; -import { arrayMove } from '@dnd-kit/sortable'; -import React from 'react'; - -import { - flattenTree, - FlatTree, - WithChildren, -} from '../../helpers/flatten-tree'; -import { useProjection } from './use-projection'; - -function removeChildrenOf({ - ids, - tree, -}: { - ids: UniqueIdentifier[]; - tree: FlatTree[]; -}) { - const excludeParentIds = [...ids]; - - return tree.filter(item => { - if (item.parentId && excludeParentIds.includes(item.parentId)) { - if (item.children.length > 0) { - excludeParentIds.push(item.id); - } - - return false; - } - - return true; - }); -} - -export function buildTree({ - flattenedTree, -}: { - flattenedTree: FlatTree[]; -}): WithChildren[] { - const sorted = flattenedTree.sort((a, b) => b.depth - a.depth); - const maxDepth = sorted[0].depth; - let tree: FlatTree[] = []; - - tree = sorted.map(item => { - if (item.depth === maxDepth) { - return item; - } - - return { - ...item, - children: [], - }; - }); - - tree.forEach(item => { - const parentIndex = tree.findIndex(({ id }) => id === item.parentId); - if (parentIndex === -1) return; - const parent = tree[parentIndex]; - - tree[parentIndex] = { - ...parent, - children: [...parent.children, item], - }; - }); - - return tree.filter(item => !item.parentId); -} - -interface OnDragMoveArgs extends DragMoveEvent { - flattenedItems: FlatTree[]; - indentationWidth?: number; - maxDepth?: number; -} - -interface DragEndArgs extends DragEndEvent { - data: WithChildren[]; - setData: (data: WithChildren>[]) => void; -} - -interface Args { - data: WithChildren[]; -} - -export function useDragAndDrop({ data }: Args) { - const [isOpenChildren, setIsOpenChildren] = React.useState< - UniqueIdentifier[] - >([]); - const { - activeId, - getProjection, - overId, - projected, - setActiveId, - setOverId, - setProjected, - } = useProjection(); - const flattenedItems = flattItems({ data }); - const activeItem = flattenedItems.find(i => i.id === activeId); - const sortedIds = React.useMemo( - () => flattenedItems.map(({ id }) => id), - [flattenedItems], - ); - - // DndKit doesn't support nested sortable, so we need to flatten the data in one array - function flattItems>({ data }: { data: T[] }) { - const tree = flattenTree({ tree: data }); - - const collapsedItems = tree.reduce( - (acc, { children, id }) => - !isOpenChildren.includes(id) && children.length ? [...acc, id] : acc, - [], - ); - - return removeChildrenOf({ - tree, - ids: activeId ? [activeId, ...collapsedItems] : collapsedItems, - }); - } - - const resetState = () => { - setOverId(null); - setActiveId(null); - setProjected(null); - }; - - const onDragOver = ({ over }: DragOverEvent) => { - setOverId(over?.id ?? null); - }; - - const onDragStart = ({ active }: DragStartEvent) => { - setActiveId(active.id); - setOverId(active.id); - }; - - const onDragMove = ({ - delta, - flattenedItems, - indentationWidth, - maxDepth, - }: OnDragMoveArgs) => { - if (maxDepth === undefined && !indentationWidth) { - throw new Error('You must provide either maxDepth abd indentationWidth'); - } - - if (!activeId || !overId) return; - - const currentProjection = getProjection({ - flattenedItems, - delta, - indentationWidth, - maxDepth, - }); - - if (projected?.parentId === currentProjection.parentId) { - return; - } - - setProjected(currentProjection); - }; - - function onDragEnd({ - active, - data, - over, - setData, - }: DragEndArgs) { - resetState(); - - if (!projected || !over) return; - const { depth, parentId } = projected; - - const clonedItems: FlatTree[] = flattenTree({ tree: data }); - const toIndex = clonedItems.findIndex(({ id }) => id === over.id); - const fromIndex = clonedItems.findIndex(({ id }) => id === active.id); - const sortedItems = arrayMove(clonedItems, fromIndex, toIndex); - const activeIndex = sortedItems.findIndex(i => i.id === active.id); - sortedItems[activeIndex] = { - ...sortedItems[activeIndex], - depth, - parentId, - }; - - const dataAfterUpdate: FlatTree>[] = sortedItems.map(item => ({ - ...item, - children: [], - })); - - setData( - buildTree({ - flattenedTree: dataAfterUpdate, - }), - ); - - const parents = sortedItems.filter(i => i.parentId === parentId); - const indexToMove = parents.findIndex(i => i.id === active.id); - const flattenedItems = flattenTree({ tree: data }); - - // -1 means that the item is the last one - const findActive = flattenedItems.find(i => i.id === active.id); - if (!findActive) return; - - // Do nothing if drag and drop on the same item on the same level - if (findActive.parentId === parentId && active.id === over.id) { - return; - } - - return { - id: active.id, - parentId: parentId, - indexToMove, - }; - } - - const actionsItem = ({ - data, - indentationWidth, - onCollapse, - }: { - data: { - children?: unknown[]; - depth?: number; - id: number | string; - }; - indentationWidth?: number; - onCollapse?: ({ isOpen }: { isOpen: boolean }) => void; - }) => { - const onCollapseInternal = () => { - const isOpen = isOpenChildren.includes(data.id); - - onCollapse?.({ isOpen }); - - setIsOpenChildren(prev => { - if (isOpen) { - return prev.filter(i => i !== data.id); - } - - return [...prev, data.id]; - }); - }; - - return { - onCollapse: onCollapseInternal, - isOpenChildren: isOpenChildren.includes(data.id), - isDropHere: projected?.parentId === data.id, - active: activeId === data.id, - depth: - (activeId === data.id && projected ? projected.depth : data.depth) ?? 0, - indentationWidth, - id: data.id, - childrenLength: data.children ? data.children.length : 0, - }; - }; - - return { - resetState, - onDragOver, - onDragStart, - onDragMove, - onDragEnd, - actionsItem, - flattenedItems, - activeItemOverlay: activeId !== null && activeItem, - sortedIds, - }; -} diff --git a/packages/frontend/src/hooks/drag&drop/use-projection.ts b/packages/frontend/src/hooks/drag&drop/use-projection.ts deleted file mode 100644 index 89f689da7..000000000 --- a/packages/frontend/src/hooks/drag&drop/use-projection.ts +++ /dev/null @@ -1,119 +0,0 @@ -import { UniqueIdentifier } from '@dnd-kit/core'; -import { arrayMove } from '@dnd-kit/sortable'; -import { Coordinates } from '@dnd-kit/utilities'; -import React from 'react'; - -import { FlatTree } from '../../helpers/flatten-tree'; - -const getDragDepth = ({ - indentationWidth, - offset, -}: { - indentationWidth: number; - offset: number; -}) => { - return Math.round(offset / indentationWidth); -}; - -function getMaxDepth({ - previousItem, -}: { - previousItem: FlatTree | undefined; -}) { - return previousItem ? previousItem.depth + 1 : 0; -} - -function getMinDepth({ - nextItem, -}: { - nextItem: FlatTree | undefined; -}) { - return nextItem ? nextItem.depth : 0; -} - -export interface ProjectionReturnType { - depth: number; - maxDepth: number; - minDepth: number; - parentId: null | number | string; -} - -export const useProjection = () => { - const [activeId, setActiveId] = React.useState(null); - const [overId, setOverId] = React.useState(null); - const [projected, setProjected] = - React.useState(); - - function getProjection({ - delta, - flattenedItems, - indentationWidth = 0, - maxDepth: maxDepthProp, - }: { - delta: Coordinates; - flattenedItems: FlatTree[]; - indentationWidth?: number; - maxDepth?: number; - }): ProjectionReturnType { - const dragOffset = delta.x; - const overItemIndex = flattenedItems.findIndex(({ id }) => id === overId); - const activeItemIndex = flattenedItems.findIndex( - ({ id }) => id === activeId, - ); - const activeItem = flattenedItems[activeItemIndex]; - const newItems = arrayMove(flattenedItems, activeItemIndex, overItemIndex); - const previousItem = newItems.at(overItemIndex - 1); - const nextItem = newItems.at(overItemIndex + 1); - const dragDepth = getDragDepth({ - offset: dragOffset, - indentationWidth, - }); - const projectedDepth = activeItem.depth + dragDepth; - const maxDepth = - maxDepthProp ?? - getMaxDepth({ - previousItem, - }); - const minDepth = getMinDepth({ nextItem }); - let depth = projectedDepth; - - if (projectedDepth >= maxDepth) { - depth = maxDepth; - } else if (projectedDepth < minDepth) { - depth = minDepth; - } - - const getParentId = (): null | number | string => { - if (depth === 0 || !previousItem) { - return null; - } - - if (depth === previousItem.depth) { - return previousItem.parentId; - } - - if (depth > previousItem.depth) { - return previousItem.id; - } - - const newParent = newItems - .slice(0, overItemIndex) - .reverse() - .find(item => item.depth === depth)?.parentId; - - return newParent ?? null; - }; - - return { depth, maxDepth, minDepth, parentId: getParentId() }; - } - - return { - getProjection, - activeId, - overId, - setOverId, - setActiveId, - projected, - setProjected, - }; -}; diff --git a/packages/frontend/src/views/admin/views/core/dashboard/categories-admin-view.tsx b/packages/frontend/src/views/admin/views/core/dashboard/categories-admin-view.tsx new file mode 100644 index 000000000..2a2092504 --- /dev/null +++ b/packages/frontend/src/views/admin/views/core/dashboard/categories-admin-view.tsx @@ -0,0 +1,41 @@ +'use client'; + +import { DragAndDropSortableList } from '@/components/drag&drop/sortable-list/list'; + +export const CategoriesBlogAdminView = () => { + const data = [ + { + id: 'Home', + children: [], + }, + { + id: 'Collections', + children: [ + { id: 'Spring', children: [] }, + { id: 'Summer', children: [] }, + { id: 'Fall', children: [] }, + { id: 'Winter', children: [] }, + ], + }, + { + id: 'About Us', + children: [], + }, + { + id: 'My Account', + children: [ + { id: 'Addresses', children: [] }, + { id: 'Order History', children: [] }, + ], + }, + ]; + + return ( + { + return
    {data.id}
    ; + }} + data={data} + /> + ); +}; diff --git a/packages/frontend/src/views/admin/views/core/dashboard/dashboard-core-admin-view.tsx b/packages/frontend/src/views/admin/views/core/dashboard/dashboard-core-admin-view.tsx index 1d71ea186..90f9afb99 100644 --- a/packages/frontend/src/views/admin/views/core/dashboard/dashboard-core-admin-view.tsx +++ b/packages/frontend/src/views/admin/views/core/dashboard/dashboard-core-admin-view.tsx @@ -8,6 +8,7 @@ import { AlertTriangle, HammerIcon } from 'lucide-react'; import { getTranslations } from 'next-intl/server'; import { WarnReqRestartServer } from '../plugins/warn-req-restart-server'; +import { CategoriesBlogAdminView } from './categories-admin-view'; export const DashboardCoreAdminView = async () => { const [ @@ -44,6 +45,7 @@ export const DashboardCoreAdminView = async () => { + ); }; diff --git a/packages/frontend/src/views/admin/views/core/plugins/views/dev/nav/actions/create/create.tsx b/packages/frontend/src/views/admin/views/core/plugins/views/dev/nav/actions/create/create.tsx index bcf7e5b7e..901973594 100644 --- a/packages/frontend/src/views/admin/views/core/plugins/views/dev/nav/actions/create/create.tsx +++ b/packages/frontend/src/views/admin/views/core/plugins/views/dev/nav/actions/create/create.tsx @@ -13,8 +13,6 @@ import { Plus } from 'lucide-react'; import { useTranslations } from 'next-intl'; import React from 'react'; -import { CreateEditNavDevPluginAdminProps } from '../../create-edit/create-edit'; - const Content = React.lazy(async () => import('../../create-edit/create-edit').then(module => ({ default: module.CreateEditNavDevPluginAdmin, @@ -22,7 +20,7 @@ const Content = React.lazy(async () => ); export const CreateNavDevPluginAdmin = ( - props: CreateEditNavDevPluginAdminProps, + props: React.ComponentProps, ) => { const t = useTranslations('core'); diff --git a/packages/frontend/src/views/admin/views/core/plugins/views/dev/nav/content.tsx b/packages/frontend/src/views/admin/views/core/plugins/views/dev/nav/content.tsx index 938f8363a..55380d813 100644 --- a/packages/frontend/src/views/admin/views/core/plugins/views/dev/nav/content.tsx +++ b/packages/frontend/src/views/admin/views/core/plugins/views/dev/nav/content.tsx @@ -1,22 +1,13 @@ 'use client'; -import { ItemDragAndDrop } from '@/components/drag&drop-item'; +import { DragAndDropSortableList } from '@/components/drag&drop/sortable-list/list'; import { Admin__Core_Plugins__Nav__ShowQuery } from '@/graphql/queries/admin/plugins/dev/nav/admin__core_plugins__nav__show.generated'; -import { ShowAdminNavPluginsObj } from '@/graphql/types'; -import { WithChildren } from '@/helpers/flatten-tree'; -import { useDragAndDrop } from '@/hooks/drag&drop/use-functions'; -import { closestCorners, DndContext, DragOverlay } from '@dnd-kit/core'; -import { - SortableContext, - verticalListSortingStrategy, -} from '@dnd-kit/sortable'; import { useParams } from 'next/navigation'; import { useTranslations } from 'next-intl'; import React from 'react'; import { toast } from 'sonner'; import { mutationChangePositionApi } from './item/hooks/mutation-change-position-api'; -import { ItemNavDevPluginAdminContext } from './item/hooks/use-item-nav-dev-plugin-admin'; import { ItemContentNavDevPluginAdmin } from './item/item'; interface Props extends Admin__Core_Plugins__Nav__ShowQuery { @@ -29,53 +20,32 @@ export const ContentNavDevPluginAdmin = ({ }: Props) => { const t = useTranslations('core'); const { code } = useParams(); - const [initData, setData] = React.useState(edges); - const data: WithChildren[] = initData.map(item => ({ - ...item, - children: - item.children?.map(child => ({ - ...child, - id: child.code, - children: [], - })) ?? [], - id: item.code, - })); - - const { - actionsItem, - activeItemOverlay, - flattenedItems, - onDragEnd, - onDragMove, - onDragOver, - onDragStart, - resetState, - sortedIds, - } = useDragAndDrop({ - data, - }); - - // Revalidate items when edges change - React.useEffect(() => { - setData(edges); - }, [edges]); - - if (data.length === 0) { - return
    {t('no_results')}
    ; - } return ( - { - const moveTo = onDragEnd({ - data, - setData, - ...event, - }); - - if (!moveTo || !code) return; + { + return ( + + ); + }} + data={edges.map(item => ({ + ...item, + children: + item.children?.map(child => ({ + ...child, + id: child.code, + children: [], + })) ?? [], + id: item.code, + }))} + maxDepth={1} + onDragEnd={async moveTo => { + if (!code) return; const mutation = await mutationChangePositionApi({ code: moveTo.id.toString(), @@ -88,58 +58,8 @@ export const ContentNavDevPluginAdmin = ({ toast.error(t('errors.title'), { description: t('errors.internal_server_error'), }); - - return; } - - window.location.reload(); }} - onDragMove={e => { - onDragMove({ ...e, flattenedItems, maxDepth: 1 }); - }} - onDragOver={onDragOver} - onDragStart={onDragStart} - > - - {flattenedItems.map(item => ( - - - - - - ))} - - - {activeItemOverlay && ( - - - - - - )} - - - + /> ); }; diff --git a/packages/frontend/src/views/admin/views/core/plugins/views/dev/nav/create-edit/create-edit.tsx b/packages/frontend/src/views/admin/views/core/plugins/views/dev/nav/create-edit/create-edit.tsx index 17d45290f..f725cc086 100644 --- a/packages/frontend/src/views/admin/views/core/plugins/views/dev/nav/create-edit/create-edit.tsx +++ b/packages/frontend/src/views/admin/views/core/plugins/views/dev/nav/create-edit/create-edit.tsx @@ -14,19 +14,17 @@ import { useTranslations } from 'next-intl'; import { useCreateNavPluginAdmin } from './hooks/use-create-nav-plugin-admin'; -export interface CreateEditNavDevPluginAdminProps { - data?: ShowAdminNavPluginsObj; - dataFromSSR: Admin__Core_Plugins__Nav__ShowQuery['admin__core_plugins__nav__show']; - icons: { icon: React.ReactNode; id: string }[]; - parentId?: string; -} - export const CreateEditNavDevPluginAdmin = ({ data, dataFromSSR, icons, parentId, -}: CreateEditNavDevPluginAdminProps) => { +}: { + data?: ShowAdminNavPluginsObj; + dataFromSSR: Admin__Core_Plugins__Nav__ShowQuery['admin__core_plugins__nav__show']; + icons: { icon: React.ReactNode; id: string }[]; + parentId?: string; +}) => { const t = useTranslations('admin.core.plugins.dev.nav'); const { onSubmit, formSchema } = useCreateNavPluginAdmin({ data, diff --git a/packages/frontend/src/views/admin/views/core/plugins/views/dev/nav/item/actions/actions.tsx b/packages/frontend/src/views/admin/views/core/plugins/views/dev/nav/item/actions/actions.tsx index e07285099..204a761b7 100644 --- a/packages/frontend/src/views/admin/views/core/plugins/views/dev/nav/item/actions/actions.tsx +++ b/packages/frontend/src/views/admin/views/core/plugins/views/dev/nav/item/actions/actions.tsx @@ -1,16 +1,25 @@ -import { ShowAdminNavPluginsObj } from '@/graphql/types'; -import { FlatTree } from '@/helpers/flatten-tree'; +import { Admin__Core_Plugins__Nav__ShowQuery } from '@/graphql/queries/admin/plugins/dev/nav/admin__core_plugins__nav__show.generated'; +import { ItemContentNavDevPluginAdmin } from '../item'; import { DeleteActionTableNavDevPluginAdmin } from './delete/delete'; import { EditActionTableNavDevPluginAdmin } from './edit'; -export const ActionsTableNavDevPluginAdmin = ( - props: FlatTree, -) => { +export const ActionsTableNavDevPluginAdmin = ({ + data, + parentId, + icons, + dataFromSSR, +}: { + dataFromSSR: Admin__Core_Plugins__Nav__ShowQuery['admin__core_plugins__nav__show']; +} & React.ComponentProps) => { return (
    - - + +
    ); }; diff --git a/packages/frontend/src/views/admin/views/core/plugins/views/dev/nav/item/actions/delete/content.tsx b/packages/frontend/src/views/admin/views/core/plugins/views/dev/nav/item/actions/delete/content.tsx index 55aa5c69e..5a84faac6 100644 --- a/packages/frontend/src/views/admin/views/core/plugins/views/dev/nav/item/actions/delete/content.tsx +++ b/packages/frontend/src/views/admin/views/core/plugins/views/dev/nav/item/actions/delete/content.tsx @@ -12,18 +12,17 @@ import { useTranslations } from 'next-intl'; import { useDeleteNavPluginAdmin } from './hooks/use-delete-nav-plugin-admin'; import { SubmitDeleteActionTableNavDevPluginAdmin } from './submit'; -export interface ContentDeleteActionTableNavDevPluginAdminProps - extends Pick { - parentCode?: string; +interface Props extends Pick { + parentId: string | undefined; } export const ContentDeleteActionTableNavDevPluginAdmin = ({ code, - parentCode, -}: ContentDeleteActionTableNavDevPluginAdminProps) => { + parentId, +}: Props) => { const t = useTranslations('admin.core.plugins.dev.nav.delete'); const tCore = useTranslations('core'); - const { onSubmit } = useDeleteNavPluginAdmin({ code, parentCode }); + const { onSubmit } = useDeleteNavPluginAdmin({ code, parentId }); return (
    diff --git a/packages/frontend/src/views/admin/views/core/plugins/views/dev/nav/item/actions/delete/delete.tsx b/packages/frontend/src/views/admin/views/core/plugins/views/dev/nav/item/actions/delete/delete.tsx index efe82f6b3..3608ef397 100644 --- a/packages/frontend/src/views/admin/views/core/plugins/views/dev/nav/item/actions/delete/delete.tsx +++ b/packages/frontend/src/views/admin/views/core/plugins/views/dev/nav/item/actions/delete/delete.tsx @@ -14,17 +14,12 @@ import { Trash2 } from 'lucide-react'; import { useTranslations } from 'next-intl'; import React from 'react'; -import { useItemNavDevPluginAdmin } from '../../hooks/use-item-nav-dev-plugin-admin'; -import { - ContentDeleteActionTableNavDevPluginAdmin, - ContentDeleteActionTableNavDevPluginAdminProps, -} from './content'; +import { ContentDeleteActionTableNavDevPluginAdmin } from './content'; export const DeleteActionTableNavDevPluginAdmin = ( - props: ContentDeleteActionTableNavDevPluginAdminProps, + props: React.ComponentProps, ) => { const t = useTranslations('core'); - const { parentId } = useItemNavDevPluginAdmin(); return ( @@ -47,10 +42,7 @@ export const DeleteActionTableNavDevPluginAdmin = ( - + ); diff --git a/packages/frontend/src/views/admin/views/core/plugins/views/dev/nav/item/actions/delete/hooks/use-delete-nav-plugin-admin.ts b/packages/frontend/src/views/admin/views/core/plugins/views/dev/nav/item/actions/delete/hooks/use-delete-nav-plugin-admin.ts index 6158d76ba..262e64357 100644 --- a/packages/frontend/src/views/admin/views/core/plugins/views/dev/nav/item/actions/delete/hooks/use-delete-nav-plugin-admin.ts +++ b/packages/frontend/src/views/admin/views/core/plugins/views/dev/nav/item/actions/delete/hooks/use-delete-nav-plugin-admin.ts @@ -3,13 +3,13 @@ import { useParams } from 'next/navigation'; import { useTranslations } from 'next-intl'; import { toast } from 'sonner'; -import { ContentDeleteActionTableNavDevPluginAdminProps } from '../content'; +import { ContentDeleteActionTableNavDevPluginAdmin } from '../content'; import { mutationApi } from './mutation-api'; export const useDeleteNavPluginAdmin = ({ code, - parentCode, -}: ContentDeleteActionTableNavDevPluginAdminProps) => { + parentId, +}: React.ComponentProps) => { const t = useTranslations('admin.core.plugins.dev.nav.delete'); const tCore = useTranslations('core'); const { setOpen } = useAlertDialog(); @@ -21,7 +21,7 @@ export const useDeleteNavPluginAdmin = ({ const mutation = await mutationApi({ code, pluginCode: Array.isArray(pluginCode) ? pluginCode[0] : pluginCode, - parentCode, + parentCode: parentId, }); if (mutation?.error) { diff --git a/packages/frontend/src/views/admin/views/core/plugins/views/dev/nav/item/actions/edit.tsx b/packages/frontend/src/views/admin/views/core/plugins/views/dev/nav/item/actions/edit.tsx index 0149a5319..edc59d59b 100644 --- a/packages/frontend/src/views/admin/views/core/plugins/views/dev/nav/item/actions/edit.tsx +++ b/packages/frontend/src/views/admin/views/core/plugins/views/dev/nav/item/actions/edit.tsx @@ -13,14 +13,10 @@ import { TooltipProvider, TooltipTrigger, } from '@/components/ui/tooltip'; -import { ShowAdminNavPluginsObj } from '@/graphql/types'; -import { FlatTree } from '@/helpers/flatten-tree'; import { Pencil } from 'lucide-react'; import { useTranslations } from 'next-intl'; import React from 'react'; -import { useItemNavDevPluginAdmin } from '../hooks/use-item-nav-dev-plugin-admin'; - const Content = React.lazy(async () => import('../../create-edit/create-edit').then(module => ({ default: module.CreateEditNavDevPluginAdmin, @@ -28,10 +24,9 @@ const Content = React.lazy(async () => ); export const EditActionTableNavDevPluginAdmin = ( - data: FlatTree, + props: React.ComponentProps, ) => { const t = useTranslations('admin.core.plugins.dev.nav'); - const rest = useItemNavDevPluginAdmin(); return ( @@ -55,7 +50,7 @@ export const EditActionTableNavDevPluginAdmin = ( }> - + diff --git a/packages/frontend/src/views/admin/views/core/plugins/views/dev/nav/item/hooks/use-item-nav-dev-plugin-admin.ts b/packages/frontend/src/views/admin/views/core/plugins/views/dev/nav/item/hooks/use-item-nav-dev-plugin-admin.ts deleted file mode 100644 index 9996deb92..000000000 --- a/packages/frontend/src/views/admin/views/core/plugins/views/dev/nav/item/hooks/use-item-nav-dev-plugin-admin.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { ShowAdminNavPluginsObj } from '@/graphql/types'; -import React from 'react'; - -interface Args { - dataFromSSR: ShowAdminNavPluginsObj[]; - icons: { icon: React.ReactNode; id: string }[]; - parentId?: string; -} - -export const ItemNavDevPluginAdminContext = React.createContext({ - dataFromSSR: [], - icons: [], -}); - -export const useItemNavDevPluginAdmin = () => - React.useContext(ItemNavDevPluginAdminContext); diff --git a/packages/frontend/src/views/admin/views/core/plugins/views/dev/nav/item/item.tsx b/packages/frontend/src/views/admin/views/core/plugins/views/dev/nav/item/item.tsx index 23f6257e1..dd3f6259b 100644 --- a/packages/frontend/src/views/admin/views/core/plugins/views/dev/nav/item/item.tsx +++ b/packages/frontend/src/views/admin/views/core/plugins/views/dev/nav/item/item.tsx @@ -1,21 +1,27 @@ +import { Admin__Core_Plugins__Nav__ShowQuery } from '@/graphql/queries/admin/plugins/dev/nav/admin__core_plugins__nav__show.generated'; import { ShowAdminNavPluginsObj } from '@/graphql/types'; -import { FlatTree } from '@/helpers/flatten-tree'; import { useParams } from 'next/navigation'; import { useTranslations } from 'next-intl'; import { ActionsTableNavDevPluginAdmin } from './actions/actions'; -import { useItemNavDevPluginAdmin } from './hooks/use-item-nav-dev-plugin-admin'; -export const ItemContentNavDevPluginAdmin = ( - data: FlatTree, -) => { +export const ItemContentNavDevPluginAdmin = ({ + data, + parentId, + icons, + dataFromSSR, +}: { + data: ShowAdminNavPluginsObj; + dataFromSSR: Admin__Core_Plugins__Nav__ShowQuery['admin__core_plugins__nav__show']; + icons: { icon: React.ReactNode; id: string }[]; + parentId?: string; +}) => { const { code: pluginCode } = useParams(); // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-expect-error const t = useTranslations(`admin_${pluginCode}.nav`); const tAdmin = useTranslations('admin.core.plugins.dev.nav'); const tCore = useTranslations('core'); - const { parentId, icons } = useItemNavDevPluginAdmin(); const langKey = parentId ? `${parentId}_${data.code}` : data.code; return ( @@ -52,7 +58,12 @@ export const ItemContentNavDevPluginAdmin = (

    - + ); }; diff --git a/packages/frontend/src/views/admin/views/core/plugins/views/dev/nav/nav.tsx b/packages/frontend/src/views/admin/views/core/plugins/views/dev/nav/nav.tsx index e112308b4..e6f2720f2 100644 --- a/packages/frontend/src/views/admin/views/core/plugins/views/dev/nav/nav.tsx +++ b/packages/frontend/src/views/admin/views/core/plugins/views/dev/nav/nav.tsx @@ -1,3 +1,4 @@ +import { flattenTree } from '@/components/drag&drop/sortable-list/flat'; import { Icon } from '@/components/icon/icon'; import { HeaderContent } from '@/components/ui/header-content'; import { fetcher } from '@/graphql/fetcher'; @@ -7,7 +8,6 @@ import { Admin__Core_Plugins__Nav__ShowQueryVariables, } from '@/graphql/queries/admin/plugins/dev/nav/admin__core_plugins__nav__show.generated'; import { ShowAdminNavPluginsObj } from '@/graphql/types'; -import { flattenTree } from '@/helpers/flatten-tree'; import { getTranslations } from 'next-intl/server'; import { CreateNavDevPluginAdmin } from './actions/create/create'; @@ -27,6 +27,11 @@ const getData = async ( return data; }; +interface NavItem extends Omit { + children: NavItem[]; + id: string; +} + export interface NavDevPluginAdminViewProps { params: { code: string }; } @@ -39,18 +44,17 @@ export const NavDevPluginAdminView = async ({ getTranslations('admin.core.plugins.dev.nav'), ]); - const flattenData = flattenTree({ - tree: data.admin__core_plugins__nav__show.map(nav => ({ + const flattenData = flattenTree( + data.admin__core_plugins__nav__show.map(nav => ({ id: nav.code, ...nav, - children: - nav.children?.map(child => ({ - id: `${nav.code}_${child.code}`, - ...child, - children: [], - })) ?? [], + children: (nav.children?.map(child => ({ + id: `${nav.code}_${child.code}`, + ...child, + children: [], + })) ?? []) as NavItem[], })), - }); + ); const icons: { icon: React.ReactNode; diff --git a/packages/frontend/src/views/admin/views/core/styles/nav/nav-admin-view.tsx b/packages/frontend/src/views/admin/views/core/styles/nav/nav-admin-view.tsx index 855ec1c40..9cfa279e8 100644 --- a/packages/frontend/src/views/admin/views/core/styles/nav/nav-admin-view.tsx +++ b/packages/frontend/src/views/admin/views/core/styles/nav/nav-admin-view.tsx @@ -1,4 +1,3 @@ -import { Card } from '@/components/ui/card'; import { HeaderContent } from '@/components/ui/header-content'; import { fetcher } from '@/graphql/fetcher'; import { @@ -44,9 +43,7 @@ export const NavAdminView = async () => { - - - + ); }; diff --git a/packages/frontend/src/views/admin/views/core/styles/nav/table/item.tsx b/packages/frontend/src/views/admin/views/core/styles/nav/table/item.tsx deleted file mode 100644 index 692b2eede..000000000 --- a/packages/frontend/src/views/admin/views/core/styles/nav/table/item.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { ShowCoreNav } from '@/graphql/types'; -import { FlatTree } from '@/helpers/flatten-tree'; -import { useTextLang } from '@/hooks/use-text-lang'; -import { ExternalLink } from 'lucide-react'; -import { useTranslations } from 'next-intl'; - -import { ActionsTableNavAdmin } from './actions/actions'; - -export const ItemContentTableContentNavAdmin = ({ - data, -}: { - data: FlatTree>; -}) => { - const t = useTranslations('admin.core.styles.nav'); - const { convertText } = useTextLang(); - - return ( - <> -
    -
    - - {convertText(data.name)} - -
    - - - {t('href', { href: data.href })}{' '} - {data.external && } - - - {data.description.length > 0 && ( - - {convertText(data.description)} - - )} -
    - - - - ); -}; diff --git a/packages/frontend/src/views/admin/views/core/styles/nav/table/table.tsx b/packages/frontend/src/views/admin/views/core/styles/nav/table/table.tsx index 4fd0b44bc..9c29047fc 100644 --- a/packages/frontend/src/views/admin/views/core/styles/nav/table/table.tsx +++ b/packages/frontend/src/views/admin/views/core/styles/nav/table/table.tsx @@ -1,70 +1,56 @@ 'use client'; -import { ItemDragAndDrop } from '@/components/drag&drop-item'; +import { DragAndDropSortableList } from '@/components/drag&drop/sortable-list/list'; import { Admin__Core_Nav__ShowQuery } from '@/graphql/queries/admin/styles/nav/admin__core_nav__show.generated'; -import { ShowCoreNav } from '@/graphql/types'; -import { useDragAndDrop } from '@/hooks/drag&drop/use-functions'; -import { closestCorners, DndContext, DragOverlay } from '@dnd-kit/core'; -import { - SortableContext, - verticalListSortingStrategy, -} from '@dnd-kit/sortable'; +import { useTextLang } from '@/hooks/use-text-lang'; +import { ExternalLink } from 'lucide-react'; import { useTranslations } from 'next-intl'; import React from 'react'; import { toast } from 'sonner'; +import { ActionsTableNavAdmin } from './actions/actions'; import { mutationChangePositionApi } from './hooks/mutation-change-position-api'; -import { ItemContentTableContentNavAdmin } from './item'; - -const indentationWidth = 20; export const TableNavAdmin = ({ core_nav__show: { edges }, }: Admin__Core_Nav__ShowQuery) => { - const t = useTranslations('core'); - const [initData, setData] = - React.useState[]>(edges); - const data = initData.map(item => ({ - ...item, - children: item.children.map(child => ({ ...child, children: [] })), - })); - - const { - actionsItem, - activeItemOverlay, - flattenedItems, - onDragEnd, - onDragMove, - onDragOver, - onDragStart, - resetState, - sortedIds, - } = useDragAndDrop>({ - data, - }); - - // Revalidate items when edges change - React.useEffect(() => { - setData(edges); - }, [edges]); - - if (data.length === 0) { - return
    {t('no_results')}
    ; - } + const t = useTranslations('admin.core.styles.nav'); + const tCore = useTranslations('core.errors'); + const { convertText } = useTextLang(); return ( - { - const moveTo = onDragEnd({ - data, - setData, - ...event, - }); + { + return ( +
    +
    +
    + + {convertText(data.name)} + +
    - if (!moveTo) return; + + {t('href', { href: data.href })}{' '} + {data.external && } + + {data.description.length > 0 && ( + + {convertText(data.description)} + + )} +
    + +
    + ); + }} + data={edges.map(item => ({ + ...item, + children: item.children.map(child => ({ ...child, children: [] })), + }))} + maxDepth={1} + onDragEnd={async moveTo => { try { await mutationChangePositionApi({ id: Number(moveTo.id), @@ -72,42 +58,11 @@ export const TableNavAdmin = ({ parentId: Number(moveTo.parentId), }); } catch (_) { - toast.error(t('errors.title'), { - description: t('errors.internal_server_error'), + toast.error(tCore('title'), { + description: tCore('internal_server_error'), }); } }} - onDragMove={e => { - onDragMove({ ...e, flattenedItems, indentationWidth, maxDepth: 1 }); - }} - onDragOver={onDragOver} - onDragStart={onDragStart} - > - - {flattenedItems.map(item => ( - - - - ))} - - - {activeItemOverlay && ( - - - - )} - - -
    + /> ); };