From d289bc883d60c48b6c3a497e80da3a43cbef7954 Mon Sep 17 00:00:00 2001 From: shinmj Date: Fri, 19 Nov 2021 17:09:08 +0900 Subject: [PATCH] bugfix menu tree dnd update --- .../api/board/dto/BoardListResponseDto.java | 11 +- .../api/posts/dto/PostsDeleteRequestDto.java | 4 + .../api/posts/dto/PostsSimpleResponseDto.java | 4 +- .../boardservice/domain/board/Board.java | 9 +- .../domain/board/BoardRepositoryImpl.java | 11 +- .../boardservice/domain/posts/Posts.java | 12 +- .../service/posts/PostsService.java | 24 +- .../api/menu/MenuApiController.java | 2 +- .../api/menu/dto/MenuTreeResponseDto.java | 4 + .../cloud/portalservice/domain/menu/Menu.java | 29 +- .../service/menu/MenuService.java | 46 +- .../components/DraggableTreeMenu/TreeJson.ts | 81 +++ .../components/DraggableTreeMenu/TreeUtils.ts | 92 --- .../components/DraggableTreeMenu/index.tsx | 16 +- .../src/components/EditForm/MenuEditForm.tsx | 7 - .../admin/src/components/Layout/Bread.tsx | 58 +- frontend/admin/src/pages/board/[id].tsx | 655 +++++++++--------- frontend/admin/src/pages/board/index.tsx | 112 ++- frontend/admin/src/pages/menu/index.tsx | 1 - .../src/pages/posts/[board]/edit/[id].tsx | 204 +++--- 20 files changed, 726 insertions(+), 656 deletions(-) create mode 100644 frontend/admin/src/components/DraggableTreeMenu/TreeJson.ts diff --git a/backend/board-service/src/main/java/org/egovframe/cloud/boardservice/api/board/dto/BoardListResponseDto.java b/backend/board-service/src/main/java/org/egovframe/cloud/boardservice/api/board/dto/BoardListResponseDto.java index 164bbdf..f06d3f8 100644 --- a/backend/board-service/src/main/java/org/egovframe/cloud/boardservice/api/board/dto/BoardListResponseDto.java +++ b/backend/board-service/src/main/java/org/egovframe/cloud/boardservice/api/board/dto/BoardListResponseDto.java @@ -58,6 +58,11 @@ public class BoardListResponseDto implements Serializable { */ private LocalDateTime createdDate; + /** + * 게시글이 있는지 여부 + */ + private Boolean isPosts; + /** * 게시판 목록 응답 DTO 생성자 * @@ -66,10 +71,11 @@ public class BoardListResponseDto implements Serializable { * @param skinTypeCode 스킨 유형 코드 */ @QueryProjection - public BoardListResponseDto(Integer boardNo, String boardName, String skinTypeCode) { + public BoardListResponseDto(Integer boardNo, String boardName, String skinTypeCode, Boolean isPosts) { this.boardNo = boardNo; this.boardName = boardName; this.skinTypeCode = skinTypeCode; + this.isPosts = isPosts; } /** @@ -82,12 +88,13 @@ public class BoardListResponseDto implements Serializable { * @param createdDate 생성 일시 */ @QueryProjection - public BoardListResponseDto(Integer boardNo, String boardName, String skinTypeCode, String skinTypeCodeName, LocalDateTime createdDate) { + public BoardListResponseDto(Integer boardNo, String boardName, String skinTypeCode, String skinTypeCodeName, LocalDateTime createdDate, Boolean isPosts) { this.boardNo = boardNo; this.boardName = boardName; this.skinTypeCode = skinTypeCode; this.skinTypeCodeName = skinTypeCodeName; this.createdDate = createdDate; + this.isPosts = isPosts; } } diff --git a/backend/board-service/src/main/java/org/egovframe/cloud/boardservice/api/posts/dto/PostsDeleteRequestDto.java b/backend/board-service/src/main/java/org/egovframe/cloud/boardservice/api/posts/dto/PostsDeleteRequestDto.java index 5b21800..6b085f4 100644 --- a/backend/board-service/src/main/java/org/egovframe/cloud/boardservice/api/posts/dto/PostsDeleteRequestDto.java +++ b/backend/board-service/src/main/java/org/egovframe/cloud/boardservice/api/posts/dto/PostsDeleteRequestDto.java @@ -1,6 +1,7 @@ package org.egovframe.cloud.boardservice.api.posts.dto; import lombok.Getter; +import org.egovframe.cloud.boardservice.domain.board.Board; import org.egovframe.cloud.boardservice.domain.posts.Posts; import org.egovframe.cloud.boardservice.domain.posts.PostsId; @@ -49,6 +50,9 @@ public class PostsDeleteRequestDto { .boardNo(boardNo) .postsNo(postsNo) .build()) + .board(Board.builder() + .boardNo(boardNo) + .build()) .build(); } diff --git a/backend/board-service/src/main/java/org/egovframe/cloud/boardservice/api/posts/dto/PostsSimpleResponseDto.java b/backend/board-service/src/main/java/org/egovframe/cloud/boardservice/api/posts/dto/PostsSimpleResponseDto.java index dcd7ceb..8cec4b4 100644 --- a/backend/board-service/src/main/java/org/egovframe/cloud/boardservice/api/posts/dto/PostsSimpleResponseDto.java +++ b/backend/board-service/src/main/java/org/egovframe/cloud/boardservice/api/posts/dto/PostsSimpleResponseDto.java @@ -1,6 +1,7 @@ package org.egovframe.cloud.boardservice.api.posts.dto; import com.querydsl.core.annotations.QueryProjection; +import java.time.LocalDate; import lombok.Getter; import lombok.NoArgsConstructor; import org.egovframe.cloud.boardservice.api.board.dto.BoardResponseDto; @@ -92,7 +93,8 @@ public class PostsSimpleResponseDto implements Serializable { */ public PostsSimpleResponseDto setIsNew(BoardResponseDto boardResponseDto) { if (boardResponseDto.getNewDisplayDayCount() != null) { - this.isNew = createdDate.plusDays(boardResponseDto.getNewDisplayDayCount()).compareTo(LocalDateTime.now()) <= 0; + int compareTo = createdDate.toLocalDate().compareTo(LocalDate.now()); + this.isNew = 0 <= compareTo && compareTo <= boardResponseDto.getNewDisplayDayCount(); } else { this.isNew = false; } diff --git a/backend/board-service/src/main/java/org/egovframe/cloud/boardservice/domain/board/Board.java b/backend/board-service/src/main/java/org/egovframe/cloud/boardservice/domain/board/Board.java index 5819021..3a57ad8 100644 --- a/backend/board-service/src/main/java/org/egovframe/cloud/boardservice/domain/board/Board.java +++ b/backend/board-service/src/main/java/org/egovframe/cloud/boardservice/domain/board/Board.java @@ -1,12 +1,17 @@ package org.egovframe.cloud.boardservice.domain.board; +import java.util.ArrayList; +import java.util.List; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; +import org.egovframe.cloud.boardservice.domain.posts.Posts; import org.egovframe.cloud.servlet.domain.BaseEntity; import javax.persistence.*; import java.math.BigDecimal; +import org.hibernate.annotations.OnDelete; +import org.hibernate.annotations.OnDeleteAction; /** * org.egovframe.cloud.boardservice.domain.board.Board @@ -113,9 +118,9 @@ public class Board extends BaseEntity { /** * 게시물 엔티티 */ - /*@OneToMany(mappedBy = "board", fetch = FetchType.LAZY) + @OneToMany(mappedBy = "board", fetch = FetchType.LAZY) @OnDelete(action = OnDeleteAction.CASCADE) - private List posts;*/ + private List posts = new ArrayList<>(); /** * 빌더 패턴 클래스 생성자 diff --git a/backend/board-service/src/main/java/org/egovframe/cloud/boardservice/domain/board/BoardRepositoryImpl.java b/backend/board-service/src/main/java/org/egovframe/cloud/boardservice/domain/board/BoardRepositoryImpl.java index 9a3c5d9..5492617 100644 --- a/backend/board-service/src/main/java/org/egovframe/cloud/boardservice/domain/board/BoardRepositoryImpl.java +++ b/backend/board-service/src/main/java/org/egovframe/cloud/boardservice/domain/board/BoardRepositoryImpl.java @@ -1,11 +1,14 @@ package org.egovframe.cloud.boardservice.domain.board; +import static com.querydsl.core.types.Projections.constructor; + import com.google.common.base.CaseFormat; import com.querydsl.core.QueryResults; import com.querydsl.core.types.Order; import com.querydsl.core.types.OrderSpecifier; import com.querydsl.core.types.Path; import com.querydsl.core.types.dsl.BooleanExpression; +import com.querydsl.core.types.dsl.CaseBuilder; import com.querydsl.core.types.dsl.Expressions; import com.querydsl.jpa.JPQLQuery; import com.querydsl.jpa.impl.JPAQueryFactory; @@ -58,12 +61,16 @@ public class BoardRepositoryImpl implements BoardRepositoryCustom { @Override public Page findPage(RequestDto requestDto, Pageable pageable) { JPQLQuery query = jpaQueryFactory - .select(new QBoardListResponseDto( + .select(constructor(BoardListResponseDto.class, QBoard.board.boardNo, QBoard.board.boardName, QBoard.board.skinTypeCode, Expressions.as(QCode.code.codeName, "skinTypeCodeName"), - QBoard.board.createdDate + QBoard.board.createdDate, + new CaseBuilder() + .when(QBoard.board.posts.size().gt(0)) + .then(Boolean.TRUE) + .otherwise(Boolean.FALSE).as("isPosts") )) .from(QBoard.board) .leftJoin(QCode.code).on(QBoard.board.skinTypeCode.eq(QCode.code.codeId).and(QCode.code.parentCodeId.eq("skin_type_code"))) diff --git a/backend/board-service/src/main/java/org/egovframe/cloud/boardservice/domain/posts/Posts.java b/backend/board-service/src/main/java/org/egovframe/cloud/boardservice/domain/posts/Posts.java index be15707..c47ff00 100644 --- a/backend/board-service/src/main/java/org/egovframe/cloud/boardservice/domain/posts/Posts.java +++ b/backend/board-service/src/main/java/org/egovframe/cloud/boardservice/domain/posts/Posts.java @@ -128,7 +128,6 @@ public class Posts extends BaseEntity { String postsContent, String postsAnswerContent, String attachmentCode, Integer readCount, Boolean noticeAt, Integer deleteAt, User creator, List comments) { - this.board = board; this.postsId = postsId; this.postsTitle = postsTitle; this.postsContent = postsContent; @@ -139,6 +138,17 @@ public class Posts extends BaseEntity { this.deleteAt = deleteAt; this.creator = creator; this.comments = comments == null ? null : new ArrayList<>(comments); + setBoard(board); + } + + /** + * 연관관계 설정 + * + * @param board + */ + public void setBoard(Board board) { + this.board = board; + board.getPosts().add(this); } /** diff --git a/backend/board-service/src/main/java/org/egovframe/cloud/boardservice/service/posts/PostsService.java b/backend/board-service/src/main/java/org/egovframe/cloud/boardservice/service/posts/PostsService.java index 81466a9..f6607b4 100644 --- a/backend/board-service/src/main/java/org/egovframe/cloud/boardservice/service/posts/PostsService.java +++ b/backend/board-service/src/main/java/org/egovframe/cloud/boardservice/service/posts/PostsService.java @@ -1,23 +1,9 @@ package org.egovframe.cloud.boardservice.service.posts; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import org.egovframe.cloud.boardservice.api.board.dto.BoardResponseDto; -import org.egovframe.cloud.boardservice.api.posts.dto.PostsDeleteRequestDto; -import org.egovframe.cloud.boardservice.api.posts.dto.PostsListResponseDto; -import org.egovframe.cloud.boardservice.api.posts.dto.PostsResponseDto; -import org.egovframe.cloud.boardservice.api.posts.dto.PostsSaveRequestDto; -import org.egovframe.cloud.boardservice.api.posts.dto.PostsSimpleResponseDto; -import org.egovframe.cloud.boardservice.api.posts.dto.PostsSimpleSaveRequestDto; -import org.egovframe.cloud.boardservice.api.posts.dto.PostsUpdateRequestDto; -import org.egovframe.cloud.boardservice.domain.posts.Posts; -import org.egovframe.cloud.boardservice.domain.posts.PostsId; -import org.egovframe.cloud.boardservice.domain.posts.PostsRead; -import org.egovframe.cloud.boardservice.domain.posts.PostsReadRepository; -import org.egovframe.cloud.boardservice.domain.posts.PostsRepository; +import org.egovframe.cloud.boardservice.api.posts.dto.*; +import org.egovframe.cloud.boardservice.domain.posts.*; import org.egovframe.cloud.boardservice.service.board.BoardService; import org.egovframe.cloud.common.dto.AttachmentEntityMessage; import org.egovframe.cloud.common.dto.RequestDto; @@ -32,6 +18,11 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + /** * org.egovframe.cloud.postsservice.service.posts.PostsService *

@@ -111,7 +102,6 @@ public class PostsService extends AbstractService { board.setNewestPosts(posts.stream().map(post -> post.setIsNew(board)) .collect(Collectors.toList())); } - data.put(board.getBoardNo(), board); } diff --git a/backend/portal-service/src/main/java/org/egovframe/cloud/portalservice/api/menu/MenuApiController.java b/backend/portal-service/src/main/java/org/egovframe/cloud/portalservice/api/menu/MenuApiController.java index ef62b0c..06ee0f8 100644 --- a/backend/portal-service/src/main/java/org/egovframe/cloud/portalservice/api/menu/MenuApiController.java +++ b/backend/portal-service/src/main/java/org/egovframe/cloud/portalservice/api/menu/MenuApiController.java @@ -66,7 +66,7 @@ public class MenuApiController { */ @GetMapping("/api/v1/menus/{menuId}") public MenuResponseDto findById(@PathVariable Long menuId) { - return menuService.findById(menuId); + return menuService.findMenuResponseDtoById(menuId); } /** diff --git a/backend/portal-service/src/main/java/org/egovframe/cloud/portalservice/api/menu/dto/MenuTreeResponseDto.java b/backend/portal-service/src/main/java/org/egovframe/cloud/portalservice/api/menu/dto/MenuTreeResponseDto.java index f4b7f94..040e2ba 100644 --- a/backend/portal-service/src/main/java/org/egovframe/cloud/portalservice/api/menu/dto/MenuTreeResponseDto.java +++ b/backend/portal-service/src/main/java/org/egovframe/cloud/portalservice/api/menu/dto/MenuTreeResponseDto.java @@ -1,5 +1,6 @@ package org.egovframe.cloud.portalservice.api.menu.dto; +import java.util.Comparator; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; @@ -52,6 +53,9 @@ public class MenuTreeResponseDto { this.level = entity.getLevel(); this.children = entity.getChildren().stream() .map(children -> new MenuTreeResponseDto(children)) + .sorted(Comparator.comparing(MenuTreeResponseDto::getSortSeq)) .collect(Collectors.toList()); + } + } diff --git a/backend/portal-service/src/main/java/org/egovframe/cloud/portalservice/domain/menu/Menu.java b/backend/portal-service/src/main/java/org/egovframe/cloud/portalservice/domain/menu/Menu.java index bfd9fdb..6961db3 100644 --- a/backend/portal-service/src/main/java/org/egovframe/cloud/portalservice/domain/menu/Menu.java +++ b/backend/portal-service/src/main/java/org/egovframe/cloud/portalservice/domain/menu/Menu.java @@ -127,20 +127,9 @@ public class Menu extends BaseEntity { public Menu updateDnD(Menu parent, Integer sortSeq, Integer level) { this.sortSeq = sortSeq; this.level = level; + if (parent == null) { - Menu oldParent = this.getParent(); - - if (oldParent == null) { - return this; - } - - Menu old = oldParent.getChildren().stream().filter(item -> item.getId().equals(this.id)).findAny().orElse(null); - if (old != null) { - oldParent.getChildren().remove(old); - } - this.parent = null; - - return this; + return updateOldParent(); } if (parent.equals(this.parent)) { @@ -149,7 +138,21 @@ public class Menu extends BaseEntity { this.parent = parent; parent.getChildren().add(this); + return this; + } + private Menu updateOldParent() { + Menu oldParent = this.getParent(); + + if (oldParent == null) { + return this; + } + + Menu old = oldParent.getChildren().stream().filter(item -> item.getId().equals(this.id)).findAny().orElse(null); + if (old != null) { + oldParent.getChildren().remove(old); + } + this.parent = null; return this; } diff --git a/backend/portal-service/src/main/java/org/egovframe/cloud/portalservice/service/menu/MenuService.java b/backend/portal-service/src/main/java/org/egovframe/cloud/portalservice/service/menu/MenuService.java index 025f79c..34def85 100644 --- a/backend/portal-service/src/main/java/org/egovframe/cloud/portalservice/service/menu/MenuService.java +++ b/backend/portal-service/src/main/java/org/egovframe/cloud/portalservice/service/menu/MenuService.java @@ -35,7 +35,7 @@ import java.util.List; */ @Slf4j @RequiredArgsConstructor -@Transactional(readOnly = true) +@Transactional @Service public class MenuService extends AbstractService { @@ -48,6 +48,7 @@ public class MenuService extends AbstractService { * @param siteId * @return */ + @Transactional(readOnly = true) public List findTreeBySiteId(Long siteId) { return menuRepository.findTreeBySiteId(siteId); } @@ -58,7 +59,8 @@ public class MenuService extends AbstractService { * @param menuId * @return */ - public MenuResponseDto findById(Long menuId) { + @Transactional(readOnly = true) + public MenuResponseDto findMenuResponseDtoById(Long menuId) { return menuRepository.findByIdWithConnectName(menuId); } @@ -68,7 +70,6 @@ public class MenuService extends AbstractService { * @param menuTreeRequestDto * @return */ - @Transactional public MenuTreeResponseDto save(MenuTreeRequestDto menuTreeRequestDto) { Site site = siteRepository.findById(menuTreeRequestDto.getSiteId()) .orElseThrow(() -> @@ -77,9 +78,7 @@ public class MenuService extends AbstractService { Menu parent = null; if (menuTreeRequestDto.getParentId() != null) { - parent = menuRepository.findById(menuTreeRequestDto.getParentId()) - .orElseThrow(() -> - new EntityNotFoundException(getMessage("valid.notexists.format", new Object[]{getMessage("menu")}) + " ID= " + menuTreeRequestDto.getParentId())); + parent = findById(menuTreeRequestDto.getParentId()); } Menu menu = menuRepository.save(Menu.builder() @@ -102,11 +101,8 @@ public class MenuService extends AbstractService { * @param name * @return */ - @Transactional public MenuTreeResponseDto updateName(Long menuId, String name) throws EntityNotFoundException { - Menu menu = menuRepository.findById(menuId) - .orElseThrow(() -> - new EntityNotFoundException(getMessage("valid.notexists.format", new Object[]{getMessage("menu")}) + " ID= " + menuId)); + Menu menu = findById(menuId); menu.updateName(name); @@ -121,11 +117,8 @@ public class MenuService extends AbstractService { * @param updateRequestDto * @return */ - @Transactional public MenuResponseDto update(Long menuId, MenuUpdateRequestDto updateRequestDto) throws EntityNotFoundException, BusinessMessageException { - Menu menu = menuRepository.findById(menuId) - .orElseThrow(() -> - new EntityNotFoundException(getMessage("valid.notexists.format", new Object[]{getMessage("menu")}) + " ID= " + menuId)); + Menu menu = findById(menuId); //컨텐츠 or 게시판인 경우 connectId 필수 if ("contents".equals(updateRequestDto.getMenuType()) || "board".equals(updateRequestDto.getMenuType())) { @@ -153,11 +146,8 @@ public class MenuService extends AbstractService { * * @param menuId */ - @Transactional public void delete(Long menuId) { - Menu menu = menuRepository.findById(menuId) - .orElseThrow(() -> - new EntityNotFoundException(getMessage("valid.notexists.format", new Object[]{getMessage("menu")}) + " ID= " + menuId)); + Menu menu = findById(menuId); menuRepository.delete(menu); } @@ -170,10 +160,10 @@ public class MenuService extends AbstractService { * @param level */ private void recursive(MenuDnDRequestDto dto, Menu parent, Integer sortSeq, Integer level) { - Menu menu = menuRepository.findById(dto.getMenuId()) - .orElseThrow(() -> - new EntityNotFoundException(getMessage("valid.notexists.format", new Object[]{getMessage("menu")}) + " ID= " + dto.getMenuId())); + Menu menu = findById(dto.getMenuId()); + menu.updateDnD(parent, sortSeq, level); + if (dto.getChildren() == null || dto.getChildren().size() <= 0) { return; } @@ -191,11 +181,21 @@ public class MenuService extends AbstractService { * @param menuDnDRequestDtoList * @return */ - @Transactional public Long updateDnD(Long siteId, List menuDnDRequestDtoList) { for (int i = 0; i < menuDnDRequestDtoList.size(); i++) { - recursive(menuDnDRequestDtoList.get(i), null, i+1, 1); + MenuDnDRequestDto requestDto = menuDnDRequestDtoList.get(i); + Menu parent = null; + if (requestDto.getParentId() != null) { + parent = findById(requestDto.getParentId()); + } + recursive(requestDto, parent, requestDto.getSortSeq(), requestDto.getLevel()); } return siteId; } + + private Menu findById(Long id) { + return menuRepository.findById(id) + .orElseThrow(() -> + new EntityNotFoundException(getMessage("valid.notexists.format", new Object[]{getMessage("menu")}) + " ID= " + id)); + } } diff --git a/frontend/admin/src/components/DraggableTreeMenu/TreeJson.ts b/frontend/admin/src/components/DraggableTreeMenu/TreeJson.ts new file mode 100644 index 0000000..c520e35 --- /dev/null +++ b/frontend/admin/src/components/DraggableTreeMenu/TreeJson.ts @@ -0,0 +1,81 @@ +import { + ItemId, + TreeData, + TreeDestinationPosition, + TreeItem, + TreeSourcePosition, +} from '@atlaskit/tree' +import { IMenuTree } from '@service' +import produce from 'immer' + +export class TreeJson { + private readonly treeJson: IMenuTree[] + private readonly treeData: Record + private readonly source: TreeSourcePosition + private readonly destination: TreeDestinationPosition + + constructor( + tree: TreeData, + source: TreeSourcePosition, + destination: TreeDestinationPosition, + ) { + this.treeJson = [] + this.treeData = tree.items + this.source = source + this.destination = destination + } + + convert(): IMenuTree[] { + if (this.source.parentId === this.destination.parentId) { + if (this.source.parentId === '0') { + this.updateTopLevel() + return this.treeJson + } + + this.update(this.source.parentId) + return this.treeJson + } + + this.update(this.source.parentId) + this.update(this.destination.parentId) + + return this.treeJson + } + + private updateTopLevel() { + const children = this.treeData['0'].children + + children.map((item, idx) => { + let toplevel = this.treeData[item] + this.treeJson.push( + produce(toplevel.data as IMenuTree, draft => { + draft.sortSeq = idx + 1 + }), + ) + }) + } + + private update(parentId: ItemId) { + let parent = this.treeData[parentId] + + let children: IMenuTree[] = [] + + const menuId = parent.data?.menuId + parent.children.map((item, idx) => { + let data = this.treeData[item].data as IMenuTree + let child = produce(data, draft => { + draft.sortSeq = idx + 1 + draft.parentId = menuId + }) + children.push(child) + }) + + if (parent.data) { + this.treeJson.push( + produce(parent.data as IMenuTree, draft => { + draft.children = children + }), + ) + } + } +} diff --git a/frontend/admin/src/components/DraggableTreeMenu/TreeUtils.ts b/frontend/admin/src/components/DraggableTreeMenu/TreeUtils.ts index 4a5b5cf..bd9e123 100644 --- a/frontend/admin/src/components/DraggableTreeMenu/TreeUtils.ts +++ b/frontend/admin/src/components/DraggableTreeMenu/TreeUtils.ts @@ -1,4 +1,3 @@ -import { TreeData, TreeItem } from '@atlaskit/tree' import { IMenuTree } from '@service' import produce from 'immer' import DraggableTreeBuilder from './DraaggableTreeBuilder' @@ -28,97 +27,6 @@ export const convertJsonToTreeData = (data: IMenuTree[]) => { return root.build() } -/** - * atlaskit flat tree data -> hierarchy json data - * - * @param tree - * @returns - */ -export const convertTreeDataToJson = (tree: TreeData) => { - let newTreeItem: TreeItem[] = [] - - const arrTree = Object.values(tree.items) - const root = arrTree.shift() - - root.children.map((itemId, index) => { - const data = arrTree.splice( - arrTree.findIndex(item => item.id === itemId), - 1, - ) - data.map(item => { - item.data = produce(item.data, draft => { - draft.sortSeq = index + 1 - draft.parentId = null - }) - newTreeItem.push(item) - }) - }) - - const convert = (target: TreeItem[], source: TreeItem[]) => { - while (source.length > 0) { - const data = source.shift() - - if (data.hasChildren) { - target.push( - produce(data, draft => { - draft.hasChildren = false - }), - ) - } - - const idx = target.findIndex(item => item.children.includes(data.id)) - - if (idx > -1) { - const parent = produce(target[idx].data as IMenuTree, draft => { - const childIdx = draft.children.findIndex( - i => i.menuId === data.data.menuId, - ) - - const child = produce(data.data as IMenuTree, childDraft => { - if (childIdx === -1) { - childDraft.sortSeq = draft.children.length + 1 - } - childDraft.parentId = draft.menuId - }) - - if (childIdx > -1) { - draft.children[childIdx] = child - } else { - draft.children.push(child) - } - }) - - target[idx] = produce(target[idx], draft => { - draft.data = parent - }) - } - } - - return target - } - - let target = newTreeItem.slice() - let source = arrTree.slice() - - while (true) { - newTreeItem = convert(target, source).slice() - - if (root.children.length === newTreeItem.length) { - break - } - - target = newTreeItem.filter(item => root.children.includes(item.id)) - source = newTreeItem.filter(item => !root.children.includes(item.id)) - } - - const newData: IMenuTree[] = [] - newTreeItem.map(treeItem => { - newData.push(Object.assign(treeItem.data)) - }) - - return newData -} - export interface IFindTree { item: any parent: any diff --git a/frontend/admin/src/components/DraggableTreeMenu/index.tsx b/frontend/admin/src/components/DraggableTreeMenu/index.tsx index e26a8c5..f1c3a81 100644 --- a/frontend/admin/src/components/DraggableTreeMenu/index.tsx +++ b/frontend/admin/src/components/DraggableTreeMenu/index.tsx @@ -1,5 +1,3 @@ -import React, { useEffect, useState } from 'react' -import { createStyles, makeStyles, Theme } from '@material-ui/core/styles' import Tree, { ItemId, moveItemOnTree, @@ -9,11 +7,14 @@ import Tree, { TreeDestinationPosition, TreeSourcePosition, } from '@atlaskit/tree' -import { convertTreeDataToJson, convertJsonToTreeData } from './TreeUtils' +import { createStyles, makeStyles, Theme } from '@material-ui/core/styles' import { IMenuTree } from '@service' -import DraaggableTreeMenuItem from './DraaggableTreeMenuItem' -import { useRecoilState, useRecoilValue } from 'recoil' import { draggableTreeExpandedAtom, draggableTreeSelectedAtom } from '@stores' +import React, { useEffect, useState } from 'react' +import { useRecoilState, useRecoilValue } from 'recoil' +import DraaggableTreeMenuItem from './DraaggableTreeMenuItem' +import { TreeJson } from './TreeJson' +import { convertJsonToTreeData } from './TreeUtils' const useStyles = makeStyles((theme: Theme) => createStyles({ @@ -126,8 +127,9 @@ function DraggableTreeMenu(props: DraggableTreeMenuProps) { const newTree = moveItemOnTree(tree, source, destination) - const convert = await convertTreeDataToJson(newTree) - handleTreeDnD(convert) + const treeJson = new TreeJson(newTree, source, destination) + const convertTree = treeJson.convert() + handleTreeDnD(convertTree) setTree(newTree) } diff --git a/frontend/admin/src/components/EditForm/MenuEditForm.tsx b/frontend/admin/src/components/EditForm/MenuEditForm.tsx index d5de9d6..78aa7a4 100644 --- a/frontend/admin/src/components/EditForm/MenuEditForm.tsx +++ b/frontend/admin/src/components/EditForm/MenuEditForm.tsx @@ -91,12 +91,6 @@ const MenuEditForm = (props: MenuEditFormProps) => { menuTypes[0]?.codeId, ) - useEffect(() => { - if (errors) { - console.log(errors) - } - }, [errors]) - const [connectIdState, setConnectIdState] = useState({}) const [dialogOpen, setDialogOpen] = useState(false) @@ -189,7 +183,6 @@ const MenuEditForm = (props: MenuEditFormProps) => { } const handleSaveBefore = (formData: IMenuInfoForm) => { - console.log('before ', formData) formData = produce(formData, draft => { draft.menuType = menuTypeState draft.menuTypeName = menuTypes.find( diff --git a/frontend/admin/src/components/Layout/Bread.tsx b/frontend/admin/src/components/Layout/Bread.tsx index 2ad28c3..13a8c55 100644 --- a/frontend/admin/src/components/Layout/Bread.tsx +++ b/frontend/admin/src/components/Layout/Bread.tsx @@ -1,12 +1,12 @@ -import React, { useCallback } from 'react' -import { useRouter } from 'next/router' -import Typography from '@material-ui/core/Typography' import Breadcrumbs from '@material-ui/core/Breadcrumbs' import Link from '@material-ui/core/Link' -import { Theme, makeStyles } from '@material-ui/core/styles' -import { currentMenuStateAtom, flatMenusSelect } from '@stores' -import { useRecoilValue } from 'recoil' +import { makeStyles, Theme } from '@material-ui/core/styles' +import Typography from '@material-ui/core/Typography' +import { currentMenuStateAtom, ISideMenu, menuStateAtom } from '@stores' +import { useRouter } from 'next/router' +import React, { useCallback } from 'react' import { useTranslation } from 'react-i18next' +import { useRecoilValue } from 'recoil' const useStyles = makeStyles((theme: Theme) => ({ root: { @@ -17,10 +17,33 @@ const useStyles = makeStyles((theme: Theme) => ({ const Bread: React.FC = () => { const classes = useStyles() const router = useRouter() - const flatMenus = useRecoilValue(flatMenusSelect) + const menus = useRecoilValue(menuStateAtom) const current = useRecoilValue(currentMenuStateAtom) const { i18n } = useTranslation() + const findParent = useCallback( + (menu: ISideMenu) => { + let parent: ISideMenu + const findItems = item => { + if (item.id === menu.parentId) { + parent = item + } + + if (item.children) { + item.children.map(v => { + return findItems(v) + }) + } + } + + menus.map(item => { + findItems(item) + }) + return parent + }, + [menus, current], + ) + const hierarchy = useCallback(() => { if (!current) { return @@ -35,21 +58,16 @@ const Bread: React.FC = () => { } let trees = [] - const arr = flatMenus.slice( - 0, - flatMenus.findIndex(item => item.id === current.id) + 1, - ) - trees.push(current) - arr.reverse().some(item => { - if (item.level < current.level) { - trees.push(item) + let findMenu = current + while (true) { + let parent = findParent(findMenu) + trees.push(parent) + findMenu = parent + if (parent.level === 1) { + break } - - if (item.level === 1) { - return true - } - }) + } let nodes = trees.reverse().map(item => item.id === current.id ? ( diff --git a/frontend/admin/src/pages/board/[id].tsx b/frontend/admin/src/pages/board/[id].tsx index e15d1d6..012089b 100644 --- a/frontend/admin/src/pages/board/[id].tsx +++ b/frontend/admin/src/pages/board/[id].tsx @@ -202,339 +202,338 @@ const BoardItem = ({ return (

-
- - - - ( - - )} - /> - {errors.boardName && ( - + + + ( + )} - - - - - - {t('board.skin_type_code')} - - ( - - )} + defaultValue={''} + /> + {errors.boardName && ( + - - - - - - ( - - )} - /> - {errors.titleDisplayLength && ( - - )} - - - - - ( - - )} - /> - {errors.postDisplayCount && ( - - )} - - - - - - ( - - )} - /> - {errors.pageDisplayCount && ( - - )} - - - - - ( - - )} - /> - {errors.newDisplayDayCount && ( - - )} - - - - - - - getSwitch(onChange, ref, value) - } - /> - } - /> - - - - - - getSwitch(onChange, ref, value) - } - /> - } - /> - - - - - - ( - - )} - /> - } - /> - - - - - - getSwitch(onChange, ref, value) - } - /> - } - /> - - - - - + )} + - + + + + {t('board.skin_type_code')} + + ( + + )} + /> + + + + + + ( + + )} + /> + {errors.titleDisplayLength && ( + + )} + + + + + ( + + )} + /> + {errors.postDisplayCount && ( + + )} + + + + + + ( + + )} + /> + {errors.pageDisplayCount && ( + + )} + + + + + ( + + )} + /> + {errors.newDisplayDayCount && ( + + )} + + + + + + + getSwitch(onChange, ref, value) + } + /> + } + /> + + + + + + getSwitch(onChange, ref, value) + } + /> + } + /> + + + + + + ( + + )} + /> + } + /> + + + + + + getSwitch(onChange, ref, value) + } + /> + } + /> + + + + + +
{ diff --git a/frontend/admin/src/pages/board/index.tsx b/frontend/admin/src/pages/board/index.tsx index 6be4fa9..71a2a1c 100644 --- a/frontend/admin/src/pages/board/index.tsx +++ b/frontend/admin/src/pages/board/index.tsx @@ -1,9 +1,14 @@ -import React, { useCallback, useMemo } from 'react' -import { AxiosError } from 'axios' -import { NextPage } from 'next' -import { useRouter } from 'next/router' -import { TFunction, useTranslation } from 'next-i18next' - +import { CustomButtons, IButtonProps } from '@components/Buttons' +import { ConfirmDialog } from '@components/Confirm' +import { PopupProps } from '@components/DialogPopup' +import Search, { IKeywordType } from '@components/Search' +import CustomDataGrid from '@components/Table/CustomDataGrid' +import { GRID_PAGE_SIZE } from '@constants' +import usePage from '@hooks/usePage' +import useSearchTypes from '@hooks/useSearchType' +// 내부 컴포넌트 및 custom hook, etc... +import { convertStringToDateFormat } from '@libs/date' +import Button from '@material-ui/core/Button' // material-ui deps import { createStyles, makeStyles, Theme } from '@material-ui/core/styles' import { @@ -12,28 +17,21 @@ import { GridValueFormatterParams, GridValueGetterParams, } from '@material-ui/data-grid' - -// 내부 컴포넌트 및 custom hook, etc... -import { convertStringToDateFormat } from '@libs/date' -import CustomDataGrid from '@components/Table/CustomDataGrid' -import { CustomButtons, IButtonProps } from '@components/Buttons' -import { Page, rownum } from '@utils' -import Search, { IKeywordType } from '@components/Search' - -// 상태관리 recoil -import { useRecoilValue, useSetRecoilState } from 'recoil' +// api +import { boardService } from '@service' import { conditionAtom, detailButtonsSnackAtom, errorStateSelector, } from '@stores' - -// api -import { boardService } from '@service' -import { PopupProps } from '@components/DialogPopup' -import Button from '@material-ui/core/Button' -import usePage from '@hooks/usePage' -import { GRID_PAGE_SIZE } from '@constants' +import { Page, rownum } from '@utils' +import { AxiosError } from 'axios' +import { NextPage } from 'next' +import { TFunction, useTranslation } from 'next-i18next' +import { useRouter } from 'next/router' +import React, { useCallback, useMemo, useState } from 'react' +// 상태관리 recoil +import { useRecoilValue, useSetRecoilState } from 'recoil' // material-ui style const useStyles = makeStyles((theme: Theme) => @@ -133,12 +131,12 @@ const Board: NextPage = props => { const setSuccessSnackBar = useSetRecoilState(detailButtonsSnackAtom) // 조회조건 select items - const searchTypes: IKeywordType[] = [ + const searchTypes: IKeywordType[] = useSearchTypes([ { key: 'boardName', label: t('board.board_name'), }, - ] + ]) /** * 상태관리 필요한 훅 @@ -150,6 +148,14 @@ const Board: NextPage = props => { // 현 페이지내 필요한 hook const { page, setPageValue } = usePage(conditionKey) + const [deleteConfirmState, setDeleteConfirmState] = useState<{ + open: boolean + boardNo: number + }>({ + open: false, + boardNo: null, + }) + // 목록 데이터 조회 및 관리 const { data, mutate } = boardService.search({ keywordType: keywordState?.keywordType || 'boardName', @@ -177,19 +183,17 @@ const Board: NextPage = props => { // 삭제 const handleDelete = useCallback( (row: any) => { - const { boardNo } = row + const { boardNo, isPosts } = row - setSuccessSnackBar('loading') + if (isPosts) { + setDeleteConfirmState({ + open: true, + boardNo, + }) + return + } - boardService.delete({ - boardNo, - callback: () => { - setSuccessSnackBar('success') - - mutate() - }, - errorCallback, - }) + deleteBoard(boardNo) }, [errorCallback, mutate, setSuccessSnackBar], ) @@ -251,6 +255,36 @@ const Board: NextPage = props => { route.push('board/-1') } + const handleConfirmClose = () => { + setDeleteConfirmState({ + open: false, + boardNo: null, + }) + } + + const deleteBoard = (boardNo: number) => { + setSuccessSnackBar('loading') + + boardService.delete({ + boardNo, + callback: () => { + setSuccessSnackBar('success') + + mutate() + }, + errorCallback, + }) + } + + const handleConfirm = () => { + const { boardNo } = deleteConfirmState + deleteBoard(boardNo) + setDeleteConfirmState({ + open: false, + boardNo: null, + }) + } + return (
= props => { onPageChange={handlePageChange} getRowId={r => r.boardNo} /> +
) } diff --git a/frontend/admin/src/pages/menu/index.tsx b/frontend/admin/src/pages/menu/index.tsx index 6fc900c..1dc3543 100644 --- a/frontend/admin/src/pages/menu/index.tsx +++ b/frontend/admin/src/pages/menu/index.tsx @@ -166,7 +166,6 @@ const Menu = ({ sites, menuTypes }: MenuProps) => { } const handleSave = async (formData: IMenuInfoForm) => { - console.log(formData) setSuccessSnackBar('loading') try { const result = await menuService.update(treeSelected.menuId, formData) diff --git a/frontend/admin/src/pages/posts/[board]/edit/[id].tsx b/frontend/admin/src/pages/posts/[board]/edit/[id].tsx index c56e5cf..462eb28 100644 --- a/frontend/admin/src/pages/posts/[board]/edit/[id].tsx +++ b/frontend/admin/src/pages/posts/[board]/edit/[id].tsx @@ -302,81 +302,119 @@ const PostsItem = ({ boardNo, postsNo, board, initData }: IPostsItemsProps) => { return (
-
- - + + + + ( + + )} + control={control} + rules={{ required: true, maxLength: 100 }} + /> + {errors.postsTitle && ( + + )} + + + + + ( + + )} + /> + } + /> + + + + {board.editorUseAt && ( + + )} + {!board.editorUseAt && ( ( )} - control={control} - rules={{ required: true, maxLength: 100 }} /> - {errors.postsTitle && ( + {errors.postsContent && ( )} - - - - ( - - )} - /> - } - /> - - + )} + + {(board.skinTypeCode === SKINT_TYPE_CODE_FAQ || + board.skinTypeCode === SKINT_TYPE_CODE_QNA) && ( {board.editorUseAt && ( - + )} {!board.editorUseAt && ( ( { /> )} /> - {errors.postsContent && ( - - )} )} - {(board.skinTypeCode === SKINT_TYPE_CODE_FAQ || - board.skinTypeCode === SKINT_TYPE_CODE_QNA) && ( - - {board.editorUseAt && ( - + )} + {board.uploadUseAt && ( + + + + {attachData && ( + )} - {!board.editorUseAt && ( - - ( - - )} - /> - - )} - - )} - {board.uploadUseAt && ( - - - - {attachData && ( - - )} - - - )} - - + + + )} +