Skip to content

[Refactor/#151] 워크스페이스 UI/UX 개선 - #152

Merged
Seojegyeong merged 8 commits into
developfrom
refactor/#151
Mar 31, 2026
Merged

[Refactor/#151] 워크스페이스 UI/UX 개선#152
Seojegyeong merged 8 commits into
developfrom
refactor/#151

Conversation

@Seojegyeong

@Seojegyeong Seojegyeong commented Mar 31, 2026

Copy link
Copy Markdown
Collaborator

🚨 관련 이슈

#151

✨ 변경사항

  • 🐞 BugFix Something isn't working
  • 💻 CrossBrowsing Browser compatibility
  • 🌏 Deploy Deploy
  • 🎨 Design Markup & styling
  • 📃 Docs Documentation writing and editing (README.md, etc.)
  • ✨ Feature Feature
  • 🔨 Refactor Code refactoring
  • ⚙️ Setting Development environment setup
  • ✅ Test Test related (storybook, jest, etc.)

✏️ 작업 내용

  1. 워크스페이스 목록 dropdown 제거 및 호버 인터랙션 개선
  2. 멤버 관리 이동 버튼 제거 -> 상단 네비게이션으로 대체
  3. workspace 레이아웃 추가 및 불필요한 pageheader, 뒤로 가기 버튼 제거(바로 옆에네비게이션 바로 이동 가능하기 때문에 뒤로 가기 제거했습니다!)
  4. 스켈레톤 UI 임시 적용 - 추후 완성해주세요!

스크린샷

CaptureX_2026-03-31_183433_localhost
CaptureX_2026-03-31_183513_localhost
CaptureX_2026-03-31_183525_localhost

😅 미완성 작업

스켈레톤 UI 임시 적용했으니 로딩 중 상태 최적화 부탁드립니다!

📢 논의 사항 및 참고 사항

  • 인라인 아이콘(<- 등) 대신 SVG 아이콘 사용해주세요..!!
  • SVG 아이콘 색상 동적 적용 시 currentColor로 변경 필요 (Reopen editor with 옵션 활용)

💬 리뷰어 가이드 (P-Rules)
P1: 필수 반영 (Critical) - 버그 가능성, 컨벤션 위반. 해결 전 머지 불가.
P2: 적극 권장 (Recommended) - 더 나은 대안 제시. 가급적 반영 권장.
P3: 제안 (Suggestion) - 아이디어 공유. 반영 여부는 드라이버 자율.
P4: 단순 확인/칭찬 (Nit) - 사소한 오타, 칭찬 등 피드백.

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • 워크스페이스 설정 및 멤버 관리 페이지에 탭 기반 네비게이션 추가
  • UI 개선

    • 버튼의 호버 및 비활성화 상태 스타일 개선
    • 드롭다운 메뉴의 시각적 디자인 및 레이아웃 개선
    • 워크스페이스 카드 인터페이스 업데이트
    • 워크스페이스 목록 로딩 상태 UI 개선
    • 워크스페이스 생성 모달 디자인 최적화
  • 기타

    • 전반적인 네비게이션 구조 개선

@Seojegyeong
Seojegyeong requested review from YermIm and jjjsun March 31, 2026 09:44
@Seojegyeong Seojegyeong self-assigned this Mar 31, 2026
@Seojegyeong Seojegyeong added the 🔨 Refactor 코드 리팩토링 label Mar 31, 2026
@Seojegyeong Seojegyeong linked an issue Mar 31, 2026 that may be closed by this pull request
1 task
@coderabbitai

coderabbitai Bot commented Mar 31, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

작업 공간 UI를 재구성하는 변경사항으로, 드롭다운 메뉴 기능을 제거하고 라우팅 구조를 부모-자식 관계로 개선하며, 로딩 상태 UI를 스켈레톤 리스트로 변경하고 버튼 및 드롭다운 메뉴의 스타일을 업데이트했습니다.

Changes

Cohort / File(s) Summary
Button 및 DropdownMenu 스타일 업데이트
src/components/common/button/Button.tsx, src/components/common/dropdownmenu/DropdownMenu.tsx
버튼 variant의 호버 상태를 투명도 기반에서 구체적인 색상으로 변경하고, 드롭다운 메뉴 컨테이너에 테두리와 애니메이션을 추가. 메뉴 항목 레이아웃을 justify-between으로 변경하고 활성/비활성 색상 스킴을 투명한 배경으로 개선했습니다.
WorkspaceCard 기능 제거 및 구조 변경
src/components/workspace/WorkspaceCard.tsx, src/components/workspace/MemberRoleSelect.tsx
WorkspaceCard에서 드롭다운 메뉴 기능 완전 제거 및 버튼 기반 구조로 리팩토링. menuItems prop 삭제 및 접근성 개선. MemberRoleSelect의 불필요한 transition-transform 클래스 제거.
로딩 상태 UI 개선
src/components/workspace/WorkspaceListLoading.tsx
단순 텍스트 로딩 메시지에서 스켈레톤 카드 리스트로 변경. 새로운 WorkspaceCardSkeleton 헬퍼 컴포넌트 추가로 접근성 및 사용자 경험 개선.
라우팅 구조 및 레이아웃 개선
src/routes/MainRoutes.tsx, src/layout/workspace/WorkspaceManageLayout.tsx
작업 공간 라우팅을 부모-자식 계층 구조로 재구성. 새로운 WorkspaceManageLayout 컴포넌트로 탭 기반 네비게이션 중앙화. 레이지 로딩 및 폴백 처리 추가.
페이지 네비게이션 UI 정리
src/pages/workspace/Workspace.tsx, src/pages/workspace/WorkspaceSetting.tsx, src/pages/workspace/MemberManagement.tsx
페이지 헤더 및 뒤로가기 버튼 제거로 중복 네비게이션 제거. 작업 공간 생성 모달 UI 개선 (크기 조정, 로고 업로드 인터페이스 재설계).
유틸리티 함수 업데이트
src/utils/loadable.tsx
loadable 함수의 기본 폴백 값을 <div>Loading...</div>에서 null로 변경. 명시적 폴백을 제공하는 호출자가 제어권을 가지도록 조정.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

Possibly related PRs

Suggested labels

🎨 Html&css, 🐞 BugFix

Suggested reviewers

  • jjjsun
  • YermIm

주요 검토 포인트

1. 라우팅 구조 변경의 영향 범위 확인

라우팅을 부모-자식 계층으로 개선한 점은 좋으나, WorkspaceManageLayout 도입으로 인한 기존 네비게이션 링크들이 모두 올바르게 업데이트되었는지 확인이 필요합니다. 특히 외부에서 /workspace/{id}/settings 또는 /workspace/{id}/members로 직접 진입하는 경우가 있다면 라우팅이 정상 작동하는지 검증해야 합니다.

2. WorkspaceCard props 제거의 종속성

menuItems prop 완전 제거로 인해 이 컴포넌트를 사용하는 다른 곳에서 런타임 에러가 발생할 수 있습니다. Workspace.tsx와 다른 파일들에서 모두 깔끔하게 제거되었는지 전체 코드베이스 검색으로 확인이 권장됩니다.

3. 접근성 개선 검증

  • WorkspaceListLoading에서 aria-live 제거 및 aria-label 추가: 스크린 리더 사용자 경험이 개선되었는지 검증 필요
  • WorkspaceCard 버튼화: 시맨틱하게 적절한지, 포커스 관리가 제대로 되는지 확인

4. 드롭다운 메뉴 스타일 일관성

DropdownMenu의 색상 변경(brand-300 → status-blue/10 및 /5)이 디자인 시스템과 일치하는지, 다른 드롭다운 사용 사례에서도 동일하게 적용되어야 하는지 검토하세요.

5. 모달 UI 변경의 반응형 동작

Workspace.tsx의 생성 모달을 xl에서 md로 축소했는데, 로고 업로드 미리보기와 폼 필드들이 작은 화면에서 적절히 표시되는지 확인이 필요합니다.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed 제목은 PR의 주요 변경사항인 워크스페이스 UI/UX 개선을 명확하게 요약하고 있으며, 이슈 번호 #151과 함께 작업 유형을 잘 표현하고 있습니다.
Description check ✅ Passed PR 설명은 관련 이슈, 변경사항 체크리스트, 상세한 작업 내용, 스크린샷, 미완성 작업, 그리고 리뷰 가이드를 포함하여 템플릿 요구사항을 충족하고 있습니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/#151

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

Copy link
Copy Markdown

📚 Storybook 배포 완료

항목 링크
📖 Storybook https://69a147b60a56365d9e2185ef-filolqfdnk.chromatic.com/
🔍 Chromatic https://www.chromatic.com/build?appId=69a147b60a56365d9e2185ef&number=176

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/pages/workspace/Workspace.tsx (1)

97-104: ⚠️ Potential issue | 🟡 Minor

setLogoPreview(null) 중복 호출 제거

onCloseCreate에서 setLogoPreview(null)이 두 번 호출되고 있습니다. 첫 번째 호출(Line 98)은 불필요하며, 두 번째 콜백(Lines 99-102)만 있으면 됩니다.

🐛 수정 제안
 const onCloseCreate = () => {
-  setLogoPreview(null);
   setLogoPreview((prev) => {
     if (prev) URL.revokeObjectURL(prev);
     return null;
   });
   setCreateOpen(false);
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/workspace/Workspace.tsx` around lines 97 - 104, In onCloseCreate,
remove the redundant direct call setLogoPreview(null) and keep only the
functional updater that revokes the previous object URL and returns null (the
existing setLogoPreview(prev => { if (prev) URL.revokeObjectURL(prev); return
null; })); update the onCloseCreate function to call setCreateOpen(false) after
that single setLogoPreview call so logo cleanup and closing behavior remain
correct.
src/components/common/dropdownmenu/DropdownMenu.tsx (1)

39-55: ⚠️ Potential issue | 🟡 Minor

키보드 접근성을 위해 tabIndex 추가 필요

role="button"이 설정되어 있고 키보드 이벤트도 처리하고 있지만, tabIndex={0}이 없으면 키보드로 포커스할 수 없습니다.

🔧 수정 제안
       <div
         role="button"
+        tabIndex={0}
         aria-haspopup="menu"
         aria-expanded={open}
         aria-controls={menuId}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/common/dropdownmenu/DropdownMenu.tsx` around lines 39 - 55,
The div in DropdownMenu.tsx that renders the interactive trigger (role="button",
aria-haspopup, aria-expanded using open, aria-controls={menuId},
aria-label={ariaLabel}, onClick calling setOpen and onKeyDown handling
Enter/Space) is not focusable by keyboard; add tabIndex={0} to that element so
it can receive focus and keyboard events (keep existing handlers and props like
className/twMerge and the trigger rendering intact). Ensure tabIndex is applied
on the same element that calls setOpen and uses trigger(open).
🧹 Nitpick comments (7)
src/components/common/button/Button.tsx (1)

39-39: 하드코딩된 hover 색상은 토큰 기반으로 맞추는 걸 권장해요.

Line 39, Line 45의 hover:bg-[#...]는 테마 확장/일괄 변경 시 추적이 어려워집니다. 현재 토큰 클래스로 통일하거나, 필요한 hover 색을 @theme 토큰으로 추가해서 참조하는 방식이 유지보수에 더 안전합니다.

예시 diff
-    primary:
-      "bg-chart-3 text-white hover:bg-[`#0e6add`] disabled:bg-bg-disabled disabled:text-text-disabled disabled:hover:bg-bg-disabled",
+    primary:
+      "bg-chart-3 text-white hover:bg-chart-2 disabled:bg-bg-disabled disabled:text-text-disabled disabled:hover:bg-bg-disabled",

-    danger:
-      "bg-status-red text-white hover:bg-[`#d91632`] disabled:bg-bg-disabled disabled:text-text-disabled disabled:hover:bg-bg-disabled",
+    danger:
+      "bg-status-red text-white hover:bg-status-red/90 disabled:bg-bg-disabled disabled:text-text-disabled disabled:hover:bg-bg-disabled",

Also applies to: 45-45

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/common/button/Button.tsx` at line 39, The hover:bg hex values
in the Button component's class list (the string containing "bg-chart-3
text-white hover:bg-[`#0e6add`] ...") are hardcoded—replace them with
theme/token-based classes or CSS variables: add an appropriate hover token to
your theme (e.g., chart-3-hover or --color-chart-3-hover) and update the
Button.tsx className(s) to use that token (e.g., hover:bg-{token} or
hover:bg-[var(--color-chart-3-hover)]), doing the same for the other occurrence
referenced in the diff so hover colors are maintainable and theme-driven.
src/components/workspace/MemberRoleSelect.tsx (2)

52-52: transition-transform 제거에 따른 UX 영향 확인 필요

transition-transform 클래스를 제거하면서 chevron 아이콘의 회전이 즉각적으로 발생하게 됩니다. 일반적으로 드롭다운 UI에서는 부드러운 전환 애니메이션이 사용자 경험 향상에 도움이 됩니다.

PR 설명에서 "hover 인터랙션 개선"을 언급하셨는데, 이 변경이 의도적인 것인지 확인이 필요합니다. 만약 의도하지 않은 제거였다면 다시 추가하는 것을 권장합니다.

🔄 transition 복원 제안
          <ChevonIcon
-            className={`h-4 w-4 shrink-0 ${open ? "rotate-0" : "-rotate-180"}`}
+            className={`h-4 w-4 shrink-0 transition-transform ${open ? "rotate-0" : "-rotate-180"}`}
          />

또는 duration을 조정하여 더 빠른 전환을 원하는 경우:

          <ChevonIcon
-            className={`h-4 w-4 shrink-0 ${open ? "rotate-0" : "-rotate-180"}`}
+            className={`h-4 w-4 shrink-0 transition-transform duration-150 ${open ? "rotate-0" : "-rotate-180"}`}
          />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/workspace/MemberRoleSelect.tsx` at line 52, The chevron in
MemberRoleSelect now rotates instantly because the tailwind transition class was
removed; restore a smooth rotation by adding the appropriate transition utility
back to the element (e.g., include "transition-transform" and optionally a
duration like "duration-150" or "duration-200") in the className that currently
reads `className={`h-4 w-4 shrink-0 ${open ? "rotate-0" : "-rotate-180"}`}` so
the rotate classes animate smoothly; adjust duration if you want a faster/slower
effect.

43-43: key prop을 통한 강제 리마운트 패턴 검토

disabled 상태 변경 시 컴포넌트를 강제로 리마운트하는 패턴이 적용되어 있습니다. 이는 드롭다운 내부 상태를 완전히 초기화하기 위한 의도로 보이나, 이미 disabled prop과 조건부 items 배열로 충분히 제어되고 있어 불필요한 성능 비용이 발생할 수 있습니다.

드롭다운 내부에서 특별히 초기화가 필요한 상태가 없다면, key prop을 제거하고 일반적인 prop 전달 방식으로 변경하는 것을 고려해 보세요.

♻️ 제안하는 리팩토링
    <DropdownMenu
-      key={disabled ? "disabled" : "enabled"}
      items={items}
      className={disabled ? "pointer-events-none opacity-50" : undefined}
      trigger={(open) => (

만약 드롭다운이 열린 상태에서 disabled로 전환될 때 닫히도록 하는 것이 목적이라면, DropdownMenu 컴포넌트 내부에서 disabled prop을 받아 처리하는 것이 더 명확한 방법입니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/workspace/MemberRoleSelect.tsx` at line 43, The component
MemberRoleSelect currently forces a remount by using key={disabled ? "disabled"
: "enabled"}; remove this key usage and rely on the disabled prop and the
conditional items array to control the DropdownMenu instead; if you need the
dropdown to close when disabled becomes true, implement that inside DropdownMenu
(e.g., have DropdownMenu accept a disabled prop and close on prop change) rather
than remounting MemberRoleSelect.
src/pages/workspace/Workspace.tsx (1)

134-137: async 함수에서 await 미사용

onSubmitCreateasync로 선언되어 있지만 mutate()를 await하지 않아 async 키워드가 불필요합니다. 현재 로직상 mutation 완료를 기다릴 필요가 없다면 async를 제거하고, 완료 후 처리가 필요하다면 mutateAsync를 사용하세요.

♻️ 옵션 1: async 제거
-const onSubmitCreate = async () => {
+const onSubmitCreate = () => {
   if (!newName.trim()) return;
   createWorkspaceMutation.mutate();
 };
♻️ 옵션 2: mutateAsync 사용 (완료 후 처리 필요 시)
 const onSubmitCreate = async () => {
   if (!newName.trim()) return;
-  createWorkspaceMutation.mutate();
+  await createWorkspaceMutation.mutateAsync();
+  // 완료 후 추가 처리
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/workspace/Workspace.tsx` around lines 134 - 137, The onSubmitCreate
function is declared async but does not await the mutation; either remove the
unnecessary async from onSubmitCreate or, if you need to wait for the mutation
to complete, call createWorkspaceMutation.mutateAsync() and await it (e.g.,
await createWorkspaceMutation.mutateAsync(...)) and handle results/errors
accordingly; update any post-mutation logic to run after the await when choosing
mutateAsync.
src/components/workspace/WorkspaceListLoading.tsx (1)

3-19: 불필요한 Fragment 제거 가능

WorkspaceCardSkeleton<><li>...</li></>에서 Fragment(<>...</>)는 불필요합니다. <li> 단일 요소만 반환하므로 Fragment 없이 직접 반환해도 됩니다.

♻️ Fragment 제거
 function WorkspaceCardSkeleton() {
   return (
-    <>
-      <li className="flex items-center justify-between rounded-component-md bg-white px-6 py-5 shadow-Soft border border-gray-100 tablet:px-4 tablet:py-4">
-        ...
-      </li>
-    </>
+    <li className="flex items-center justify-between rounded-component-md bg-white px-6 py-5 shadow-Soft border border-gray-100 tablet:px-4 tablet:py-4">
+      ...
+    </li>
   );
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/workspace/WorkspaceListLoading.tsx` around lines 3 - 19, The
WorkspaceCardSkeleton component currently wraps its single <li> element in a
React Fragment; remove the unnecessary fragment so WorkspaceCardSkeleton returns
the <li> directly (locate the WorkspaceCardSkeleton function and the surrounding
<>...</> and delete the fragment wrappers).
src/pages/workspace/MemberManagement.tsx (1)

231-245: 에러 타입 처리 개선 고려

Line 234의 as unknown as IApiErrorResponse 타입 단언은 타입 안전성을 우회합니다. useInfiniteQuery의 에러 타입을 제네릭으로 명시하면 이 단언을 제거할 수 있습니다.

♻️ 타입 단언 제거 제안
  const membersQuery = useInfiniteQuery({
+   // 에러 타입을 명시적으로 지정
    queryKey: ["workspaceMembers", orgId, PAGE_SIZE],
    ...
  });

  // 또는 에러 메시지 접근 시 옵셔널 체이닝 활용
  const errorMessage =
    memberCountQuery.error?.message ||
-   (membersQuery.error as unknown as IApiErrorResponse)?.message ||
+   membersQuery.error?.message ||
    "팀 구성원 정보를 불러오지 못했습니다";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/workspace/MemberManagement.tsx` around lines 231 - 245, The error
handling currently uses a forced cast (as unknown as IApiErrorResponse) for
membersQuery.error; update the useInfiniteQuery / useQuery calls that create
membersQuery and memberCountQuery to provide the correct generic error type
(IApiErrorResponse) so their .error properties are typed properly, then remove
the cast and use membersQuery.error?.message and memberCountQuery.error?.message
directly; look for the hooks that create membersQuery and memberCountQuery and
add the generic parameter(s) to align their error types with IApiErrorResponse.
src/layout/workspace/WorkspaceManageLayout.tsx (1)

10-12: workspaceId undefined 처리 검토

useParams()에서 반환되는 workspaceIdstring | undefined 타입입니다. 현재 라우트 구성상 이 레이아웃은 항상 workspace/:workspaceId 경로에서만 렌더링되므로 실제로 undefined가 될 가능성은 낮지만, 타입 안전성을 위해 명시적 처리를 고려해 볼 수 있습니다.

♻️ 타입 안전성 개선 (선택사항)
 export default function WorkspaceManageLayout() {
-  const { workspaceId } = useParams();
+  const { workspaceId } = useParams<{ workspaceId: string }>();
+
+  if (!workspaceId) {
+    return null; // 또는 에러 UI
+  }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/layout/workspace/WorkspaceManageLayout.tsx` around lines 10 - 12, The
workspaceId from useParams() in WorkspaceManageLayout is typed as string |
undefined; make it explicit by checking and handling the undefined case instead
of assuming it's present: retrieve workspaceId via useParams(), then either (a)
guard early (if (!workspaceId) return a NotFound/Redirect/throw new Error with a
clear message) or (b) assert non-null with a deliberate cast/comment (e.g.,
const workspaceId = params.workspaceId! ) if you want to guarantee presence, and
adjust downstream usages to accept a string; update the WorkspaceManageLayout to
perform this explicit check or assertion so type-safety is enforced.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/utils/loadable.tsx`:
- Around line 5-8: The loadable utility currently defaults fallback to null
causing MainRoutes components to render a blank screen; update the loadable<T
extends ComponentType<any>>(Component: T, fallback: ReactNode = null) function
to use a sensible default fallback (e.g., a shared GenericSkeleton or
LoadingPlaceholder) instead of null, and then for the specific MainRoutes
components (OverviewDashboard, PlatformDashboard, Timeline, AdsListPage,
CampaignDetail, CampaignGroup, WorkspaceManageLayout, WorkspaceSetting,
MemberManagement, Billing) pass their corresponding more specific skeleton
components as the fallback when calling loadable so they show an appropriate
skeleton on slow networks while AuthRoutes can keep their own skeleton
fallbacks. Ensure you reference the loadable function signature and the listed
component names when making the changes.

---

Outside diff comments:
In `@src/components/common/dropdownmenu/DropdownMenu.tsx`:
- Around line 39-55: The div in DropdownMenu.tsx that renders the interactive
trigger (role="button", aria-haspopup, aria-expanded using open,
aria-controls={menuId}, aria-label={ariaLabel}, onClick calling setOpen and
onKeyDown handling Enter/Space) is not focusable by keyboard; add tabIndex={0}
to that element so it can receive focus and keyboard events (keep existing
handlers and props like className/twMerge and the trigger rendering intact).
Ensure tabIndex is applied on the same element that calls setOpen and uses
trigger(open).

In `@src/pages/workspace/Workspace.tsx`:
- Around line 97-104: In onCloseCreate, remove the redundant direct call
setLogoPreview(null) and keep only the functional updater that revokes the
previous object URL and returns null (the existing setLogoPreview(prev => { if
(prev) URL.revokeObjectURL(prev); return null; })); update the onCloseCreate
function to call setCreateOpen(false) after that single setLogoPreview call so
logo cleanup and closing behavior remain correct.

---

Nitpick comments:
In `@src/components/common/button/Button.tsx`:
- Line 39: The hover:bg hex values in the Button component's class list (the
string containing "bg-chart-3 text-white hover:bg-[`#0e6add`] ...") are
hardcoded—replace them with theme/token-based classes or CSS variables: add an
appropriate hover token to your theme (e.g., chart-3-hover or
--color-chart-3-hover) and update the Button.tsx className(s) to use that token
(e.g., hover:bg-{token} or hover:bg-[var(--color-chart-3-hover)]), doing the
same for the other occurrence referenced in the diff so hover colors are
maintainable and theme-driven.

In `@src/components/workspace/MemberRoleSelect.tsx`:
- Line 52: The chevron in MemberRoleSelect now rotates instantly because the
tailwind transition class was removed; restore a smooth rotation by adding the
appropriate transition utility back to the element (e.g., include
"transition-transform" and optionally a duration like "duration-150" or
"duration-200") in the className that currently reads `className={`h-4 w-4
shrink-0 ${open ? "rotate-0" : "-rotate-180"}`}` so the rotate classes animate
smoothly; adjust duration if you want a faster/slower effect.
- Line 43: The component MemberRoleSelect currently forces a remount by using
key={disabled ? "disabled" : "enabled"}; remove this key usage and rely on the
disabled prop and the conditional items array to control the DropdownMenu
instead; if you need the dropdown to close when disabled becomes true, implement
that inside DropdownMenu (e.g., have DropdownMenu accept a disabled prop and
close on prop change) rather than remounting MemberRoleSelect.

In `@src/components/workspace/WorkspaceListLoading.tsx`:
- Around line 3-19: The WorkspaceCardSkeleton component currently wraps its
single <li> element in a React Fragment; remove the unnecessary fragment so
WorkspaceCardSkeleton returns the <li> directly (locate the
WorkspaceCardSkeleton function and the surrounding <>...</> and delete the
fragment wrappers).

In `@src/layout/workspace/WorkspaceManageLayout.tsx`:
- Around line 10-12: The workspaceId from useParams() in WorkspaceManageLayout
is typed as string | undefined; make it explicit by checking and handling the
undefined case instead of assuming it's present: retrieve workspaceId via
useParams(), then either (a) guard early (if (!workspaceId) return a
NotFound/Redirect/throw new Error with a clear message) or (b) assert non-null
with a deliberate cast/comment (e.g., const workspaceId = params.workspaceId! )
if you want to guarantee presence, and adjust downstream usages to accept a
string; update the WorkspaceManageLayout to perform this explicit check or
assertion so type-safety is enforced.

In `@src/pages/workspace/MemberManagement.tsx`:
- Around line 231-245: The error handling currently uses a forced cast (as
unknown as IApiErrorResponse) for membersQuery.error; update the
useInfiniteQuery / useQuery calls that create membersQuery and memberCountQuery
to provide the correct generic error type (IApiErrorResponse) so their .error
properties are typed properly, then remove the cast and use
membersQuery.error?.message and memberCountQuery.error?.message directly; look
for the hooks that create membersQuery and memberCountQuery and add the generic
parameter(s) to align their error types with IApiErrorResponse.

In `@src/pages/workspace/Workspace.tsx`:
- Around line 134-137: The onSubmitCreate function is declared async but does
not await the mutation; either remove the unnecessary async from onSubmitCreate
or, if you need to wait for the mutation to complete, call
createWorkspaceMutation.mutateAsync() and await it (e.g., await
createWorkspaceMutation.mutateAsync(...)) and handle results/errors accordingly;
update any post-mutation logic to run after the await when choosing mutateAsync.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e07e9575-cdf5-4d7e-b5ef-3c8f996e34a5

📥 Commits

Reviewing files that changed from the base of the PR and between bca2f66 and d28e0a0.

⛔ Files ignored due to path filters (1)
  • src/assets/icon/common/uploadImg.svg is excluded by !**/*.svg and included by src/**
📒 Files selected for processing (11)
  • src/components/common/button/Button.tsx
  • src/components/common/dropdownmenu/DropdownMenu.tsx
  • src/components/workspace/MemberRoleSelect.tsx
  • src/components/workspace/WorkspaceCard.tsx
  • src/components/workspace/WorkspaceListLoading.tsx
  • src/layout/workspace/WorkspaceManageLayout.tsx
  • src/pages/workspace/MemberManagement.tsx
  • src/pages/workspace/Workspace.tsx
  • src/pages/workspace/WorkspaceSetting.tsx
  • src/routes/MainRoutes.tsx
  • src/utils/loadable.tsx
💤 Files with no reviewable changes (1)
  • src/pages/workspace/WorkspaceSetting.tsx

Comment thread src/utils/loadable.tsx
@YermIm

YermIm commented Mar 31, 2026

Copy link
Copy Markdown
Collaborator

P4: 상단 탭 좋은 것 같습니다! 수고하셨습니다 :)

@jjjsun jjjsun left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P4: 확인했습니다!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🔨 Refactor 코드 리팩토링

Projects

None yet

Development

Successfully merging this pull request may close these issues.

♻️ [Refactor] 워크스페이스 목록 디자인 수정 및 개선

3 participants