From 8e8d1f91b4bf6fdef739b0fa560a6367c1a7344e Mon Sep 17 00:00:00 2001 From: backuppc Date: Wed, 3 Dec 2025 10:32:10 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EB=A9=94=EB=89=B4=20=EC=9E=AC=EB=B0=B0?= =?UTF-8?q?=EC=B9=98=20=EB=B0=8F=20UX=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 메뉴: 즐겨찾기를 할일 좌측으로 이동 - 게시판: 답글 있는 게시글 삭제 방지 (댓글은 허용) - 즐겨찾기: ESC 키로 다이얼로그 닫기 지원 - 프로젝트: 기본 필터를 검토/진행/완료로 변경 (보류 해제) --- .../Web/MachineBridge/MachineBridge.Board.cs | 13 +++ .../components/favorite/FavoriteDialog.tsx | 13 +++ .../frontend/src/components/layout/Header.tsx | 98 ++++++++++++------- Project/frontend/src/pages/BoardList.tsx | 37 +++++-- Project/frontend/src/pages/Jobreport.tsx | 4 +- Project/frontend/src/pages/Project.tsx | 6 +- 6 files changed, 122 insertions(+), 49 deletions(-) diff --git a/Project/Web/MachineBridge/MachineBridge.Board.cs b/Project/Web/MachineBridge/MachineBridge.Board.cs index 8010978..ab4b0d0 100644 --- a/Project/Web/MachineBridge/MachineBridge.Board.cs +++ b/Project/Web/MachineBridge/MachineBridge.Board.cs @@ -301,6 +301,19 @@ namespace Project.Web return JsonConvert.SerializeObject(new { Success = false, Message = "삭제 권한이 없습니다." }); } + // 답글 존재 여부 확인 (is_comment=false인 답글만) + var replyCheckCmd = new SqlCommand(@" + SELECT COUNT(*) + FROM EETGW_Board + WHERE root_idx = @idx AND depth > 0 AND (is_comment IS NULL OR is_comment = 0)", conn); + replyCheckCmd.Parameters.Add("@idx", SqlDbType.Int).Value = idx; + var replyCount = (int)replyCheckCmd.ExecuteScalar(); + + if (replyCount > 0) + { + return JsonConvert.SerializeObject(new { Success = false, Message = "답글이 있는 게시글은 삭제할 수 없습니다. 답글을 먼저 삭제해주세요." }); + } + var cmd = new SqlCommand("DELETE FROM EETGW_Board WHERE idx = @idx", conn); cmd.Parameters.Add("@idx", SqlDbType.Int).Value = idx; diff --git a/Project/frontend/src/components/favorite/FavoriteDialog.tsx b/Project/frontend/src/components/favorite/FavoriteDialog.tsx index 1a3a031..3dfbebe 100644 --- a/Project/frontend/src/components/favorite/FavoriteDialog.tsx +++ b/Project/frontend/src/components/favorite/FavoriteDialog.tsx @@ -93,6 +93,19 @@ export function FavoriteDialog({ isOpen, onClose }: FavoriteDialogProps) { } }, [isOpen]); + useEffect(() => { + const handleEscape = (e: KeyboardEvent) => { + if (e.key === 'Escape' && isOpen) { + onClose(); + } + }; + + if (isOpen) { + window.addEventListener('keydown', handleEscape); + return () => window.removeEventListener('keydown', handleEscape); + } + }, [isOpen, onClose]); + const loadFavorites = async () => { setLoading(true); try { diff --git a/Project/frontend/src/components/layout/Header.tsx b/Project/frontend/src/components/layout/Header.tsx index e6cb12e..098514c 100644 --- a/Project/frontend/src/components/layout/Header.tsx +++ b/Project/frontend/src/components/layout/Header.tsx @@ -80,13 +80,12 @@ const leftDropdownMenus: DropdownMenuConfig[] = [ }, ]; -// 좌측 단독 액션 버튼 (즐겨찾기) -const leftActionItems: NavItem[] = [ - { icon: Star, label: '즐겨찾기', action: 'favorite' }, -]; +// 좌측 단독 액션 버튼 +const leftActionItems: NavItem[] = []; // 우측 메뉴 항목 const rightNavItems: NavItem[] = [ + { icon: Star, label: '즐겨찾기', action: 'favorite' }, { path: '/todo', icon: CheckSquare, label: '할일' }, ]; @@ -490,23 +489,34 @@ export function Header(_props: HeaderProps) { ))} - {/* 우측 메뉴들 (할일) */} + {/* 우측 메뉴들 (즐겨찾기, 할일) */} {rightNavItems.map((item) => ( - - clsx( - 'flex items-center space-x-2 px-4 py-2 rounded-lg transition-all duration-200 text-sm font-medium', - isActive - ? 'bg-white/20 text-white shadow-lg' - : 'text-white/70 hover:bg-white/10 hover:text-white' - ) - } - > - - {item.label} - + item.path ? ( + + clsx( + 'flex items-center space-x-2 px-4 py-2 rounded-lg transition-all duration-200 text-sm font-medium', + isActive + ? 'bg-white/20 text-white shadow-lg' + : 'text-white/70 hover:bg-white/10 hover:text-white' + ) + } + > + + {item.label} + + ) : ( + + ) ))} @@ -573,24 +583,38 @@ export function Header(_props: HeaderProps) { /> ))} - {/* 우측 메뉴들 (할일) */} + {/* 우측 메뉴들 (즐겨찾기, 할일) */} {rightNavItems.map((item) => ( - setIsMobileMenuOpen(false)} - className={({ isActive }) => - clsx( - 'flex items-center space-x-3 px-4 py-3 rounded-lg transition-all duration-200', - isActive - ? 'bg-white/20 text-white' - : 'text-white/70 hover:bg-white/10 hover:text-white' - ) - } - > - - {item.label} - + item.path ? ( + setIsMobileMenuOpen(false)} + className={({ isActive }) => + clsx( + 'flex items-center space-x-3 px-4 py-3 rounded-lg transition-all duration-200', + isActive + ? 'bg-white/20 text-white' + : 'text-white/70 hover:bg-white/10 hover:text-white' + ) + } + > + + {item.label} + + ) : ( + + ) ))} diff --git a/Project/frontend/src/pages/BoardList.tsx b/Project/frontend/src/pages/BoardList.tsx index 732d754..6f2d00b 100644 --- a/Project/frontend/src/pages/BoardList.tsx +++ b/Project/frontend/src/pages/BoardList.tsx @@ -575,13 +575,36 @@ export function BoardList({
{(userLevel >= 9 || userId === '395552') && ( - + <> + + + )}