From 467a82973584ce0533e67a4cf1c4f4c150ab6f06 Mon Sep 17 00:00:00 2001 From: aXenDeveloper Date: Sat, 24 Feb 2024 09:39:34 +0100 Subject: [PATCH 01/16] feat(admin): Add edit button with form for forum --- .../forum/views/forums/actions/actions.tsx | 8 ++--- .../content/hooks/query-api.ts | 0 .../hooks/use-permissions-groups-admin.ts | 0 .../content/main.tsx | 0 .../content/permissions/content.tsx | 0 .../content/permissions/permissions.tsx | 0 .../create-edit.tsx} | 2 +- .../hooks/mutation-create-api.ts | 0 .../hooks/use-create-edit-form-forum-admin.ts | 0 .../views/forums/table/content-table.tsx | 3 +- .../forums/table/item/actions/actions.tsx | 9 +++++ .../views/forums/table/item/actions/edit.tsx | 33 +++++++++++++++++++ .../forum/views/forums/table/item/item.tsx | 32 ++++++++++-------- 13 files changed, 67 insertions(+), 20 deletions(-) rename frontend/admin/forum/views/forums/{create-edit-form => create-edit}/content/hooks/query-api.ts (100%) rename frontend/admin/forum/views/forums/{create-edit-form => create-edit}/content/hooks/use-permissions-groups-admin.ts (100%) rename frontend/admin/forum/views/forums/{create-edit-form => create-edit}/content/main.tsx (100%) rename frontend/admin/forum/views/forums/{create-edit-form => create-edit}/content/permissions/content.tsx (100%) rename frontend/admin/forum/views/forums/{create-edit-form => create-edit}/content/permissions/permissions.tsx (100%) rename frontend/admin/forum/views/forums/{create-edit-form/create-edit-form-forum-admin.tsx => create-edit/create-edit.tsx} (98%) rename frontend/admin/forum/views/forums/{create-edit-form => create-edit}/hooks/mutation-create-api.ts (100%) rename frontend/admin/forum/views/forums/{create-edit-form => create-edit}/hooks/use-create-edit-form-forum-admin.ts (100%) create mode 100644 frontend/admin/forum/views/forums/table/item/actions/actions.tsx create mode 100644 frontend/admin/forum/views/forums/table/item/actions/edit.tsx diff --git a/frontend/admin/forum/views/forums/actions/actions.tsx b/frontend/admin/forum/views/forums/actions/actions.tsx index de714d1ce..2fb0843d3 100644 --- a/frontend/admin/forum/views/forums/actions/actions.tsx +++ b/frontend/admin/forum/views/forums/actions/actions.tsx @@ -8,9 +8,9 @@ import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog"; import { Button } from "@/components/ui/button"; import { Loader } from "@/components/loader"; -const CreateEditFormForumAdmin = lazy(() => - import("../create-edit-form/create-edit-form-forum-admin").then(module => ({ - default: module.CreateEditFormForumAdmin +const Content = lazy(() => + import("../create-edit/create-edit").then(module => ({ + default: module.CreateEditForumAdmin })) ); @@ -28,7 +28,7 @@ export const ActionsForumsForumAdmin = () => { }> - + diff --git a/frontend/admin/forum/views/forums/create-edit-form/content/hooks/query-api.ts b/frontend/admin/forum/views/forums/create-edit/content/hooks/query-api.ts similarity index 100% rename from frontend/admin/forum/views/forums/create-edit-form/content/hooks/query-api.ts rename to frontend/admin/forum/views/forums/create-edit/content/hooks/query-api.ts diff --git a/frontend/admin/forum/views/forums/create-edit-form/content/hooks/use-permissions-groups-admin.ts b/frontend/admin/forum/views/forums/create-edit/content/hooks/use-permissions-groups-admin.ts similarity index 100% rename from frontend/admin/forum/views/forums/create-edit-form/content/hooks/use-permissions-groups-admin.ts rename to frontend/admin/forum/views/forums/create-edit/content/hooks/use-permissions-groups-admin.ts diff --git a/frontend/admin/forum/views/forums/create-edit-form/content/main.tsx b/frontend/admin/forum/views/forums/create-edit/content/main.tsx similarity index 100% rename from frontend/admin/forum/views/forums/create-edit-form/content/main.tsx rename to frontend/admin/forum/views/forums/create-edit/content/main.tsx diff --git a/frontend/admin/forum/views/forums/create-edit-form/content/permissions/content.tsx b/frontend/admin/forum/views/forums/create-edit/content/permissions/content.tsx similarity index 100% rename from frontend/admin/forum/views/forums/create-edit-form/content/permissions/content.tsx rename to frontend/admin/forum/views/forums/create-edit/content/permissions/content.tsx diff --git a/frontend/admin/forum/views/forums/create-edit-form/content/permissions/permissions.tsx b/frontend/admin/forum/views/forums/create-edit/content/permissions/permissions.tsx similarity index 100% rename from frontend/admin/forum/views/forums/create-edit-form/content/permissions/permissions.tsx rename to frontend/admin/forum/views/forums/create-edit/content/permissions/permissions.tsx diff --git a/frontend/admin/forum/views/forums/create-edit-form/create-edit-form-forum-admin.tsx b/frontend/admin/forum/views/forums/create-edit/create-edit.tsx similarity index 98% rename from frontend/admin/forum/views/forums/create-edit-form/create-edit-form-forum-admin.tsx rename to frontend/admin/forum/views/forums/create-edit/create-edit.tsx index 00f3618fa..4391cf381 100644 --- a/frontend/admin/forum/views/forums/create-edit-form/create-edit-form-forum-admin.tsx +++ b/frontend/admin/forum/views/forums/create-edit/create-edit.tsx @@ -29,7 +29,7 @@ enum TabsEnum { PERMISSIONS = "permissions" } -export const CreateEditFormForumAdmin = () => { +export const CreateEditForumAdmin = () => { const t = useTranslations("admin.forum.forums"); const tCore = useTranslations("core"); const { form, onSubmit } = useCreateEditFormForumAdmin(); diff --git a/frontend/admin/forum/views/forums/create-edit-form/hooks/mutation-create-api.ts b/frontend/admin/forum/views/forums/create-edit/hooks/mutation-create-api.ts similarity index 100% rename from frontend/admin/forum/views/forums/create-edit-form/hooks/mutation-create-api.ts rename to frontend/admin/forum/views/forums/create-edit/hooks/mutation-create-api.ts diff --git a/frontend/admin/forum/views/forums/create-edit-form/hooks/use-create-edit-form-forum-admin.ts b/frontend/admin/forum/views/forums/create-edit/hooks/use-create-edit-form-forum-admin.ts similarity index 100% rename from frontend/admin/forum/views/forums/create-edit-form/hooks/use-create-edit-form-forum-admin.ts rename to frontend/admin/forum/views/forums/create-edit/hooks/use-create-edit-form-forum-admin.ts diff --git a/frontend/admin/forum/views/forums/table/content-table.tsx b/frontend/admin/forum/views/forums/table/content-table.tsx index 9e97ca3b0..555fba412 100644 --- a/frontend/admin/forum/views/forums/table/content-table.tsx +++ b/frontend/admin/forum/views/forums/table/content-table.tsx @@ -87,8 +87,9 @@ export const ContentTableForumsForumAdmin = () => { if (isLoading) return ; if (isError) return ; - if (!data || data.length === 0) + if (!data || data.length === 0) { return
{t("no_results")}
; + } return ( { + return ( +
+ +
+ ); +}; diff --git a/frontend/admin/forum/views/forums/table/item/actions/edit.tsx b/frontend/admin/forum/views/forums/table/item/actions/edit.tsx new file mode 100644 index 000000000..ecc50d586 --- /dev/null +++ b/frontend/admin/forum/views/forums/table/item/actions/edit.tsx @@ -0,0 +1,33 @@ +import { Pencil } from "lucide-react"; +import { useTranslations } from "next-intl"; +import { Suspense, lazy } from "react"; + +import { Loader } from "@/components/loader"; +import { Button } from "@/components/ui/button"; +import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog"; + +const Content = lazy(() => + import("../../../create-edit/create-edit").then(module => ({ + default: module.CreateEditForumAdmin + })) +); + +export const EditActionForumAdmin = () => { + const t = useTranslations("core"); + + return ( + + + + + + + }> + + + + + ); +}; diff --git a/frontend/admin/forum/views/forums/table/item/item.tsx b/frontend/admin/forum/views/forums/table/item/item.tsx index 91d7baf35..b9a1a5200 100644 --- a/frontend/admin/forum/views/forums/table/item/item.tsx +++ b/frontend/admin/forum/views/forums/table/item/item.tsx @@ -9,6 +9,7 @@ import { useTextLang } from "@/hooks/core/use-text-lang"; import { cn } from "@/functions/classnames"; import { useChildrenForumForumsAdminAPI } from "./hooks/use-children-forum-forums-admin-api"; import type { Admin__Forum_Forums__ShowFlattenedItem } from "../types"; +import { ActionsForumAdmin } from "./actions/actions"; interface Props extends Admin__Forum_Forums__ShowFlattenedItem { indentationWidth: number; @@ -58,18 +59,10 @@ export const ItemTableForumsForumAdmin = ({ "--spacing": `${indentationWidth * depth}px` } as CSSProperties } - onClick={() => { - if (allowOpenChildren) onCollapse?.(id); - }} - role={allowOpenChildren ? "button" : undefined} - tabIndex={allowOpenChildren ? 0 : -1} - onKeyDown={e => { - if (e.key === "Enter" && allowOpenChildren) onCollapse?.(id); - }} >
{childrenCount > 0 && ( - + )}
{convertText(name)}
+ +
); From df09b13f6ecc75a8b4aba1c479cbe0872ec824dc Mon Sep 17 00:00:00 2001 From: aXenDeveloper Date: Sat, 24 Feb 2024 19:05:37 +0100 Subject: [PATCH 02/16] refactor(frontend): Improve drag and drop hook --- backend/src/forum/forums/show/dto/show.obj.ts | 8 +- backend/src/schema.gql | 8 +- .../views/forums/table/content-table.tsx | 69 ++++------ .../table/hooks/use-forum-forums-admin-api.ts | 17 ++- .../forum/views/forums/table/item/item.tsx | 5 +- .../forum/views/forums/table/use-functions.ts | 130 ++++++++++++++++++ .../views/forums/table/use-projection.ts | 107 ++++++++++++++ frontend/graphql/hooks.ts | 14 +- 8 files changed, 293 insertions(+), 65 deletions(-) create mode 100644 frontend/admin/forum/views/forums/table/use-functions.ts create mode 100644 frontend/admin/forum/views/forums/table/use-projection.ts diff --git a/backend/src/forum/forums/show/dto/show.obj.ts b/backend/src/forum/forums/show/dto/show.obj.ts index 80c8f2a9f..8698ceb72 100644 --- a/backend/src/forum/forums/show/dto/show.obj.ts +++ b/backend/src/forum/forums/show/dto/show.obj.ts @@ -54,8 +54,8 @@ export class FirstShowForumForums extends ShowForumForums { @ObjectType() export class ChildrenShowForumForums extends ShowForumForums { - @Field(() => [LastChildShowForumForums], { nullable: true }) - children: LastChildShowForumForums[] | null; + @Field(() => [LastChildShowForumForums]) + children: LastChildShowForumForums[]; } @ObjectType() @@ -63,8 +63,8 @@ export class ShowForumForumsWithParent extends FirstShowForumForums { @Field(() => ShowForumForums, { nullable: true }) parent: ShowForumForums | null; - @Field(() => [ChildrenShowForumForums], { nullable: true }) - children: ChildrenShowForumForums[] | null; + @Field(() => [ChildrenShowForumForums]) + children: ChildrenShowForumForums[]; } @ObjectType() diff --git a/backend/src/schema.gql b/backend/src/schema.gql index 2044aa69d..b97c99a7c 100644 --- a/backend/src/schema.gql +++ b/backend/src/schema.gql @@ -34,7 +34,7 @@ type AvatarUser { type ChildrenShowForumForums { _count: ShowForumForumsCount! - children: [LastChildShowForumForums!] + children: [LastChildShowForumForums!]! created: Int! description: [TextLanguage!]! id: Int! @@ -44,7 +44,7 @@ type ChildrenShowForumForums { type CreateForumForumsObj { _count: ShowForumForumsCount! - children: [ChildrenShowForumForums!] + children: [ChildrenShowForumForums!]! created: Int! description: [TextLanguage!]! id: Int! @@ -438,7 +438,7 @@ type ShowForumForums { type ShowForumForumsAdmin { _count: ShowForumForumsCount! - children: [ChildrenShowForumForums!] + children: [ChildrenShowForumForums!]! created: Int! description: [TextLanguage!]! id: Int! @@ -463,7 +463,7 @@ type ShowForumForumsObj { type ShowForumForumsWithParent { _count: ShowForumForumsCount! - children: [ChildrenShowForumForums!] + children: [ChildrenShowForumForums!]! created: Int! description: [TextLanguage!]! id: Int! diff --git a/frontend/admin/forum/views/forums/table/content-table.tsx b/frontend/admin/forum/views/forums/table/content-table.tsx index 555fba412..f2e70be32 100644 --- a/frontend/admin/forum/views/forums/table/content-table.tsx +++ b/frontend/admin/forum/views/forums/table/content-table.tsx @@ -6,8 +6,7 @@ import { PointerSensor, useSensor, useSensors, - MeasuringStrategy, - type UniqueIdentifier + MeasuringStrategy } from "@dnd-kit/core"; import { SortableContext, @@ -20,22 +19,17 @@ import { useQueryClient, type InfiniteData } from "@tanstack/react-query"; import { useTranslations } from "next-intl"; import { ItemTableForumsForumAdmin } from "./item/item"; -import { useForumForumsAdminAPI } from "./hooks/use-forum-forums-admin-api"; import { - buildTree, - flattenTree, - getForumProjection, - removeChildrenOf -} from "./functions"; -import type { - Admin__Forum_Forums__ShowFlattenedItem, - Admin__Forum_Forums__ShowWithProjection -} from "./types"; + useForumForumsAdminAPI, + type Admin__Forum_Forums__ShowQueryItem +} from "./hooks/use-forum-forums-admin-api"; import { Loader } from "@/components/loader"; import { ErrorAdminView } from "@/admin/core/global/error-admin-view"; import { APIKeys } from "@/graphql/api-keys"; import type { Admin__Forum_Forums__ShowQuery } from "@/graphql/hooks"; import { mutationChangePositionApi } from "./hooks/mutation-change-position-api"; +import { useDragAndDrop } from "./use-functions"; +import { useProjection, type ProjectionReturnType } from "./use-projection"; const indentationWidth = 20; @@ -44,11 +38,11 @@ export const ContentTableForumsForumAdmin = () => { const { data, fetchNextPage, hasNextPage, isError, isLoading } = useForumForumsAdminAPI(); const queryClient = useQueryClient(); - const [activeId, setActiveId] = useState(null); - const [overId, setOverId] = useState(null); - const [projected, setProjected] = - useState(); - const [isOpenChildren, setIsOpenChildren] = useState([]); + const [projected, setProjected] = useState(); + + const { activeId, getProjection, overId, setActiveId, setOverId } = + useProjection(); + const testDragAndDrop = useDragAndDrop({ activeId }); const resetState = () => { setOverId(null); @@ -63,23 +57,7 @@ export const ContentTableForumsForumAdmin = () => { }) ); - // DndKit doesn't support nested sortable, so we need to flatten the data in one array - const flattenedItems: Admin__Forum_Forums__ShowFlattenedItem[] = - useMemo(() => { - const tree = flattenTree(data); - - const collapsedItems = tree.reduce( - (acc, { children, id }) => - !isOpenChildren.includes(id) && children?.length ? [...acc, id] : acc, - [] - ); - - return removeChildrenOf({ - items: tree, - ids: activeId ? [activeId, ...collapsedItems] : collapsedItems - }); - }, [data, activeId, isOpenChildren]); - + const flattenedItems = testDragAndDrop.flattenedItems({ data }); const sortedIds = useMemo( () => flattenedItems.map(({ id }) => id), [flattenedItems] @@ -105,13 +83,11 @@ export const ContentTableForumsForumAdmin = () => { onDragMove={({ delta }) => { if (!activeId || !overId) return; - const currentProjection = getForumProjection( - flattenedItems, - activeId, - overId, - delta.x, + const currentProjection = getProjection({ + tree: flattenedItems, + dragOffset: delta.x, indentationWidth - ); + }); if (projected?.parentId === currentProjection.parentId) { return; @@ -128,8 +104,7 @@ export const ContentTableForumsForumAdmin = () => { if (!projected || !over) return; const { depth, parentId } = projected; - const clonedItems: Admin__Forum_Forums__ShowFlattenedItem[] = - JSON.parse(JSON.stringify(flattenTree(data))); + const clonedItems = testDragAndDrop.flattenTree({ tree: data }); const overIndex = clonedItems.findIndex(({ id }) => id === over.id); const activeIndex = clonedItems.findIndex(({ id }) => id === active.id); @@ -164,7 +139,9 @@ export const ContentTableForumsForumAdmin = () => { ...lastPage, admin__forum_forums__show: { ...lastPage.admin__forum_forums__show, - edges: buildTree(sortedItems) + edges: testDragAndDrop.buildTree({ + flattenedTree: sortedItems + }) as Admin__Forum_Forums__ShowQueryItem[] } } ], @@ -228,7 +205,7 @@ export const ContentTableForumsForumAdmin = () => { key={item.id} indentationWidth={indentationWidth} onCollapse={id => { - setIsOpenChildren(prev => { + testDragAndDrop.setIsOpenChildren(prev => { if (prev.includes(id)) { return prev.filter(i => i !== id); } @@ -236,7 +213,9 @@ export const ContentTableForumsForumAdmin = () => { return [...prev, id]; }); }} - isOpenChildren={isOpenChildren.includes(item.id)} + isOpenChildren={testDragAndDrop.isOpenChildren.includes( + item.id + )} isDropHere={projected?.parentId === item.id} {...item} /> diff --git a/frontend/admin/forum/views/forums/table/hooks/use-forum-forums-admin-api.ts b/frontend/admin/forum/views/forums/table/hooks/use-forum-forums-admin-api.ts index 05e6c3dc5..6d4923360 100644 --- a/frontend/admin/forum/views/forums/table/hooks/use-forum-forums-admin-api.ts +++ b/frontend/admin/forum/views/forums/table/hooks/use-forum-forums-admin-api.ts @@ -8,6 +8,11 @@ import { queryApi } from "./query-api"; export interface Admin__Forum_Forums__ShowQueryItem extends Omit {} +export interface ShowForumForumsAdminWithChildren + extends Omit { + children: ShowForumForumsAdminWithChildren[]; +} + export const useForumForumsAdminAPI = () => { const query = useInfiniteQuery({ queryKey: [APIKeys.FORUMS_ADMIN], @@ -27,10 +32,16 @@ export const useForumForumsAdminAPI = () => { } }); - const data: ShowForumForumsAdmin[] = useMemo(() => { + const data: ShowForumForumsAdminWithChildren[] = useMemo(() => { return ( - query.data?.pages.flatMap( - ({ admin__forum_forums__show: { edges } }) => edges + query.data?.pages.flatMap(({ admin__forum_forums__show: { edges } }) => + edges.map(item => ({ + ...item, + children: + item.children.length > 0 + ? (item.children as unknown as ShowForumForumsAdminWithChildren[]) // Convert to the correct type, drizzle don't support recursive types + : [] + })) ) ?? [] ); }, [query.data]); diff --git a/frontend/admin/forum/views/forums/table/item/item.tsx b/frontend/admin/forum/views/forums/table/item/item.tsx index b9a1a5200..866077470 100644 --- a/frontend/admin/forum/views/forums/table/item/item.tsx +++ b/frontend/admin/forum/views/forums/table/item/item.tsx @@ -8,10 +8,11 @@ import { Button } from "@/components/ui/button"; import { useTextLang } from "@/hooks/core/use-text-lang"; import { cn } from "@/functions/classnames"; import { useChildrenForumForumsAdminAPI } from "./hooks/use-children-forum-forums-admin-api"; -import type { Admin__Forum_Forums__ShowFlattenedItem } from "../types"; import { ActionsForumAdmin } from "./actions/actions"; +import type { ShowForumForumsAdminWithChildren } from "../hooks/use-forum-forums-admin-api"; +import type { FlatTree } from "../use-functions"; -interface Props extends Admin__Forum_Forums__ShowFlattenedItem { +interface Props extends FlatTree { indentationWidth: number; isOpenChildren: boolean; isDropHere?: boolean; diff --git a/frontend/admin/forum/views/forums/table/use-functions.ts b/frontend/admin/forum/views/forums/table/use-functions.ts new file mode 100644 index 000000000..cd855c17d --- /dev/null +++ b/frontend/admin/forum/views/forums/table/use-functions.ts @@ -0,0 +1,130 @@ +import type { UniqueIdentifier } from "@dnd-kit/core"; +import { useState } from "react"; + +type WithChildren = Omit & { + children: T[]; + id: number; +}; + +export type FlatTree = { + depth: number; + index: number; + parentId: number; +} & WithChildren; + +function flattenTree>({ + depth = 0, + parentId = 0, + tree +}: { + tree: T[]; + depth?: number; + parentId?: number; +}): FlatTree[] { + return tree.reduce[]>((acc, item, index) => { + const children = item.children + ? flattenTree({ + tree: item.children, + parentId: item.id, + depth: depth + 1 + }) + : []; + + return [ + ...acc, + { + ...item, + parentId: parentId, + depth: depth, + index, + children + }, + ...children + ]; + }, []); +} + +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) > 0) { + excludeParentIds.push(item.id); + } + + return false; + } + + return true; + }); +} + +interface Args { + activeId: UniqueIdentifier | null; +} + +export const useDragAndDrop = ({ activeId }: Args) => { + const [isOpenChildren, setIsOpenChildren] = useState([]); + + // DndKit doesn't support nested sortable, so we need to flatten the data in one array + function flattenedItems>({ 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 + }); + } + + function buildTree({ + flattenedTree + }: { + flattenedTree: FlatTree[]; + }): WithChildren[] { + const root: { children: WithChildren[]; id: string } = { + id: "root", + children: [] + }; + + const nodes: Record> = { + [root.id]: root + } as unknown as Record>; + + for (const item of flattenedTree) { + const { id } = item; + const parentId = item.parentId ?? root.id; + const parent = + nodes[parentId] ?? flattenedTree.find(({ id }) => id === parentId); + + nodes[id] = item; + + if (parent) { + parent.children = parent.children ?? []; + parent.children.push(item as T); + } + } + + return root.children; + } + + return { + flattenTree, + flattenedItems, + isOpenChildren, + setIsOpenChildren, + buildTree + }; +}; diff --git a/frontend/admin/forum/views/forums/table/use-projection.ts b/frontend/admin/forum/views/forums/table/use-projection.ts new file mode 100644 index 000000000..a690f0676 --- /dev/null +++ b/frontend/admin/forum/views/forums/table/use-projection.ts @@ -0,0 +1,107 @@ +import { useState } from "react"; +import type { UniqueIdentifier } from "@dnd-kit/core"; +import { arrayMove } from "@dnd-kit/sortable"; + +import type { FlatTree } from "./use-functions"; + +const getDragDepth = ({ + indentationWidth, + offset +}: { + indentationWidth: number; + offset: number; +}) => { + return Math.round(offset / indentationWidth); +}; + +function getMaxDepth({ + previousItem +}: { + previousItem: FlatTree; +}) { + return previousItem ? previousItem.depth + 1 : 0; +} + +function getMinDepth({ + nextItem +}: { + nextItem: FlatTree; +}) { + return nextItem ? nextItem.depth : 0; +} + +export interface ProjectionReturnType { + depth: number; + maxDepth: number; + minDepth: number; + parentId: number; +} + +export const useProjection = () => { + const [activeId, setActiveId] = useState(null); + const [overId, setOverId] = useState(null); + + function getProjection({ + dragOffset, + indentationWidth, + tree + }: { + dragOffset: number; + indentationWidth: number; + tree: FlatTree[]; + }): ProjectionReturnType { + const overItemIndex = tree.findIndex(({ id }) => id === overId); + const activeItemIndex = tree.findIndex(({ id }) => id === activeId); + const activeItem = tree[activeItemIndex]; + const newItems = arrayMove(tree, activeItemIndex, overItemIndex); + const previousItem = newItems[overItemIndex - 1]; + const nextItem = newItems[overItemIndex + 1]; + const dragDepth = getDragDepth({ + offset: dragOffset, + indentationWidth + }); + const projectedDepth = activeItem.depth + dragDepth; + const maxDepth = getMaxDepth({ + previousItem + }); + const minDepth = getMinDepth({ nextItem }); + let depth = projectedDepth; + + if (projectedDepth >= maxDepth) { + depth = maxDepth; + } else if (projectedDepth < minDepth) { + depth = minDepth; + } + + const getParentId = (): number => { + if (depth === 0 || !previousItem) { + return 0; + } + + 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 ?? 0; + }; + + return { depth, maxDepth, minDepth, parentId: getParentId() }; + } + + return { + getProjection, + activeId, + overId, + setOverId, + setActiveId + }; +}; diff --git a/frontend/graphql/hooks.ts b/frontend/graphql/hooks.ts index 9b1bb54a4..f6034de83 100644 --- a/frontend/graphql/hooks.ts +++ b/frontend/graphql/hooks.ts @@ -53,7 +53,7 @@ export type AvatarUser = { export type ChildrenShowForumForums = { __typename?: 'ChildrenShowForumForums'; _count: ShowForumForumsCount; - children?: Maybe>; + children: Array; created: Scalars['Int']['output']; description: Array; id: Scalars['Int']['output']; @@ -64,7 +64,7 @@ export type ChildrenShowForumForums = { export type CreateForumForumsObj = { __typename?: 'CreateForumForumsObj'; _count: ShowForumForumsCount; - children?: Maybe>; + children: Array; created: Scalars['Int']['output']; description: Array; id: Scalars['Int']['output']; @@ -834,7 +834,7 @@ export type ShowForumForums = { export type ShowForumForumsAdmin = { __typename?: 'ShowForumForumsAdmin'; _count: ShowForumForumsCount; - children?: Maybe>; + children: Array; created: Scalars['Int']['output']; description: Array; id: Scalars['Int']['output']; @@ -863,7 +863,7 @@ export type ShowForumForumsObj = { export type ShowForumForumsWithParent = { __typename?: 'ShowForumForumsWithParent'; _count: ShowForumForumsCount; - children?: Maybe>; + children: Array; created: Scalars['Int']['output']; description: Array; id: Scalars['Int']['output']; @@ -1302,7 +1302,7 @@ export type Admin__Forum_Forums__ShowQueryVariables = Exact<{ }>; -export type Admin__Forum_Forums__ShowQuery = { __typename?: 'Query', admin__forum_forums__show: { __typename?: 'ShowForumForumsAdminObj', edges: Array<{ __typename?: 'ShowForumForumsAdmin', id: number, position: number, created: number, description: Array<{ __typename?: 'TextLanguage', language_code: string, value: string }>, name: Array<{ __typename?: 'TextLanguage', language_code: string, value: string }>, children?: Array<{ __typename?: 'ChildrenShowForumForums', created: number, id: number, position: number, description: Array<{ __typename?: 'TextLanguage', language_code: string, value: string }>, name: Array<{ __typename?: 'TextLanguage', language_code: string, value: string }>, _count: { __typename?: 'ShowForumForumsCount', children: number } }> | null, _count: { __typename?: 'ShowForumForumsCount', children: number } }>, pageInfo: { __typename?: 'PageInfo', count: number, endCursor?: number | null, hasNextPage: boolean, hasPreviousPage: boolean, startCursor?: number | null, totalCount: number } } }; +export type Admin__Forum_Forums__ShowQuery = { __typename?: 'Query', admin__forum_forums__show: { __typename?: 'ShowForumForumsAdminObj', edges: Array<{ __typename?: 'ShowForumForumsAdmin', id: number, position: number, created: number, description: Array<{ __typename?: 'TextLanguage', language_code: string, value: string }>, name: Array<{ __typename?: 'TextLanguage', language_code: string, value: string }>, children: Array<{ __typename?: 'ChildrenShowForumForums', created: number, id: number, position: number, description: Array<{ __typename?: 'TextLanguage', language_code: string, value: string }>, name: Array<{ __typename?: 'TextLanguage', language_code: string, value: string }>, _count: { __typename?: 'ShowForumForumsCount', children: number } }>, _count: { __typename?: 'ShowForumForumsCount', children: number } }>, pageInfo: { __typename?: 'PageInfo', count: number, endCursor?: number | null, hasNextPage: boolean, hasPreviousPage: boolean, startCursor?: number | null, totalCount: number } } }; export type Admin__Core_Groups__ShowQueryVariables = Exact<{ first?: InputMaybe; @@ -1428,7 +1428,7 @@ export type Core_Sessions__AuthorizationQuery = { __typename?: 'Query', core_ses export type Forum_Forums__ShowQueryVariables = Exact<{ [key: string]: never; }>; -export type Forum_Forums__ShowQuery = { __typename?: 'Query', forum_forums__show: { __typename?: 'ShowForumForumsObj', edges: Array<{ __typename?: 'ShowForumForumsWithParent', id: number, description: Array<{ __typename?: 'TextLanguage', language_code: string, value: string }>, name: Array<{ __typename?: 'TextLanguage', language_code: string, value: string }>, children?: Array<{ __typename?: 'ChildrenShowForumForums', id: number, name: Array<{ __typename?: 'TextLanguage', language_code: string, value: string }>, children?: Array<{ __typename?: 'LastChildShowForumForums', id: number, name: Array<{ __typename?: 'TextLanguage', language_code: string, value: string }> }> | null, description: Array<{ __typename?: 'TextLanguage', language_code: string, value: string }> }> | null }> } }; +export type Forum_Forums__ShowQuery = { __typename?: 'Query', forum_forums__show: { __typename?: 'ShowForumForumsObj', edges: Array<{ __typename?: 'ShowForumForumsWithParent', id: number, description: Array<{ __typename?: 'TextLanguage', language_code: string, value: string }>, name: Array<{ __typename?: 'TextLanguage', language_code: string, value: string }>, children: Array<{ __typename?: 'ChildrenShowForumForums', id: number, name: Array<{ __typename?: 'TextLanguage', language_code: string, value: string }>, children: Array<{ __typename?: 'LastChildShowForumForums', id: number, name: Array<{ __typename?: 'TextLanguage', language_code: string, value: string }> }>, description: Array<{ __typename?: 'TextLanguage', language_code: string, value: string }> }> }> } }; export type Forum_Forums__Show_ItemQueryVariables = Exact<{ cursor?: InputMaybe; @@ -1438,7 +1438,7 @@ export type Forum_Forums__Show_ItemQueryVariables = Exact<{ }>; -export type Forum_Forums__Show_ItemQuery = { __typename?: 'Query', forum_forums__show: { __typename?: 'ShowForumForumsObj', edges: Array<{ __typename?: 'ShowForumForumsWithParent', id: number, description: Array<{ __typename?: 'TextLanguage', language_code: string, value: string }>, name: Array<{ __typename?: 'TextLanguage', language_code: string, value: string }>, children?: Array<{ __typename?: 'ChildrenShowForumForums', id: number, name: Array<{ __typename?: 'TextLanguage', language_code: string, value: string }>, children?: Array<{ __typename?: 'LastChildShowForumForums', id: number, name: Array<{ __typename?: 'TextLanguage', language_code: string, value: string }> }> | null, description: Array<{ __typename?: 'TextLanguage', language_code: string, value: string }> }> | null, permissions: { __typename?: 'PermissionsForumForumsCount', can_create: boolean } }> }, forum_topics__show: { __typename?: 'ShowTopicsForumsObj', edges: Array<{ __typename?: 'ShowTopicsForums', created: number, id: number, locked: boolean, title: Array<{ __typename?: 'TextLanguage', language_code: string, value: string }>, user: { __typename?: 'User', id: number, name_seo: string, name: string, avatar_color: string, avatar?: { __typename?: 'AvatarUser', id: number, dir_folder: string, name: string } | null, group: { __typename?: 'GroupUser', id: number, name: Array<{ __typename?: 'TextLanguage', language_code: string, value: string }> } }, content: Array<{ __typename?: 'TextLanguage', language_code: string, value: string }>, forum: { __typename?: 'ForumTopicsForums', id: number, name: Array<{ __typename?: 'TextLanguage', language_code: string, value: string }> } }>, pageInfo: { __typename?: 'PageInfo', count: number, endCursor?: number | null, hasNextPage: boolean, hasPreviousPage: boolean, startCursor?: number | null, totalCount: number } } }; +export type Forum_Forums__Show_ItemQuery = { __typename?: 'Query', forum_forums__show: { __typename?: 'ShowForumForumsObj', edges: Array<{ __typename?: 'ShowForumForumsWithParent', id: number, description: Array<{ __typename?: 'TextLanguage', language_code: string, value: string }>, name: Array<{ __typename?: 'TextLanguage', language_code: string, value: string }>, children: Array<{ __typename?: 'ChildrenShowForumForums', id: number, name: Array<{ __typename?: 'TextLanguage', language_code: string, value: string }>, children: Array<{ __typename?: 'LastChildShowForumForums', id: number, name: Array<{ __typename?: 'TextLanguage', language_code: string, value: string }> }>, description: Array<{ __typename?: 'TextLanguage', language_code: string, value: string }> }>, permissions: { __typename?: 'PermissionsForumForumsCount', can_create: boolean } }> }, forum_topics__show: { __typename?: 'ShowTopicsForumsObj', edges: Array<{ __typename?: 'ShowTopicsForums', created: number, id: number, locked: boolean, title: Array<{ __typename?: 'TextLanguage', language_code: string, value: string }>, user: { __typename?: 'User', id: number, name_seo: string, name: string, avatar_color: string, avatar?: { __typename?: 'AvatarUser', id: number, dir_folder: string, name: string } | null, group: { __typename?: 'GroupUser', id: number, name: Array<{ __typename?: 'TextLanguage', language_code: string, value: string }> } }, content: Array<{ __typename?: 'TextLanguage', language_code: string, value: string }>, forum: { __typename?: 'ForumTopicsForums', id: number, name: Array<{ __typename?: 'TextLanguage', language_code: string, value: string }> } }>, pageInfo: { __typename?: 'PageInfo', count: number, endCursor?: number | null, hasNextPage: boolean, hasPreviousPage: boolean, startCursor?: number | null, totalCount: number } } }; export type Forum_Forums__Show_Item_MoreQueryVariables = Exact<{ cursor?: InputMaybe; From 53e90e7af017b3ec5a04613fa3df5b0098e8a40b Mon Sep 17 00:00:00 2001 From: aXenDeveloper Date: Sat, 24 Feb 2024 19:19:32 +0100 Subject: [PATCH 03/16] chore: Change buildTree to generic --- .../change_position/change_position.service.ts | 4 ++-- .../forum/views/forums/table/use-functions.ts | 14 ++++++-------- .../forum/views/forums/table/use-projection.ts | 8 ++++---- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/backend/src/admin/forum/forums/change_position/change_position.service.ts b/backend/src/admin/forum/forums/change_position/change_position.service.ts index f952de6ff..e6bd14933 100644 --- a/backend/src/admin/forum/forums/change_position/change_position.service.ts +++ b/backend/src/admin/forum/forums/change_position/change_position.service.ts @@ -27,8 +27,8 @@ export class ChangePositionForumForumsService { const allChildrenParent = await this.databaseService.db.query.forum_forums.findMany({ where: (table, { eq }) => - parent_id === null - ? isNull(table.parent_id) + !parent_id + ? isNull(table.parent_id ?? null) : eq(table.parent_id, parent_id), orderBy: (table, { asc }) => asc(table.position) }); diff --git a/frontend/admin/forum/views/forums/table/use-functions.ts b/frontend/admin/forum/views/forums/table/use-functions.ts index cd855c17d..7f690e95d 100644 --- a/frontend/admin/forum/views/forums/table/use-functions.ts +++ b/frontend/admin/forum/views/forums/table/use-functions.ts @@ -9,17 +9,17 @@ type WithChildren = Omit & { export type FlatTree = { depth: number; index: number; - parentId: number; + parentId: number | null; } & WithChildren; function flattenTree>({ depth = 0, - parentId = 0, + parentId = null, tree }: { tree: T[]; depth?: number; - parentId?: number; + parentId?: number | null; }): FlatTree[] { return tree.reduce[]>((acc, item, index) => { const children = item.children @@ -105,16 +105,14 @@ export const useDragAndDrop = ({ activeId }: Args) => { for (const item of flattenedTree) { const { id } = item; - const parentId = item.parentId ?? root.id; + const parentId = item.parentId || root.id; const parent = nodes[parentId] ?? flattenedTree.find(({ id }) => id === parentId); nodes[id] = item; - if (parent) { - parent.children = parent.children ?? []; - parent.children.push(item as T); - } + parent.children = parent.children ?? []; + parent.children.push(item as T); } return root.children; diff --git a/frontend/admin/forum/views/forums/table/use-projection.ts b/frontend/admin/forum/views/forums/table/use-projection.ts index a690f0676..8755d16dd 100644 --- a/frontend/admin/forum/views/forums/table/use-projection.ts +++ b/frontend/admin/forum/views/forums/table/use-projection.ts @@ -34,7 +34,7 @@ export interface ProjectionReturnType { depth: number; maxDepth: number; minDepth: number; - parentId: number; + parentId: number | null; } export const useProjection = () => { @@ -73,9 +73,9 @@ export const useProjection = () => { depth = minDepth; } - const getParentId = (): number => { + const getParentId = (): number | null => { if (depth === 0 || !previousItem) { - return 0; + return null; } if (depth === previousItem.depth) { @@ -91,7 +91,7 @@ export const useProjection = () => { .reverse() .find(item => item.depth === depth)?.parentId; - return newParent ?? 0; + return newParent ?? null; }; return { depth, maxDepth, minDepth, parentId: getParentId() }; From 2313255719f11cb88630ed89b90c6c5bbb6ba4af Mon Sep 17 00:00:00 2001 From: aXenDeveloper Date: Sat, 24 Feb 2024 20:42:30 +0100 Subject: [PATCH 04/16] fix(frontend): Multiple re-render item when user move item in child array --- .../views/forums/table/content-table.tsx | 71 ++++++------------- .../forum/views/forums/table/item/item.tsx | 4 +- .../forum/views/forums/table/use-functions.ts | 57 ++++++++------- 3 files changed, 54 insertions(+), 78 deletions(-) diff --git a/frontend/admin/forum/views/forums/table/content-table.tsx b/frontend/admin/forum/views/forums/table/content-table.tsx index f2e70be32..54a83a1f6 100644 --- a/frontend/admin/forum/views/forums/table/content-table.tsx +++ b/frontend/admin/forum/views/forums/table/content-table.tsx @@ -15,29 +15,32 @@ import { verticalListSortingStrategy } from "@dnd-kit/sortable"; import { useMemo, useState } from "react"; -import { useQueryClient, type InfiniteData } from "@tanstack/react-query"; import { useTranslations } from "next-intl"; import { ItemTableForumsForumAdmin } from "./item/item"; import { useForumForumsAdminAPI, - type Admin__Forum_Forums__ShowQueryItem + type ShowForumForumsAdminWithChildren } from "./hooks/use-forum-forums-admin-api"; import { Loader } from "@/components/loader"; import { ErrorAdminView } from "@/admin/core/global/error-admin-view"; -import { APIKeys } from "@/graphql/api-keys"; -import type { Admin__Forum_Forums__ShowQuery } from "@/graphql/hooks"; import { mutationChangePositionApi } from "./hooks/mutation-change-position-api"; -import { useDragAndDrop } from "./use-functions"; +import { useDragAndDrop, type FlatTree } from "./use-functions"; import { useProjection, type ProjectionReturnType } from "./use-projection"; const indentationWidth = 20; export const ContentTableForumsForumAdmin = () => { const t = useTranslations("core"); - const { data, fetchNextPage, hasNextPage, isError, isLoading } = - useForumForumsAdminAPI(); - const queryClient = useQueryClient(); + const { + data: initData, + fetchNextPage, + hasNextPage, + isError, + isLoading + } = useForumForumsAdminAPI(); + const [data, setData] = + useState(initData); const [projected, setProjected] = useState(); const { activeId, getProjection, overId, setActiveId, setOverId } = @@ -104,51 +107,17 @@ export const ContentTableForumsForumAdmin = () => { if (!projected || !over) return; const { depth, parentId } = projected; - const clonedItems = testDragAndDrop.flattenTree({ tree: data }); - - 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 findItemsParent = sortedItems.filter( - i => i.parentId === parentId - ); - - // Update position of the item - findItemsParent.forEach((item, index) => { - const findIndex = sortedItems.findIndex(i => i.id === item.id); - - if (!findIndex) return; - - sortedItems[findIndex] = { - ...item, - position: index - }; + + const clonedItems: FlatTree[] = + testDragAndDrop.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 build = testDragAndDrop.buildTree({ + flattenedTree: sortedItems }); - queryClient.setQueryData>( - [APIKeys.FORUMS_ADMIN], - old => { - const lastPage = old?.pages.at(-1); - if (!old || !lastPage) return old; - - return { - pages: [ - { - ...lastPage, - admin__forum_forums__show: { - ...lastPage.admin__forum_forums__show, - edges: testDragAndDrop.buildTree({ - flattenedTree: sortedItems - }) as Admin__Forum_Forums__ShowQueryItem[] - } - } - ], - pageParams: [old.pageParams.at(-1)] - }; - } - ); + setData(build); // -1 means that the item is the last one const findActive = flattenedItems.find(i => i.id === active.id); diff --git a/frontend/admin/forum/views/forums/table/item/item.tsx b/frontend/admin/forum/views/forums/table/item/item.tsx index 866077470..284ed31ec 100644 --- a/frontend/admin/forum/views/forums/table/item/item.tsx +++ b/frontend/admin/forum/views/forums/table/item/item.tsx @@ -104,7 +104,9 @@ export const ItemTableForumsForumAdmin = ({ )}
- {convertText(name)} + + {convertText(name)} - {id} +
diff --git a/frontend/admin/forum/views/forums/table/use-functions.ts b/frontend/admin/forum/views/forums/table/use-functions.ts index 7f690e95d..ee2291cff 100644 --- a/frontend/admin/forum/views/forums/table/use-functions.ts +++ b/frontend/admin/forum/views/forums/table/use-functions.ts @@ -2,7 +2,7 @@ import type { UniqueIdentifier } from "@dnd-kit/core"; import { useState } from "react"; type WithChildren = Omit & { - children: T[]; + children: WithChildren[]; id: number; }; @@ -12,28 +12,28 @@ export type FlatTree = { parentId: number | null; } & WithChildren; -function flattenTree>({ +function flattenTree({ depth = 0, parentId = null, tree }: { - tree: T[]; + tree: WithChildren[]; depth?: number; parentId?: number | null; }): FlatTree[] { - return tree.reduce[]>((acc, item, index) => { - const children = item.children + return tree.reduce[]>((previousValue, currentValue, index) => { + const children = currentValue.children ? flattenTree({ - tree: item.children, - parentId: item.id, + tree: currentValue.children, + parentId: currentValue.id, depth: depth + 1 }) : []; return [ - ...acc, + ...previousValue, { - ...item, + ...currentValue, parentId: parentId, depth: depth, index, @@ -94,28 +94,33 @@ export const useDragAndDrop = ({ activeId }: Args) => { }: { flattenedTree: FlatTree[]; }): WithChildren[] { - const root: { children: WithChildren[]; id: string } = { - id: "root", - children: [] - }; + const tree: WithChildren[] = []; - const nodes: Record> = { - [root.id]: root - } as unknown as Record>; + // Prepare the tree with the root elements + flattenedTree + .filter(item => !item.parentId) + .forEach(item => { + tree.push({ ...item, children: [] }); + }); + + flattenedTree + .filter(item => item.parentId) + .forEach(item => { + const parentIndex = tree.findIndex(({ id }) => id === item.parentId); - for (const item of flattenedTree) { - const { id } = item; - const parentId = item.parentId || root.id; - const parent = - nodes[parentId] ?? flattenedTree.find(({ id }) => id === parentId); + if (parentIndex === -1) { + throw new Error("Parent not found"); + } - nodes[id] = item; + const parent = tree[parentIndex]; - parent.children = parent.children ?? []; - parent.children.push(item as T); - } + tree[parentIndex] = { + ...parent, + children: [...parent.children, item] + }; + }); - return root.children; + return tree; } return { From 639d59282f94354e2f7d8ddcd2bf8ef7ee18e82c Mon Sep 17 00:00:00 2001 From: aXenDeveloper Date: Sat, 24 Feb 2024 21:24:32 +0100 Subject: [PATCH 05/16] fix(frontend): Drop item to the parent from child in drag and drop --- frontend/admin/forum/views/forums/table/content-table.tsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/frontend/admin/forum/views/forums/table/content-table.tsx b/frontend/admin/forum/views/forums/table/content-table.tsx index 54a83a1f6..a4f9bb2e5 100644 --- a/frontend/admin/forum/views/forums/table/content-table.tsx +++ b/frontend/admin/forum/views/forums/table/content-table.tsx @@ -113,6 +113,13 @@ export const ContentTableForumsForumAdmin = () => { 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 build = testDragAndDrop.buildTree({ flattenedTree: sortedItems }); From ad0b8ba7c5b825a3293fdc4464ef0f1df390f8c5 Mon Sep 17 00:00:00 2001 From: aXenDeveloper Date: Sat, 24 Feb 2024 21:37:42 +0100 Subject: [PATCH 06/16] fix(frontend): indexToMove for drag and drop --- frontend/admin/forum/views/forums/table/content-table.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/admin/forum/views/forums/table/content-table.tsx b/frontend/admin/forum/views/forums/table/content-table.tsx index a4f9bb2e5..2931fb33d 100644 --- a/frontend/admin/forum/views/forums/table/content-table.tsx +++ b/frontend/admin/forum/views/forums/table/content-table.tsx @@ -150,7 +150,7 @@ export const ContentTableForumsForumAdmin = () => { const indexToMove = active.id === over.id ? -1 - : flattenedItems.find(i => i.id === over.id)?.position ?? -1; + : flattenedItems.find(i => i.id === over.id)?.index ?? -1; // Do nothing if drag and drop on the same item on the same level if (findActive?.parentId === parentId && active.id === over.id) { From a9fcf04883bc26fd7bd508009d3a85706bfa51c6 Mon Sep 17 00:00:00 2001 From: aXenDeveloper Date: Sat, 24 Feb 2024 22:20:01 +0100 Subject: [PATCH 07/16] feat(frontend): Add DragOverlay for forums in AdminCP --- .../views/forums/table/content-table.tsx | 83 +++++++++---------- .../forum/views/forums/table/item/item.tsx | 9 +- .../forum/views/forums/table/use-functions.ts | 6 +- 3 files changed, 44 insertions(+), 54 deletions(-) diff --git a/frontend/admin/forum/views/forums/table/content-table.tsx b/frontend/admin/forum/views/forums/table/content-table.tsx index 2931fb33d..2dc686f95 100644 --- a/frontend/admin/forum/views/forums/table/content-table.tsx +++ b/frontend/admin/forum/views/forums/table/content-table.tsx @@ -1,4 +1,3 @@ -import { Virtuoso } from "react-virtuoso"; import { DndContext, closestCorners, @@ -6,7 +5,8 @@ import { PointerSensor, useSensor, useSensors, - MeasuringStrategy + MeasuringStrategy, + DragOverlay } from "@dnd-kit/core"; import { SortableContext, @@ -22,8 +22,6 @@ import { useForumForumsAdminAPI, type ShowForumForumsAdminWithChildren } from "./hooks/use-forum-forums-admin-api"; -import { Loader } from "@/components/loader"; -import { ErrorAdminView } from "@/admin/core/global/error-admin-view"; import { mutationChangePositionApi } from "./hooks/mutation-change-position-api"; import { useDragAndDrop, type FlatTree } from "./use-functions"; import { useProjection, type ProjectionReturnType } from "./use-projection"; @@ -32,13 +30,7 @@ const indentationWidth = 20; export const ContentTableForumsForumAdmin = () => { const t = useTranslations("core"); - const { - data: initData, - fetchNextPage, - hasNextPage, - isError, - isLoading - } = useForumForumsAdminAPI(); + const { data: initData } = useForumForumsAdminAPI(); const [data, setData] = useState(initData); const [projected, setProjected] = useState(); @@ -61,13 +53,12 @@ export const ContentTableForumsForumAdmin = () => { ); const flattenedItems = testDragAndDrop.flattenedItems({ data }); + const activeItem = flattenedItems.find(i => i.id === activeId); const sortedIds = useMemo( () => flattenedItems.map(({ id }) => id), [flattenedItems] ); - if (isLoading) return ; - if (isError) return ; if (!data || data.length === 0) { return
{t("no_results")}
; } @@ -165,39 +156,41 @@ export const ContentTableForumsForumAdmin = () => { }} > - { - if (hasNextPage) { - fetchNextPage(); + {flattenedItems.map(item => ( + { + testDragAndDrop.setIsOpenChildren(prev => { + if (prev.includes(id)) { + return prev.filter(i => i !== id); + } + + return [...prev, id]; + }); + }} + isOpenChildren={testDragAndDrop.isOpenChildren.includes(item.id)} + isDropHere={projected?.parentId === item.id} + active={activeId === item.id} + {...item} + depth={ + activeId === item.id && projected?.depth + ? projected?.depth + : item.depth } - }} - itemContent={(_index, item) => { - return ( - { - testDragAndDrop.setIsOpenChildren(prev => { - if (prev.includes(id)) { - return prev.filter(i => i !== id); - } - - return [...prev, id]; - }); - }} - isOpenChildren={testDragAndDrop.isOpenChildren.includes( - item.id - )} - isDropHere={projected?.parentId === item.id} - {...item} - /> - ); - }} - /> + /> + ))} + + + {activeId !== null && activeItem && ( + + )} +
); diff --git a/frontend/admin/forum/views/forums/table/item/item.tsx b/frontend/admin/forum/views/forums/table/item/item.tsx index 284ed31ec..b219b0235 100644 --- a/frontend/admin/forum/views/forums/table/item/item.tsx +++ b/frontend/admin/forum/views/forums/table/item/item.tsx @@ -15,11 +15,13 @@ import type { FlatTree } from "../use-functions"; interface Props extends FlatTree { indentationWidth: number; isOpenChildren: boolean; + active?: boolean; isDropHere?: boolean; onCollapse?: (id: UniqueIdentifier) => void; } export const ItemTableForumsForumAdmin = ({ + active, children, depth, id, @@ -66,7 +68,8 @@ export const ItemTableForumsForumAdmin = ({ "p-4 flex gap-2 bg-card items-center transition-[background-color,opacity] relative border", { "animate-pulse bg-primary/20": isDropHere, - "z-10": isDragging + "z-10": isDragging, + "opacity-50": active } )} style={{ @@ -104,9 +107,7 @@ export const ItemTableForumsForumAdmin = ({ )}
- - {convertText(name)} - {id} - + {convertText(name)}
diff --git a/frontend/admin/forum/views/forums/table/use-functions.ts b/frontend/admin/forum/views/forums/table/use-functions.ts index ee2291cff..60801c315 100644 --- a/frontend/admin/forum/views/forums/table/use-functions.ts +++ b/frontend/admin/forum/views/forums/table/use-functions.ts @@ -107,11 +107,7 @@ export const useDragAndDrop = ({ activeId }: Args) => { .filter(item => item.parentId) .forEach(item => { const parentIndex = tree.findIndex(({ id }) => id === item.parentId); - - if (parentIndex === -1) { - throw new Error("Parent not found"); - } - + if (parentIndex === -1) return; const parent = tree[parentIndex]; tree[parentIndex] = { From 99f028ed4411f0c9789e8a3d1d0c8ee3a4e79894 Mon Sep 17 00:00:00 2001 From: aXenDeveloper Date: Sat, 24 Feb 2024 22:29:43 +0100 Subject: [PATCH 08/16] perf(frontend): Remove react query for forums AdminCP --- .../views/forums/forums-forum-admin-view.tsx | 5 +- .../views/forums/table/content-table.tsx | 13 ++++-- .../table/hooks/use-forum-forums-admin-api.ts | 46 ++++--------------- .../admin/forum/views/forums/table/table.tsx | 16 ++++++- .../admin/(auth)/forum/forums/page.tsx | 42 +++-------------- 5 files changed, 40 insertions(+), 82 deletions(-) diff --git a/frontend/admin/forum/views/forums/forums-forum-admin-view.tsx b/frontend/admin/forum/views/forums/forums-forum-admin-view.tsx index abc0033d9..9183497ef 100644 --- a/frontend/admin/forum/views/forums/forums-forum-admin-view.tsx +++ b/frontend/admin/forum/views/forums/forums-forum-admin-view.tsx @@ -3,8 +3,9 @@ import { useTranslations } from "next-intl"; import { HeaderContent } from "@/components/header-content/header-content"; import { ActionsForumsForumAdmin } from "./actions/actions"; import { TableForumsForumAdmin } from "./table/table"; +import type { Admin__Forum_Forums__ShowQuery } from "@/graphql/hooks"; -export const ForumsForumAdminView = () => { +export const ForumsForumAdminView = (props: Admin__Forum_Forums__ShowQuery) => { const t = useTranslations("admin.forum.forums"); return ( @@ -13,7 +14,7 @@ export const ForumsForumAdminView = () => { - + ); }; diff --git a/frontend/admin/forum/views/forums/table/content-table.tsx b/frontend/admin/forum/views/forums/table/content-table.tsx index 2dc686f95..1ff105d4c 100644 --- a/frontend/admin/forum/views/forums/table/content-table.tsx +++ b/frontend/admin/forum/views/forums/table/content-table.tsx @@ -28,13 +28,16 @@ import { useProjection, type ProjectionReturnType } from "./use-projection"; const indentationWidth = 20; -export const ContentTableForumsForumAdmin = () => { +export interface TableForumsForumAdminProps { + initData: ShowForumForumsAdminWithChildren[]; +} + +export const ContentTableForumsForumAdmin = ({ + initData +}: TableForumsForumAdminProps) => { const t = useTranslations("core"); - const { data: initData } = useForumForumsAdminAPI(); - const [data, setData] = - useState(initData); + const { data, setData } = useForumForumsAdminAPI({ initData }); const [projected, setProjected] = useState(); - const { activeId, getProjection, overId, setActiveId, setOverId } = useProjection(); const testDragAndDrop = useDragAndDrop({ activeId }); diff --git a/frontend/admin/forum/views/forums/table/hooks/use-forum-forums-admin-api.ts b/frontend/admin/forum/views/forums/table/hooks/use-forum-forums-admin-api.ts index 6d4923360..cd04c1f9a 100644 --- a/frontend/admin/forum/views/forums/table/hooks/use-forum-forums-admin-api.ts +++ b/frontend/admin/forum/views/forums/table/hooks/use-forum-forums-admin-api.ts @@ -1,9 +1,6 @@ -import { useInfiniteQuery } from "@tanstack/react-query"; -import { useMemo } from "react"; +import { useState } from "react"; -import { APIKeys } from "@/graphql/api-keys"; import { type ShowForumForumsAdmin } from "@/graphql/hooks"; -import { queryApi } from "./query-api"; export interface Admin__Forum_Forums__ShowQueryItem extends Omit {} @@ -13,41 +10,16 @@ export interface ShowForumForumsAdminWithChildren children: ShowForumForumsAdminWithChildren[]; } -export const useForumForumsAdminAPI = () => { - const query = useInfiniteQuery({ - queryKey: [APIKeys.FORUMS_ADMIN], - queryFn: async ({ pageParam }) => { - return await queryApi(pageParam); - }, - initialPageParam: { - first: 10 - }, - getNextPageParam: ({ admin__forum_forums__show: { pageInfo } }) => { - if (pageInfo.hasNextPage) { - return { - first: 10, - cursor: pageInfo.endCursor - }; - } - } - }); +interface Args { + initData: ShowForumForumsAdminWithChildren[]; +} - const data: ShowForumForumsAdminWithChildren[] = useMemo(() => { - return ( - query.data?.pages.flatMap(({ admin__forum_forums__show: { edges } }) => - edges.map(item => ({ - ...item, - children: - item.children.length > 0 - ? (item.children as unknown as ShowForumForumsAdminWithChildren[]) // Convert to the correct type, drizzle don't support recursive types - : [] - })) - ) ?? [] - ); - }, [query.data]); +export const useForumForumsAdminAPI = ({ initData }: Args) => { + const [data, setData] = + useState(initData); return { - ...query, - data + data, + setData }; }; diff --git a/frontend/admin/forum/views/forums/table/table.tsx b/frontend/admin/forum/views/forums/table/table.tsx index f85e39354..04f1afa76 100644 --- a/frontend/admin/forum/views/forums/table/table.tsx +++ b/frontend/admin/forum/views/forums/table/table.tsx @@ -3,6 +3,8 @@ import { Suspense, lazy } from "react"; import { Loader } from "@/components/loader"; +import type { Admin__Forum_Forums__ShowQuery } from "@/graphql/hooks"; +import type { ShowForumForumsAdminWithChildren } from "./hooks/use-forum-forums-admin-api"; const ContentTableForumsForumAdmin = lazy(() => import("./content-table").then(module => ({ @@ -10,10 +12,20 @@ const ContentTableForumsForumAdmin = lazy(() => })) ); -export const TableForumsForumAdmin = () => { +export const TableForumsForumAdmin = ({ + admin__forum_forums__show: { edges } +}: Admin__Forum_Forums__ShowQuery) => { + const initData: ShowForumForumsAdminWithChildren[] = edges.map(item => ({ + ...item, + children: + item.children.length > 0 + ? (item.children as unknown as ShowForumForumsAdminWithChildren[]) // Convert to the correct type, drizzle don't support recursive types + : [] + })); + return ( }> - + ); }; diff --git a/frontend/app/[locale]/(apps)/(admin)/admin/(auth)/forum/forums/page.tsx b/frontend/app/[locale]/(apps)/(admin)/admin/(auth)/forum/forums/page.tsx index dc5b73ec8..5e86df2e3 100644 --- a/frontend/app/[locale]/(apps)/(admin)/admin/(auth)/forum/forums/page.tsx +++ b/frontend/app/[locale]/(apps)/(admin)/admin/(auth)/forum/forums/page.tsx @@ -1,9 +1,5 @@ -import { HydrationBoundary, dehydrate } from "@tanstack/react-query"; - import { ForumsForumAdminView } from "@/admin/forum/views/forums/forums-forum-admin-view"; -import getQueryClient from "@/functions/get-query-client"; import { fetcher } from "@/graphql/fetcher"; -import { APIKeys } from "@/graphql/api-keys"; import { Admin__Forum_Forums__Show, type Admin__Forum_Forums__ShowQuery, @@ -11,38 +7,12 @@ import { } from "@/graphql/hooks"; export default async function Page() { - const queryClient = getQueryClient(); - await queryClient.prefetchInfiniteQuery({ - queryKey: [APIKeys.FORUMS_ADMIN], - queryFn: async ({ pageParam }) => { - const { data } = await fetcher< - Admin__Forum_Forums__ShowQuery, - Admin__Forum_Forums__ShowQueryVariables - >({ - query: Admin__Forum_Forums__Show, - variables: pageParam - }); - - return data; - }, - initialPageParam: { - first: 10 - }, - getNextPageParam: ({ admin__forum_forums__show: { pageInfo } }) => { - if (pageInfo.hasNextPage) { - return { - first: 10, - cursor: pageInfo.endCursor - }; - } - }, - pages: 1 + const { data } = await fetcher< + Admin__Forum_Forums__ShowQuery, + Admin__Forum_Forums__ShowQueryVariables + >({ + query: Admin__Forum_Forums__Show }); - const dehydratedState = dehydrate(queryClient); - return ( - - - - ); + return ; } From 6e4d4bc882eff4adc15337a122607c5a83411446 Mon Sep 17 00:00:00 2001 From: aXenDeveloper Date: Sun, 25 Feb 2024 11:27:41 +0100 Subject: [PATCH 09/16] fix(frontend): Drop item to children with invalid index for forums in AdminCP --- .../views/forums/table/content-table.tsx | 39 ++++++++++--------- .../forum/views/forums/table/item/item.tsx | 4 +- 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/frontend/admin/forum/views/forums/table/content-table.tsx b/frontend/admin/forum/views/forums/table/content-table.tsx index 1ff105d4c..a7f1c9ea1 100644 --- a/frontend/admin/forum/views/forums/table/content-table.tsx +++ b/frontend/admin/forum/views/forums/table/content-table.tsx @@ -120,31 +120,34 @@ export const ContentTableForumsForumAdmin = ({ setData(build); + const parents = sortedItems.filter(i => i.parentId === parentId); + const indexToMove = parents.findIndex(i => i.id === active.id); + // -1 means that the item is the last one const findActive = flattenedItems.find(i => i.id === active.id); if (!findActive) return; - // If change item position on the same level at the end of the list - if (active.id === over.id && depth < findActive.depth) { - const findParentPosition = flattenedItems.find( - i => i.id === findActive.parentId - )?.position; + // // If change item position on the same level at the end of the list + // if (active.id === over.id && depth < findActive.depth) { + // const findParentPosition = flattenedItems.find( + // i => i.id === findActive.parentId + // )?.position; - if (findParentPosition === undefined) return; + // if (findParentPosition === undefined) return; - await mutationChangePositionApi({ - id: Number(active.id), - parentId, - indexToMove: findParentPosition + 1 - }); + // await mutationChangePositionApi({ + // id: Number(active.id), + // parentId, + // indexToMove: findParentPosition + 1 + // }); - return; - } + // return; + // } - const indexToMove = - active.id === over.id - ? -1 - : flattenedItems.find(i => i.id === over.id)?.index ?? -1; + // const indexToMove = + // active.id === over.id + // ? -1 + // : flattenedItems.find(i => i.id === over.id)?.index ?? -1; // Do nothing if drag and drop on the same item on the same level if (findActive?.parentId === parentId && active.id === over.id) { @@ -177,7 +180,7 @@ export const ContentTableForumsForumAdmin = ({ active={activeId === item.id} {...item} depth={ - activeId === item.id && projected?.depth + activeId === item.id && projected?.parentId ? projected?.depth : item.depth } diff --git a/frontend/admin/forum/views/forums/table/item/item.tsx b/frontend/admin/forum/views/forums/table/item/item.tsx index b219b0235..dddcaa70d 100644 --- a/frontend/admin/forum/views/forums/table/item/item.tsx +++ b/frontend/admin/forum/views/forums/table/item/item.tsx @@ -107,7 +107,9 @@ export const ItemTableForumsForumAdmin = ({ )}
- {convertText(name)} + + {convertText(name)} - {id} +
From ae497a8eeb0f2f73b9ca2b26347c9ee99c4f083d Mon Sep 17 00:00:00 2001 From: aXenDeveloper Date: Sun, 25 Feb 2024 19:00:04 +0100 Subject: [PATCH 10/16] perf(frontend): Add update mutation for forums in AdminCP --- .../views/forums/table/content-table.tsx | 29 +++- .../forum/views/forums/table/functions.ts | 161 ------------------ .../table/hooks/mutation-update-data-api.ts | 26 +++ .../table/hooks/use-forum-forums-admin-api.ts | 58 ++++++- .../forum/views/forums/table/item/item.tsx | 10 +- .../admin/forum/views/forums/table/types.ts | 15 -- .../forum/views/forums/table/use-functions.ts | 66 ++++--- 7 files changed, 135 insertions(+), 230 deletions(-) delete mode 100644 frontend/admin/forum/views/forums/table/functions.ts create mode 100644 frontend/admin/forum/views/forums/table/hooks/mutation-update-data-api.ts delete mode 100644 frontend/admin/forum/views/forums/table/types.ts diff --git a/frontend/admin/forum/views/forums/table/content-table.tsx b/frontend/admin/forum/views/forums/table/content-table.tsx index a7f1c9ea1..f7a07980c 100644 --- a/frontend/admin/forum/views/forums/table/content-table.tsx +++ b/frontend/admin/forum/views/forums/table/content-table.tsx @@ -23,7 +23,12 @@ import { type ShowForumForumsAdminWithChildren } from "./hooks/use-forum-forums-admin-api"; import { mutationChangePositionApi } from "./hooks/mutation-change-position-api"; -import { useDragAndDrop, type FlatTree } from "./use-functions"; +import { + useDragAndDrop, + type FlatTree, + buildTree, + flattenTree +} from "./use-functions"; import { useProjection, type ProjectionReturnType } from "./use-projection"; const indentationWidth = 20; @@ -36,7 +41,7 @@ export const ContentTableForumsForumAdmin = ({ initData }: TableForumsForumAdminProps) => { const t = useTranslations("core"); - const { data, setData } = useForumForumsAdminAPI({ initData }); + const { data, setData, updateData } = useForumForumsAdminAPI({ initData }); const [projected, setProjected] = useState(); const { activeId, getProjection, overId, setActiveId, setOverId } = useProjection(); @@ -103,7 +108,7 @@ export const ContentTableForumsForumAdmin = ({ const { depth, parentId } = projected; const clonedItems: FlatTree[] = - testDragAndDrop.flattenTree({ tree: data }); + 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); @@ -114,11 +119,11 @@ export const ContentTableForumsForumAdmin = ({ parentId }; - const build = testDragAndDrop.buildTree({ - flattenedTree: sortedItems - }); - - setData(build); + setData( + buildTree({ + flattenedTree: sortedItems + }) + ); const parents = sortedItems.filter(i => i.parentId === parentId); const indexToMove = parents.findIndex(i => i.id === active.id); @@ -167,8 +172,14 @@ export const ContentTableForumsForumAdmin = ({ key={item.id} indentationWidth={indentationWidth} onCollapse={id => { + const isOpen = testDragAndDrop.isOpenChildren.includes(id); + + if (!isOpen) { + updateData({ parentId: Number(id) }); + } + testDragAndDrop.setIsOpenChildren(prev => { - if (prev.includes(id)) { + if (isOpen) { return prev.filter(i => i !== id); } diff --git a/frontend/admin/forum/views/forums/table/functions.ts b/frontend/admin/forum/views/forums/table/functions.ts deleted file mode 100644 index 29b8ffad3..000000000 --- a/frontend/admin/forum/views/forums/table/functions.ts +++ /dev/null @@ -1,161 +0,0 @@ -import type { UniqueIdentifier } from "@dnd-kit/core"; -import { arrayMove } from "@dnd-kit/sortable"; - -import type { - Admin__Forum_Forums__ShowFlattenedItem, - Admin__Forum_Forums__ShowWithProjection -} from "./types"; -import type { Admin__Forum_Forums__ShowQueryItem } from "./hooks/use-forum-forums-admin-api"; -import type { - ChildrenShowForumForums, - ShowForumForumsAdmin -} from "@/graphql/hooks"; - -const getDragDepth = ({ - indentationWidth, - offset -}: { - indentationWidth: number; - offset: number; -}) => { - return Math.round(offset / indentationWidth); -}; - -const getMaxDepth = ({ - previousItem -}: { - previousItem: Admin__Forum_Forums__ShowFlattenedItem; -}) => { - return previousItem ? previousItem.depth + 1 : 0; -}; - -const getMinDepth = ({ - nextItem -}: { - nextItem: Admin__Forum_Forums__ShowFlattenedItem; -}) => { - return nextItem ? nextItem.depth : 0; -}; - -export const removeChildrenOf = ({ - ids, - items -}: { - ids: UniqueIdentifier[]; - items: Admin__Forum_Forums__ShowFlattenedItem[]; -}) => { - const excludeParentIds = [...ids]; - - return items.filter(item => { - if (item.parentId && excludeParentIds.includes(item.parentId)) { - if ((item.children?.length ?? 0) > 0) { - excludeParentIds.push(item.id); - } - - return false; - } - - return true; - }); -}; - -export const getForumProjection = ( - items: Admin__Forum_Forums__ShowFlattenedItem[], - activeId: UniqueIdentifier, - overId: UniqueIdentifier, - dragOffset: number, - indentationWidth: number -): Admin__Forum_Forums__ShowWithProjection => { - 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 = newItems[overItemIndex - 1]; - const nextItem = newItems[overItemIndex + 1]; - const dragDepth = getDragDepth({ - offset: dragOffset, - indentationWidth - }); - const projectedDepth = activeItem.depth + dragDepth; - const maxDepth = getMaxDepth({ - previousItem - }); - const minDepth = getMinDepth({ nextItem }); - let depth = projectedDepth; - - if (projectedDepth >= maxDepth) { - depth = maxDepth; - } else if (projectedDepth < minDepth) { - depth = minDepth; - } - - const 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; - }; - - return { depth, maxDepth, minDepth, parentId: getParentId() }; -}; - -export const flattenTree = ( - items: Admin__Forum_Forums__ShowQueryItem[], - parentId: number | null = null, - depth = 0 -): Admin__Forum_Forums__ShowFlattenedItem[] => { - return items.reduce( - (acc, item, index) => { - return [ - ...acc, - { ...item, parentId, depth, index }, - ...flattenTree( - (item.children ?? []) as ShowForumForumsAdmin[], - item.id, - depth + 1 - ) - ]; - }, - [] - ); -}; - -export const buildTree = ( - flattenedItems: Admin__Forum_Forums__ShowFlattenedItem[] -): Admin__Forum_Forums__ShowQueryItem[] => { - const root: { children: Admin__Forum_Forums__ShowQueryItem[]; id: string } = { - id: "root", - children: [] - }; - const nodes: Record = { - [root.id]: root - } as unknown as Record; - const items = flattenedItems.map(item => ({ ...item, children: [] })); - - for (const item of items) { - const { id } = item; - const parentId = item.parentId ?? root.id; - const parent = nodes[parentId] ?? items.find(({ id }) => id === parentId); - - nodes[id] = item; - - parent.children = parent.children ?? []; - parent.children.push(item as ChildrenShowForumForums); - } - - return root.children as Admin__Forum_Forums__ShowQueryItem[]; -}; diff --git a/frontend/admin/forum/views/forums/table/hooks/mutation-update-data-api.ts b/frontend/admin/forum/views/forums/table/hooks/mutation-update-data-api.ts new file mode 100644 index 000000000..4f14ba0bf --- /dev/null +++ b/frontend/admin/forum/views/forums/table/hooks/mutation-update-data-api.ts @@ -0,0 +1,26 @@ +"use server"; + +import { fetcher } from "@/graphql/fetcher"; +import { + Admin__Forum_Forums__Show, + type Admin__Forum_Forums__ShowQuery, + type Admin__Forum_Forums__ShowQueryVariables +} from "@/graphql/hooks"; + +export const mutationUpdateDataApi = async ( + variables: Admin__Forum_Forums__ShowQueryVariables +) => { + try { + const { data } = await fetcher< + Admin__Forum_Forums__ShowQuery, + Admin__Forum_Forums__ShowQueryVariables + >({ + query: Admin__Forum_Forums__Show, + variables + }); + + return { data }; + } catch (error) { + return { error }; + } +}; diff --git a/frontend/admin/forum/views/forums/table/hooks/use-forum-forums-admin-api.ts b/frontend/admin/forum/views/forums/table/hooks/use-forum-forums-admin-api.ts index cd04c1f9a..de84dbb45 100644 --- a/frontend/admin/forum/views/forums/table/hooks/use-forum-forums-admin-api.ts +++ b/frontend/admin/forum/views/forums/table/hooks/use-forum-forums-admin-api.ts @@ -1,12 +1,13 @@ import { useState } from "react"; +import { toast } from "sonner"; +import { useTranslations } from "next-intl"; import { type ShowForumForumsAdmin } from "@/graphql/hooks"; - -export interface Admin__Forum_Forums__ShowQueryItem - extends Omit {} +import { mutationUpdateDataApi } from "./mutation-update-data-api"; +import { buildTree, flattenTree, type FlatTree } from "../use-functions"; export interface ShowForumForumsAdminWithChildren - extends Omit { + extends Omit { children: ShowForumForumsAdminWithChildren[]; } @@ -15,11 +16,58 @@ interface Args { } export const useForumForumsAdminAPI = ({ initData }: Args) => { + const t = useTranslations("core"); const [data, setData] = useState(initData); + const updateData = async ({ parentId }: { parentId: number }) => { + const mutation = await mutationUpdateDataApi({ parentId }); + + if (mutation.error || !mutation.data) { + toast.error(t("errors.title"), { + description: t("errors.internal_server_error") + }); + + return; + } + + const data = mutation.data.admin__forum_forums__show.edges; + + // setData(prev => { + // const clonedItems: FlatTree[] = + // flattenTree({ tree: prev }); + + // const parentIndex1 = clonedItems.findIndex(i => i.id === parentId); + // const parent1 = clonedItems[parentIndex1]; + + // clonedItems[parentIndex1] = { + // ...parent1, + // children: data + // }; + + // const build1 = buildTree({ flattenedTree: clonedItems }); + + // const test = clonedItems.filter(item => !item.parentId); + + // const items = clonedItems.filter(item => item.parentId !== parentId); + + // const parentIndex = items.findIndex(i => i.id === parentId); + // const parent = items[parentIndex]; + + // items[parentIndex] = { + // ...parent, + // children: mutation.data.admin__forum_forums__show.edges + // }; + + // const build = buildTree({ flattenedTree: items }); + + // return build; + // }); + }; + return { data, - setData + setData, + updateData }; }; diff --git a/frontend/admin/forum/views/forums/table/item/item.tsx b/frontend/admin/forum/views/forums/table/item/item.tsx index dddcaa70d..57580ccce 100644 --- a/frontend/admin/forum/views/forums/table/item/item.tsx +++ b/frontend/admin/forum/views/forums/table/item/item.tsx @@ -45,8 +45,8 @@ export const ItemTableForumsForumAdmin = ({ animateLayoutChanges: ({ isSorting, wasDragging }) => isSorting || wasDragging ? false : true }); - const childrenCount = children?.length ?? 0; - const allowOpenChildren = childrenCount > 0; + + const allowOpenChildren = children.length > 0 && onCollapse; useChildrenForumForumsAdminAPI({ parentId: id, @@ -89,11 +89,9 @@ export const ItemTableForumsForumAdmin = ({ - {childrenCount > 0 && ( + {allowOpenChildren && (