[Feature/#91] 멤버 관리 페이지 UI 구현 - #97
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthrough워크스페이스 멤버 관리 UI를 추가합니다. 토글 컴포넌트, 멤버 리스트/아이템/역할 선택, 권한 테이블, 멤버 관리 페이지 및 라우트( Changes
Sequence Diagram(s)(생성 조건에 부합하지 않아 생략) Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
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. Comment |
📚 Storybook 배포 완료
|
There was a problem hiding this comment.
Actionable comments posted: 8
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/types/workspace/workspace.ts (1)
41-44:⚠️ Potential issue | 🔴 Critical
TUploadImageResponse타입 누락으로 인한 컴파일 오류 발생
src/types/workspace/workspace.ts에서TUploadImageResponse타입이 제거되었으나,src/api/workspace/org.ts에서 여전히 이 타입을 import하여 사용 중입니다. 이로 인해 TypeScript 컴파일 오류가 발생합니다.
uploadImage함수에서 반환 값이data.data.url이므로, 다음과 같이 타입을 복원해야 합니다:export type TUploadImageResponse = { url: string; };이 타입 정의를
TApiResult타입 정의 다음에 추가하여 org.ts의 import 오류를 해결하세요.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/types/workspace/workspace.ts` around lines 41 - 44, Restore the missing TUploadImageResponse type in the workspace types file: after the existing TApiResult declaration add a new exported type named TUploadImageResponse matching the shape used by uploadImage (an object with a url string) so imports in src/api/workspace/org.ts resolve; ensure the exported name exactly matches TUploadImageResponse and that uploadImage's return type aligns with data.data.url.
🧹 Nitpick comments (2)
src/pages/workspace/MemberManagement.tsx (1)
7-18: 라우트 파라미터workspaceId를 페이지에서 소비하지 않고 있습니다.경로가
workspace/:workspaceId/members인데 현재 페이지/하위 컴포넌트가 해당 값을 사용하지 않아, API 연동 시 조직 스코프 불일치가 생길 수 있습니다. 최소한 페이지에서workspaceId를 읽고 하위 컴포넌트로 전달하는 구조로 잡아두는 편이 안전합니다.
As per coding guidelines,src/**: 1. 상태 관리: 서버 상태(React Query)와 전역 상태(Zustand)의 분리 여부 확인.및2. 구조와 책임 분리: 페이지에 비즈니스 로직이 과도하지 않은지 확인.🤖 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 7 - 18, The page component MemberManagement currently ignores the route param workspaceId; update MemberManagement to read workspaceId (e.g., via useParams) and pass it down as a prop to child components (MemberList and PermissionTable) so they can scope API calls to the correct workspace; ensure you only read the param in the page (no heavy business logic here) and forward workspaceId (e.g., workspaceId={workspaceId}) to both MemberList and PermissionTable so downstream components use that ID for their React Query requests or Zustand actions.src/types/workspace/workspace.ts (1)
73-73:TPermissionValue에 한글 문자열 리터럴 사용 검토
TPermissionValue가"가능" | "불가능"한글 문자열을 사용하고 있습니다. 일반적으로 API 응답은boolean또는 영문 문자열("ALLOWED" | "DENIED")을 사용하는 경우가 많습니다.현재 API 명세와 일치하는지 확인해 주세요. 만약 UI 표시용 데이터라면 괜찮지만, API 응답 타입이라면 실제 응답 형식에 맞춰 수정이 필요할 수 있습니다. 다국어 지원을 고려한다면 표시 문자열은 별도로 관리하는 것이 좋습니다.
♻️ boolean 사용 제안 (API 응답이 boolean인 경우)
-export type TPermissionValue = "가능" | "불가능"; +export type TPermissionValue = boolean; export type TPermissionRow = { key: | "campaignView" | "billingManage" | "workspaceView" | "memberInvite" | "memberRoleEdit" | "workspaceEdit" | "projectDelete"; label: string; description: string; admin: TPermissionValue; member: TPermissionValue; };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/types/workspace/workspace.ts` at line 73, TPermissionValue currently uses Korean string literals ("가능" | "불가능"); verify whether this type models API responses or UI display values and align it accordingly: if the API returns booleans, change TPermissionValue to boolean; if the API returns English enums, change it to a string union like "ALLOWED" | "DENIED"; if these are only UI labels, keep the current type but move mapping logic into the UI layer (e.g., create a function that maps API boolean/enum to the Korean display strings) and update all usages of TPermissionValue to use the appropriate representation (refer to the TPermissionValue type declaration to locate and update usages).
🤖 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/components/common/toggle/Toggle.tsx`:
- Around line 23-25: The switch element with role="switch" is missing
aria-checked, so assistive tech cannot determine on/off state; update the Toggle
component to add aria-checked that reflects the component's internal state/prop
(e.g., aria-checked={!!checked} or aria-checked={isOn}) alongside role="switch",
ensuring the value is a boolean and kept in sync with the same state used for
visual rendering (look for the Toggle component and the JSX element that
currently has role="switch", aria-label, disabled and add aria-checked
referencing the existing checked/isOn state).
In `@src/components/workspace/MemberItem.tsx`:
- Around line 37-41: The onChange in MemberItem currently only logs the new role
and doesn't update state; update MemberList to hold members via useState and
pass a role-change handler down to MemberItem: add an onChange prop to
MemberItem's Props type (onChange: (newRole: string) => void), have MemberItem
call that prop from its MemberRoleSelect onChange, and implement the handler in
MemberList to update the specific member's role in the useState-managed members
array (by id/index) so the UI reflects the change.
In `@src/components/workspace/MemberList.tsx`:
- Line 51: The invite button in the MemberList component currently uses
aria-label="업로드", which mismatches its function; update the invite button
element in MemberList (the button or IconButton used for inviting members) to
use an accurate ARIA label like aria-label="팀원 초대" or the component's i18n key
(e.g., aria-label={t('invite_member')}) so screen readers convey the correct
action, and ensure any tooltip/title text is consistent with the new ARIA label.
- Around line 44-45: The text "현재 4명의 구성원이 활동 중입니다" is hardcoded; update the
MemberList component to compute and render the member count from the data source
instead — replace the literal "4명" with the length of the members array
(mockMembers.length or the prop/state variable used in MemberList) so the
displayed count reflects actual data; ensure you reference the same variable the
component uses to render member items (mockMembers) and keep the surrounding
string formatting intact.
In `@src/components/workspace/MemberRoleSelect.tsx`:
- Around line 60-71: The trigger button in MemberRoleSelect lacks ARIA
attributes for dropdown state; update the button element in the MemberRoleSelect
component (the button using setIsOpen and rendering ChevronIcon) to include
aria-haspopup="listbox" and aria-expanded={isOpen} (ensure it resolves to
"false" when disabled), so assistive tech can announce the popup role and
current open/closed state; keep existing disabled handling and class logic but
add these attributes bound to the component's isOpen state.
- Around line 74-85: The dropdown keeps option buttons in the DOM when closed,
allowing keyboard focus; update MemberRoleSelect so option elements are only
interactive when visible by either conditionally rendering the restOptions map
(render it only when isOpen && !disabled) or by setting each option button to
tabIndex={isOpen && !disabled ? 0 : -1} and adding aria-hidden={!(isOpen &&
!disabled)}; target the restOptions map rendering and the button elements (keyed
by option, using handleSelect and roleLabelMap) and ensure isOpen/disabled
gating is applied consistently.
In `@src/components/workspace/PermissionTable.tsx`:
- Around line 126-129: 해당 관리자인지 표시하는 셀(내부에 AdminCheckBadge 렌더링)은 아이콘만으로는 스크린리더에
의미가 없으므로 AdminCheckBadge가 렌더되는 td 내부(현재 <td className="px-6 py-5 text-center">
안)의 적절한 위치에 시각적으로 숨겨진 텍스트(span 등)에 sr-only 클래스를 추가하여 접근성 텍스트(예: "가능")를 함께 렌더하도록
수정하세요; 즉 AdminCheckBadge 컴포넌트 호출 옆이나 내부에 <span className="sr-only">가능</span> 형태의
보이지 않는 텍스트를 삽입해 스크린리더가 아이콘의 의미를 읽을 수 있게 만드세요.
In `@src/pages/workspace/MemberManagement.tsx`:
- Line 23: The ownership-transfer button currently calls a plain alert via the
onButtonClick prop; replace that with a real handler or disable UI: wire
onButtonClick to a modal-opening function (e.g., openOwnershipTransferModal)
that triggers your ownership confirmation modal component (create
OwnershipTransferModal skeleton and pass necessary props/callbacks like
onConfirm/onCancel), and ensure API errors are handled with user feedback in the
onConfirm flow; if the feature is not ready, instead set the button to disabled
and show a concise helper message or tooltip explaining it's coming soon (update
the same component where onButtonClick is defined, referencing the onButtonClick
prop and the ownership transfer button element).
---
Outside diff comments:
In `@src/types/workspace/workspace.ts`:
- Around line 41-44: Restore the missing TUploadImageResponse type in the
workspace types file: after the existing TApiResult declaration add a new
exported type named TUploadImageResponse matching the shape used by uploadImage
(an object with a url string) so imports in src/api/workspace/org.ts resolve;
ensure the exported name exactly matches TUploadImageResponse and that
uploadImage's return type aligns with data.data.url.
---
Nitpick comments:
In `@src/pages/workspace/MemberManagement.tsx`:
- Around line 7-18: The page component MemberManagement currently ignores the
route param workspaceId; update MemberManagement to read workspaceId (e.g., via
useParams) and pass it down as a prop to child components (MemberList and
PermissionTable) so they can scope API calls to the correct workspace; ensure
you only read the param in the page (no heavy business logic here) and forward
workspaceId (e.g., workspaceId={workspaceId}) to both MemberList and
PermissionTable so downstream components use that ID for their React Query
requests or Zustand actions.
In `@src/types/workspace/workspace.ts`:
- Line 73: TPermissionValue currently uses Korean string literals ("가능" |
"불가능"); verify whether this type models API responses or UI display values and
align it accordingly: if the API returns booleans, change TPermissionValue to
boolean; if the API returns English enums, change it to a string union like
"ALLOWED" | "DENIED"; if these are only UI labels, keep the current type but
move mapping logic into the UI layer (e.g., create a function that maps API
boolean/enum to the Korean display strings) and update all usages of
TPermissionValue to use the appropriate representation (refer to the
TPermissionValue type declaration to locate and update usages).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 895ff846-89f3-41b4-b9b4-bae8cf2625c2
⛔ Files ignored due to path filters (4)
src/assets/icon/workspace/check.svgis excluded by!**/*.svgand included bysrc/**src/assets/icon/workspace/mail.svgis excluded by!**/*.svgand included bysrc/**src/assets/icon/workspace/trash.svgis excluded by!**/*.svgand included bysrc/**src/assets/icon/workspace/user.svgis excluded by!**/*.svgand included bysrc/**
📒 Files selected for processing (9)
src/components/common/toggle/Toggle.tsxsrc/components/workspace/MemberItem.tsxsrc/components/workspace/MemberList.tsxsrc/components/workspace/MemberRoleSelect.tsxsrc/components/workspace/PermissionTable.tsxsrc/pages/workspace/MemberManagement.tsxsrc/pages/workspace/Workspace.tsxsrc/routes/MainRoutes.tsxsrc/types/workspace/workspace.ts
📚 Storybook 배포 완료
|
📚 Storybook 배포 완료
|
There was a problem hiding this comment.
♻️ Duplicate comments (3)
src/pages/workspace/MemberManagement.tsx (1)
23-23:⚠️ Potential issue | 🟡 Minor소유권 이전 버튼은 placeholder
alert대신 실제 핸들러(또는 비활성화)로 처리해 주세요.현재 클릭 시 사용자 흐름이 끊깁니다. 모달 오픈 핸들러로 연결하거나, 준비 전이라면
buttonDisabled와 안내 문구를 같이 두는 편이 안전합니다.As per coding guidelines,
src/**: 6. 에러 처리: API 실패 대응 및 사용자 피드백 적절성 검토. 에러 바운더리 사용 확인.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/workspace/MemberManagement.tsx` at line 23, Replace the placeholder alert in the MemberManagement component's onButtonClick with a real handler: wire it to an ownership-transfer flow (e.g. call openOwnershipTransferModal or dispatch an action that opens OwnershipTransferModal and performs the API call), and implement proper error handling/feedback on failure; if the feature isn’t ready, set buttonDisabled on the ownership transfer button and show a contextual message or tooltip explaining why (use unique identifiers onButtonClick, MemberManagement, buttonDisabled, openOwnershipTransferModal/OwnershipTransferModal to locate and update the code).src/components/workspace/MemberItem.tsx (1)
37-41:⚠️ Potential issue | 🟠 Major역할 변경 콜백이 실제 상태를 갱신하지 않아 기능이 동작하지 않습니다.
현재는
console.log만 실행되어 사용자가 바꾼 역할이 어디에도 반영되지 않습니다.MemberItem은 변경 이벤트만 전달하고, 실제 멤버 상태 갱신은MemberList(부모)에서 처리하도록 연결해 주세요.As per coding guidelines,
src/**: 1. 상태 관리: 서버 상태(React Query)와 전역 상태(Zustand)의 분리 여부 확인. useMutation, useQuery의 올바른 사용 확인.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/workspace/MemberItem.tsx` around lines 37 - 41, Replace the no-op console.log in MemberItem's MemberRoleSelect onChange with a call that forwards the change to the parent so the parent can perform the actual state/server update: add/ensure MemberItem accepts a prop like onRoleChange (or similar) and call onRoleChange(member.id, newRole) inside the onChange handler for MemberRoleSelect; in MemberList implement that handler to run the mutation (useMutation) to update the member role on the server and update React Query cache/global state accordingly.src/components/common/toggle/Toggle.tsx (1)
23-25:⚠️ Potential issue | 🟠 Major
switch상태값(aria-checked)을 꼭 같이 전달해 주세요.
role="switch"만 있고 상태 속성이 없어 보조기기에서 on/off를 알 수 없습니다. 이전 리뷰에서 지적된 내용과 동일 이슈입니다.수정 예시
<button type="button" role="switch" + aria-checked={checked} aria-label={ariaLabel} disabled={disabled} onClick={onToggle}As per coding guidelines,
src/**: 7. 접근성: 시맨틱 HTML, ARIA 속성 사용 확인.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/common/toggle/Toggle.tsx` around lines 23 - 25, The toggle with role="switch" in the Toggle component is missing the required ARIA state attribute aria-checked so assistive tech can't read on/off; update the element rendered by Toggle (the element that currently sets role="switch", aria-label={ariaLabel}, disabled={disabled}) to also include aria-checked bound to the component's checked state/prop (e.g., the internal state or prop name used to represent on/off) and ensure that checked is a boolean; also keep aria-label and disabled as-is so screen readers get label, state, and disabled info.
🧹 Nitpick comments (1)
src/types/workspace/workspace.ts (1)
52-57: 멤버 타입에 불변 식별자 필드를 추가하는 게 안전합니다.현재
TWorkspaceMember는memberId(또는orgMemberId)를 타입에 포함하고, 리스트 key/업데이트 payload도 해당 필드 기준으로 맞추는 쪽을 권장합니다.As per coding guidelines,
src/**: 4. 타입 안정성: TypeScript 타입의 명확성 확인. any 사용 지양, 제네릭 활용 검토.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/types/workspace/workspace.ts` around lines 52 - 57, TWorkspaceMember lacks a stable identifier which makes member updates/removals brittle; add a non-nullable unique id field (e.g. memberId or orgMemberId) to the TWorkspaceMember type, update any list rendering keys and API payload shapes to use that id instead of email, and ensure functions/types that consume TWorkspaceMember (e.g. update/delete handlers and DTOs) accept and propagate the new id field for reliable identification.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@src/components/common/toggle/Toggle.tsx`:
- Around line 23-25: The toggle with role="switch" in the Toggle component is
missing the required ARIA state attribute aria-checked so assistive tech can't
read on/off; update the element rendered by Toggle (the element that currently
sets role="switch", aria-label={ariaLabel}, disabled={disabled}) to also include
aria-checked bound to the component's checked state/prop (e.g., the internal
state or prop name used to represent on/off) and ensure that checked is a
boolean; also keep aria-label and disabled as-is so screen readers get label,
state, and disabled info.
In `@src/components/workspace/MemberItem.tsx`:
- Around line 37-41: Replace the no-op console.log in MemberItem's
MemberRoleSelect onChange with a call that forwards the change to the parent so
the parent can perform the actual state/server update: add/ensure MemberItem
accepts a prop like onRoleChange (or similar) and call onRoleChange(member.id,
newRole) inside the onChange handler for MemberRoleSelect; in MemberList
implement that handler to run the mutation (useMutation) to update the member
role on the server and update React Query cache/global state accordingly.
In `@src/pages/workspace/MemberManagement.tsx`:
- Line 23: Replace the placeholder alert in the MemberManagement component's
onButtonClick with a real handler: wire it to an ownership-transfer flow (e.g.
call openOwnershipTransferModal or dispatch an action that opens
OwnershipTransferModal and performs the API call), and implement proper error
handling/feedback on failure; if the feature isn’t ready, set buttonDisabled on
the ownership transfer button and show a contextual message or tooltip
explaining why (use unique identifiers onButtonClick, MemberManagement,
buttonDisabled, openOwnershipTransferModal/OwnershipTransferModal to locate and
update the code).
---
Nitpick comments:
In `@src/types/workspace/workspace.ts`:
- Around line 52-57: TWorkspaceMember lacks a stable identifier which makes
member updates/removals brittle; add a non-nullable unique id field (e.g.
memberId or orgMemberId) to the TWorkspaceMember type, update any list rendering
keys and API payload shapes to use that id instead of email, and ensure
functions/types that consume TWorkspaceMember (e.g. update/delete handlers and
DTOs) accept and propagate the new id field for reliable identification.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 4000b33d-9a06-4d23-8d7a-5b910007b6b4
⛔ Files ignored due to path filters (4)
src/assets/icon/workspace/check.svgis excluded by!**/*.svgand included bysrc/**src/assets/icon/workspace/mail.svgis excluded by!**/*.svgand included bysrc/**src/assets/icon/workspace/trash.svgis excluded by!**/*.svgand included bysrc/**src/assets/icon/workspace/user.svgis excluded by!**/*.svgand included bysrc/**
📒 Files selected for processing (9)
src/components/common/toggle/Toggle.tsxsrc/components/workspace/MemberItem.tsxsrc/components/workspace/MemberList.tsxsrc/components/workspace/MemberRoleSelect.tsxsrc/components/workspace/PermissionTable.tsxsrc/pages/workspace/MemberManagement.tsxsrc/pages/workspace/Workspace.tsxsrc/routes/MainRoutes.tsxsrc/types/workspace/workspace.ts
🚧 Files skipped from review as they are similar to previous changes (4)
- src/components/workspace/MemberList.tsx
- src/pages/workspace/Workspace.tsx
- src/components/workspace/MemberRoleSelect.tsx
- src/components/workspace/PermissionTable.tsx
📚 Storybook 배포 완료
|
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
src/components/workspace/MemberList.tsx (1)
64-64:⚠️ Potential issue | 🟡 Minor구성원 수 표시는
memberList.length기준으로 맞춰주세요.현재는
mockMembers.length를 사용해서, 실제 렌더 데이터(memberList)와 쉽게 어긋납니다.간단 수정 diff
- 현재 {mockMembers.length}명의 구성원이 활동 중입니다 + 현재 {memberList.length}명의 구성원이 활동 중입니다🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/workspace/MemberList.tsx` at line 64, The displayed member count is using mockMembers.length instead of the actual rendered array memberList; update the string in MemberList (component MemberList.tsx) to use memberList.length (replace mockMembers.length with memberList.length) so the UI reflects the real data source, and verify any nearby usages of mockMembers are similarly replaced or scoped only to test fixtures.
🧹 Nitpick comments (1)
src/components/workspace/MemberList.tsx (1)
67-74: 초대 버튼의alertTODO는 제거하고, 상위에서 핸들러를 주입받는 형태가 좋습니다.지금 방식은 사용자 흐름을 끊고 테스트도 어려워집니다.
onInviteClickprop으로 분리하거나, 미구현 단계면 버튼을 비활성화해 의도를 명확히 해주세요. 원하면 이 컴포넌트에 맞춘 최소 패치(Props 정의 + 호출부 연결)까지 바로 정리해드릴게요.As per coding guidelines,
src/**: 2. 구조와 책임 분리: 페이지에 비즈니스 로직이 과도하지 않은지 확인. 커스텀 훅으로의 분리 여부 검토.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/workspace/MemberList.tsx` around lines 67 - 74, Replace the inline alert handler on the invite Button with a prop-based handler: add an optional onInviteClick prop to the MemberList component's props, call that prop in the Button's onClick (e.g., onClick={() => onInviteClick?.()}), and if the prop is absent disable the button (or set aria-disabled) to indicate unimplemented behavior; update the Button usage and the MemberList props/type definition accordingly so parent components can inject the invite modal logic instead of using alert.
🤖 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/components/workspace/MemberRoleSelect.tsx`:
- Around line 28-31: When the MemberRoleSelect component's disabled prop toggles
to true the dropdown state (isOpen) should be closed to avoid showing options
when re-enabled; add an effect in MemberRoleSelect that watches the disabled
prop and calls setIsOpen(false) whenever disabled becomes true (use the existing
isOpen and setIsOpen state variables), and ensure similar logic is applied at
the other location referenced (around line 75) so both places synchronize isOpen
with disabled.
- Around line 60-89: The trigger button in MemberRoleSelect (the one that
toggles isOpen via setIsOpen) lacks an ARIA relationship to the popup and the
options lack semantic roles; add aria-controls on that trigger pointing to a
unique id for the popup, give the popup div role="menu" with that id and
aria-hidden based on disabled/isOpen, and mark each option button rendered from
restOptions with role="menuitem" (and manage aria-selected or tabIndex as
appropriate) so assistive tech can recognize the menu structure; update
references where handleSelect is called to ensure selection accessibility
attributes are updated accordingly.
---
Duplicate comments:
In `@src/components/workspace/MemberList.tsx`:
- Line 64: The displayed member count is using mockMembers.length instead of the
actual rendered array memberList; update the string in MemberList (component
MemberList.tsx) to use memberList.length (replace mockMembers.length with
memberList.length) so the UI reflects the real data source, and verify any
nearby usages of mockMembers are similarly replaced or scoped only to test
fixtures.
---
Nitpick comments:
In `@src/components/workspace/MemberList.tsx`:
- Around line 67-74: Replace the inline alert handler on the invite Button with
a prop-based handler: add an optional onInviteClick prop to the MemberList
component's props, call that prop in the Button's onClick (e.g., onClick={() =>
onInviteClick?.()}), and if the prop is absent disable the button (or set
aria-disabled) to indicate unimplemented behavior; update the Button usage and
the MemberList props/type definition accordingly so parent components can inject
the invite modal logic instead of using alert.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 259fd3c6-0b80-4a96-b19d-648ccb757917
📒 Files selected for processing (5)
src/components/common/toggle/Toggle.tsxsrc/components/workspace/MemberItem.tsxsrc/components/workspace/MemberList.tsxsrc/components/workspace/MemberRoleSelect.tsxsrc/components/workspace/PermissionTable.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
- src/components/workspace/MemberItem.tsx
- src/components/workspace/PermissionTable.tsx
- src/components/common/toggle/Toggle.tsx
📚 Storybook 배포 완료
|
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/components/workspace/MemberRoleSelect.tsx (1)
83-100: 조건부 렌더링과 ARIA 역할이 잘 적용되었습니다.이전 리뷰에서 지적된 문제들이 해결되었습니다:
isOpen && !disabled조건부 렌더링으로 닫힌 상태에서 포커스 문제 해결role="menu",role="menuitem"적용으로 보조기기 지원 개선참고: Line 87의 클래스 조건
${isOpen && !disabled ? "max-h-10 opacity-100" : "max-h-0 opacity-0"}는 이미 조건부 렌더링 블록 내에 있어 항상 첫 번째 분기가 실행됩니다. 코드 가독성을 위해 정적 클래스로 단순화할 수 있습니다.♻️ 단순화 예시
<div id={menuId} role="menu" - className={`overflow-hidden bg-gray-100 transition-all duration-200 ease-out ${isOpen && !disabled ? "max-h-10 opacity-100" : "max-h-0 opacity-0"}`} + className="overflow-hidden bg-gray-100 transition-all duration-200 ease-out max-h-10 opacity-100" >🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/workspace/MemberRoleSelect.tsx` around lines 83 - 100, The className conditional on the div with id={menuId} (currently using `${isOpen && !disabled ? "max-h-10 opacity-100" : "max-h-0 opacity-0"}`) is redundant because the entire block is already guarded by `isOpen && !disabled`; update the `className` on that element to use the static classes for the open state (e.g., include "max-h-10 opacity-100" along with the existing classes) and remove the ternary expression so the string is constant; the rest of the component (including `handleSelect`, `restOptions`, and `role="menu"/"menuitem"`) can remain unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@src/components/workspace/MemberRoleSelect.tsx`:
- Around line 83-100: The className conditional on the div with id={menuId}
(currently using `${isOpen && !disabled ? "max-h-10 opacity-100" : "max-h-0
opacity-0"}`) is redundant because the entire block is already guarded by
`isOpen && !disabled`; update the `className` on that element to use the static
classes for the open state (e.g., include "max-h-10 opacity-100" along with the
existing classes) and remove the ternary expression so the string is constant;
the rest of the component (including `handleSelect`, `restOptions`, and
`role="menu"/"menuitem"`) can remain unchanged.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 837ba1ce-2a71-4e3c-a7e7-0ac3cafb7ebd
📒 Files selected for processing (1)
src/components/workspace/MemberRoleSelect.tsx
|
P4: 저는 |
📚 Storybook 배포 완료
|
There was a problem hiding this comment.
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 (1)
src/components/common/controlbox/ControlBox.tsx (1)
45-49:⚠️ Potential issue | 🟡 MinorWorkspaceSetting.tsx에서 min-width 설정 추가 필요
min-w-180제거로 최소 너비 보장이 없어졌습니다.WorkspaceSetting.tsx299줄의 ControlBox는containerClassName에min-w-*클래스 없이 사용되고 있어, 좁은 뷰포트에서 콘텐츠가 의도치 않게 축소될 수 있습니다.containerClassName에 적절한min-w-*값을 추가하거나,classNameprop에min-w-*를 명시해주세요.다른 사용처(AdsListPage, AdDetailContent, CampaignDetail)는 이미
containerClassName에min-w-*를 전달하고 있어 영향이 없습니다.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/common/controlbox/ControlBox.tsx` around lines 45 - 49, The ControlBox usage in WorkspaceSetting (the ControlBox component instance around the WorkspaceSetting rendering) lacks a min-width class because containerClassName has no min-w-*; update the WorkspaceSetting.tsx where ControlBox is rendered (the instance near line ~299) to include an appropriate Tailwind min-w class (e.g., add "min-w-180" or another required size) by appending it to containerClassName or to the className prop passed into ControlBox so the ControlBox component (which composes containerClassName and className) always enforces the minimum width and prevents content from collapsing on narrow viewports.
🤖 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/index.css`:
- Around line 10-11: 전역 CSS에서 html, body에 설정된 overflow: hidden은 인증 페이지의 스크롤을
막으므로 index.css에서 이 전역 속성을 제거하고 중복된 overflow-x: hidden도 삭제하거나 축소하세요; 대신 인증 화면 전용
스크롤 처리는 AuthLayout.tsx(또는 인증 폼 래퍼)에서 overflow-y: auto/scroll과 max-height 또는 적절한
컨테이너 스타일을 적용해 처리하고, MainLayout이 페이지 전체 레이아웃용 오버플로 제어를 담당하도록 역할을 분리하세요.
---
Outside diff comments:
In `@src/components/common/controlbox/ControlBox.tsx`:
- Around line 45-49: The ControlBox usage in WorkspaceSetting (the ControlBox
component instance around the WorkspaceSetting rendering) lacks a min-width
class because containerClassName has no min-w-*; update the WorkspaceSetting.tsx
where ControlBox is rendered (the instance near line ~299) to include an
appropriate Tailwind min-w class (e.g., add "min-w-180" or another required
size) by appending it to containerClassName or to the className prop passed into
ControlBox so the ControlBox component (which composes containerClassName and
className) always enforces the minimum width and prevents content from
collapsing on narrow viewports.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 770d270b-acc3-4105-837e-0f1a3301a896
📒 Files selected for processing (4)
src/components/common/controlbox/ControlBox.tsxsrc/index.csssrc/layout/main/MainLayout.tsxsrc/pages/workspace/MemberManagement.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- src/pages/workspace/MemberManagement.tsx
…Layout.tsx에서만 해결하도록 수정 방법 변경)
📚 Storybook 배포 완료
|
Seojegyeong
left a comment
There was a problem hiding this comment.
P4: 확인했습니다! 수고하셨습니다.
|
P4: 확인했습니다 수고하셨습니다! |




🚨 관련 이슈
Closed #91
✨ 변경사항
✏️ 작업 내용
😅 미완성 작업
📢 논의 사항 및 참고 사항
작업단위가 커져서 모달 작업은 따로 이슈올려서 작업진행하겠습니다.
toggle은 재사용가능성이 높아서 따로 컴포넌트만들어놓았으니, 앞으로의 다른작업에 사용하실때 디자인 적용해서 사용하시면될것같습니다.
팀 구성원파트에서 관리자와 멤버 역할 변경하는 부분 현재는 모달이 열릴시에 카드높이가 변경되어서 부자연스러워보이는데, 리팩토링 작업 진행예정입니다.관리자 변경ControlBox는 워크스페이스 수정 부분과 동일한 패턴으로 배치되도록 수정했습니다권한 관련해서는 현재 mock데이터 만들어서 7개의 권한을 넣어두었는데, 이부분에 대해서 어떤 권한을 변경가능하도록 할지는 1차 MVP 완료후 2차 MVP에 대해서 논의할때 얘기해보면 좋을것같습니다.
그리고 권한 파트에서 지금 멤버 권한을 토글로 변경가능하도록 설정되었는데, 토글은 손쉽게 상태변경이 가능하다는 장점이 있고 직관적으로 보여서 좋다는 장점이 있지만, 권한은 중요도가 높은 작업이기 때문에 사용자가 한번더 체크하는 장치가 있으면 좋을것같습니다. 매번 토글 상태 변경할때마다
맞습니까?라는 문구가 나오기보다는 현재처럼 유지하고, 권한설정과 소유권변경 사이에변경사항 저장하기버튼을 추가로 만들어서,권한설정을 변경하시겠습니까?라고 안내문구를 한번에 내고 상태 저장도 한번에 되도록 진행하는것이 좋을것같다고 생각했는데, 어떠실까요?Summary by CodeRabbit
Summary by CodeRabbit
새로운 기능
타입/데이터
스타일/레이아웃