From 85aaebe134af81a685fa0bfc7905439d8f4722d6 Mon Sep 17 00:00:00 2001 From: accura0117 Date: Mon, 30 Mar 2026 21:35:53 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20React=20=ED=94=84=EB=A1=A0=ED=8A=B8?= =?UTF-8?q?=EC=97=94=EB=93=9C=20=EB=B2=84=EA=B7=B8=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?=EB=B0=8F=20=EB=AA=A8=EB=B0=94=EC=9D=BC=20=EB=B0=98=EC=9D=91?= =?UTF-8?q?=ED=98=95=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - CORS: React 포트(5174) 허용 추가 (application.yml) - ProjectController: /my 엔드포인트 userId 파라미터 선택사항으로 변경 - API 응답 안전 처리: toArray 유틸 추가, 모든 뷰에서 배열 보장 - DataTable emptyMessage: JSX → 문자열로 변경 (PrimeReact 호환) - LoginView: Password → InputText type=password 변경 - 모바일 사이드바: Vue/React 양쪽 inline width 제거로 슬라이드 정상 동작 - 모바일 서브메뉴 텍스트 표시 CSS 수정 Co-Authored-By: Claude Opus 4.6 (1M context) --- .../co/accura/wtm/api/ProjectController.java | 9 +++++-- wtm-api/src/main/resources/application.yml | 2 +- wtm-frontend-react/src/core/api/utils.ts | 14 +++++++++++ .../src/core/components/AppSidebar.scss | 24 +++++++++++++++++++ .../src/core/components/AppSidebar.tsx | 8 ++++--- .../src/core/components/BaseCrudTable.tsx | 7 +----- .../approval/views/ApprovalHistoryView.tsx | 3 ++- .../approval/views/ApprovalPendingView.tsx | 3 ++- .../src/modules/auth/views/LoginView.tsx | 7 ++---- .../modules/dashboard/views/DashboardView.tsx | 14 ++++++----- .../modules/project/views/ProjectListView.tsx | 3 ++- .../src/modules/report/views/ReportView.tsx | 5 ++-- .../src/modules/teal/views/TealListView.tsx | 9 +++---- .../timesheet/views/TimesheetHistoryView.tsx | 3 ++- .../timesheet/views/TimesheetWeekView.tsx | 3 ++- .../src/modules/user/views/UserListView.tsx | 3 ++- .../src/modules/wbs/views/WbsTreeView.tsx | 9 +++---- .../src/core/components/AppSidebar.vue | 6 ++--- 18 files changed, 90 insertions(+), 42 deletions(-) create mode 100644 wtm-frontend-react/src/core/api/utils.ts diff --git a/wtm-api/src/main/java/kr/co/accura/wtm/api/ProjectController.java b/wtm-api/src/main/java/kr/co/accura/wtm/api/ProjectController.java index bf376ec..ebc1a26 100644 --- a/wtm-api/src/main/java/kr/co/accura/wtm/api/ProjectController.java +++ b/wtm-api/src/main/java/kr/co/accura/wtm/api/ProjectController.java @@ -8,6 +8,8 @@ import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import kr.co.accura.wbx.spring.auth.WbxUserDetails; +import org.springframework.security.core.annotation.AuthenticationPrincipal; import jakarta.validation.Valid; import java.util.List; import java.util.Map; @@ -45,8 +47,11 @@ public class ProjectController { } @GetMapping("/my") - public ResponseEntity> myProjects(@RequestParam Long userId) { - return ResponseEntity.ok(projectService.findMyProjects(userId)); + public ResponseEntity> myProjects( + @AuthenticationPrincipal WbxUserDetails user, + @RequestParam(required = false) Long userId) { + Long resolvedUserId = userId != null ? userId : user.getId(); + return ResponseEntity.ok(projectService.findMyProjects(resolvedUserId)); } @GetMapping("/{id}/members") diff --git a/wtm-api/src/main/resources/application.yml b/wtm-api/src/main/resources/application.yml index 1e78b44..077bbfa 100644 --- a/wtm-api/src/main/resources/application.yml +++ b/wtm-api/src/main/resources/application.yml @@ -36,7 +36,7 @@ wbx: admin-ui: enabled: true cors: - allowed-origins: ${CORS_ORIGINS:http://localhost:5173} + allowed-origins: ${CORS_ORIGINS:http://localhost:5173,http://localhost:5174} notification: sse-enabled: true diff --git a/wtm-frontend-react/src/core/api/utils.ts b/wtm-frontend-react/src/core/api/utils.ts new file mode 100644 index 0000000..a18b77d --- /dev/null +++ b/wtm-frontend-react/src/core/api/utils.ts @@ -0,0 +1,14 @@ +/** + * API 응답에서 안전하게 배열을 추출합니다. + * - 응답이 배열이면 그대로 반환 + * - 응답이 { items: [...] } 형태면 items 반환 + * - 그 외 빈 배열 반환 + */ +export function toArray(data: unknown): T[] { + if (Array.isArray(data)) return data as T[]; + if (data && typeof data === 'object' && 'items' in data) { + const items = (data as Record).items; + if (Array.isArray(items)) return items as T[]; + } + return []; +} diff --git a/wtm-frontend-react/src/core/components/AppSidebar.scss b/wtm-frontend-react/src/core/components/AppSidebar.scss index 1810f72..ea7ec25 100644 --- a/wtm-frontend-react/src/core/components/AppSidebar.scss +++ b/wtm-frontend-react/src/core/components/AppSidebar.scss @@ -59,4 +59,28 @@ border: none; background: transparent; } + + &__menu .p-panelmenu-panel { + .p-panelmenu-header-content, + .p-menuitem-content { + white-space: nowrap; + overflow: visible; + } + + .p-panelmenu-content { + overflow: visible; + } + + .p-menuitem-text { + display: inline !important; + } + } + + // 모바일에서 서브메뉴 텍스트 표시 보장 + &--mobile &__nav { + .p-menuitem-text, + .p-panelmenu-header-content span { + display: inline !important; + } + } } diff --git a/wtm-frontend-react/src/core/components/AppSidebar.tsx b/wtm-frontend-react/src/core/components/AppSidebar.tsx index e14a375..f0ad6d9 100644 --- a/wtm-frontend-react/src/core/components/AppSidebar.tsx +++ b/wtm-frontend-react/src/core/components/AppSidebar.tsx @@ -34,8 +34,6 @@ export default function AppSidebar({ visible, collapsed, mobile }: Props) { })); }, [currentUser, navigate]); - const sidebarWidth = collapsed ? `${LAYOUT.sidebarCollapsedWidth}px` : `${LAYOUT.sidebarWidth}px`; - const classNames = [ 'app-sidebar', visible && 'app-sidebar--visible', @@ -45,8 +43,12 @@ export default function AppSidebar({ visible, collapsed, mobile }: Props) { .filter(Boolean) .join(' '); + const style = mobile + ? undefined + : { width: collapsed ? `${LAYOUT.sidebarCollapsedWidth}px` : `${LAYOUT.sidebarWidth}px` }; + return ( -