Skip to content

[feat] 소셜 페이지 채팅목록 API 연동#14

Merged
dohy-eon merged 5 commits into
devfrom
fix/ALT-173
Apr 29, 2026
Merged

[feat] 소셜 페이지 채팅목록 API 연동#14
dohy-eon merged 5 commits into
devfrom
fix/ALT-173

Conversation

@kim3360
Copy link
Copy Markdown
Contributor

@kim3360 kim3360 commented Apr 28, 2026

ID

  • ALT-173

변경 내용

  • 매니저 소셜 페이지를 실제 채팅방 목록 API와 연동
  • 소셜 리스트 UI를 스와이프 액션 중심으로 정리하고, 하단 네비게이션의 소셜 탭 이동 연결

구현 사항

  • GET /manager/chat/rooms API 추가 및 타입 정의 구성
    • chatroom 응답 DTO/쿼리 파라미터 타입 정리
  • fetchChatRooms 구현
    • features/social/hooks/useChatRoomsViewModel 추가
  • React Query 기반 목록 조회
    • updatedAt 상대시간 변환 및 UI 렌더용 데이터 매핑
  • pages/manager/social 연동
    • 하드코딩 리스트 제거 후 API 데이터 렌더링
  • 소셜 리스트 UI 개선
    • 마우스/포인터 드래그 기반 스와이프 동작 정리

구현 시연 (필요 시)

  • 작업물을 가시적으로 표현한다 (스크린샷, 영상 등)

참고 사항 (필요 시)

  • 현재 소셜 목록의 unread 관련 값은 API 스펙 확정 후 후속 반영 예정

Summary by CodeRabbit

  • New Features

    • Dynamic chat room list with live data fetching and loading/error states
    • Search entry retained as a popup entry point
  • Improvements

    • Unread dot replaced by unread count pill
    • Swipe actions simplified to delete-only with compact icon
    • Removed category filters and floating action button
    • Message tab routing adjusts by user role

@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 28, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
alter-client Ready Ready Preview, Comment Apr 28, 2026 11:33am

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 28, 2026

Warning

.coderabbit.yaml has a parsing error

The CodeRabbit configuration file in this repository has a parsing error and default settings were used instead. Please fix the error(s) in the configuration file. You can initialize chat with CodeRabbit to get help with the configuration file.

💥 Parsing errors (1)
Validation error: String must contain at most 250 character(s) at "tone_instructions"
⚙️ Configuration instructions
  • Please see the configuration documentation for more information.
  • You can also validate your configuration using the online YAML validator.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
📝 Walkthrough

Walkthrough

Introduces chat-room fetching and view model: new API, types, and hook; replaces static social UI with dynamic list rendering; moves search to feature layer; removes several shared UI components; and updates routing for manager message paths. (49 words)

Changes

Cohort / File(s) Summary
API & Types
src/features/social/api/chatroom.ts, src/features/social/types/chatroom.ts
Adds fetchChatRooms and cursor-paginated DTOs (CursorPaginatedApiResponseChatRoomListResponseDto, ChatRoomListResponseDto, ChatRoomPageDto, ChatRoomListQueryParams).
ViewModel Hook
src/features/social/hooks/useChatRoomsViewModel.ts
New useChatRoomsViewModel hook and SocialListItemViewData that fetches chat rooms (React Query), adapts items (name, message, timeAgo, unread), and exposes loading/error states.
Feature Exports
src/features/social/index.ts
Re-exports fetchChatRooms, useChatRoomsViewModel, related DTO types, and SocialListItemViewData.
Feature-level UI
src/features/social/common/SocialSearch.tsx
Adds feature-level SocialSearch component with optional onClick prop; replaces removed shared search.
Social List UI
src/features/social/ui/SocialList.tsx
Removes onRead handlers, adds optional unreadCount prop and counter pill, converts delete action to icon button, tightens swipe width and restricts drag start to left mouse button.
Page Integration
src/pages/manager/social/index.tsx
Social page now uses useChatRoomsViewModel to render dynamic list, shows loading/error states, removes category row and floating action button, and uses SocialSearch popup content sourced from chatRooms.
Routing & Nav
src/shared/stores/useDocStore.ts, src/shared/ui/common/Docbar.tsx
Adds /manager/social pathname mapping and switches Docbar message nav target to /manager/social for manager scope (otherwise /user/message).
Removed Shared UI
src/shared/ui/common/FloatingActionButton.tsx, src/shared/ui/manager/social/SocialCategory.tsx, src/shared/ui/manager/social/SocialSearch.tsx
Deletes floating action button, old SocialCategory, and old shared SocialSearch components and their props/types.

Sequence Diagram

sequenceDiagram
    participant User
    participant SocialPage as SocialPage
    participant Hook as useChatRoomsViewModel
    participant Query as ReactQuery
    participant API as fetchChatRooms
    participant Server as Backend
    participant UI as SwipeableSocialItem

    User->>SocialPage: navigate to /manager/social
    SocialPage->>Hook: initialize hook
    Hook->>Query: useQuery(fetchChatRooms)
    Query->>API: call fetchChatRooms(params)
    API->>Server: GET /manager/chat/rooms?pageSize=...
    Server-->>API: return cursor-paginated response
    API-->>Query: response.data
    Query-->>Hook: delivers data
    Hook->>Hook: map data -> SocialListItemViewData (name, message, timeAgo, unread)
    Hook-->>SocialPage: chatRooms, isLoading, isError
    SocialPage->>UI: render SwipeableSocialItem per chatRoom
    UI-->>User: display list with unread counts
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Suggested reviewers

  • limtjdghks
  • kyeongb-bin

Poem

🐰 Hopping through code with a twitch of my nose,
Fetching chat rooms where the conversation grows,
Hooks hum a tune, types tidy and bright,
Old buttons tucked in for a quieter night,
I nibble bugs gently and bound toward new light.

🚥 Pre-merge checks | ✅ 4 | ❌ 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 (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main feature: connecting the manager social page to the chat room list API, which matches the primary objective of the PR.
Description check ✅ Passed The description follows the repository template with all required sections completed: ID (ALT-173), 변경 내용 (changes), 구현 사항 (implementation details), and 참고 사항 (notes about future unread updates).
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ 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 fix/ALT-173

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.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

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

⚠️ Outside diff range comments (2)
src/features/social/ui/SocialList.tsx (1)

57-149: ⚠️ Potential issue | 🟠 Major

Don't expose swipe-delete UI when no delete handler is wired.

SwipeableSocialItem stays swipeable and renders a destructive action even when onDelete is undefined. The current caller in src/pages/manager/social/index.tsx:36-42 passes no handler, so users get a dead control.

Proposed fix
 export function SwipeableSocialItem({
   name,
   message,
   timeAgo,
   unread = false,
   onDelete,
   unreadCount,
 }: SwipeableSocialItemProps) {
+  const hasDeleteAction = Boolean(onDelete)
   const [translateX, setTranslateX] = useState(0)
   const [isDragging, setIsDragging] = useState(false)

   const pointerIdRef = useRef<number | null>(null)
   const startXRef = useRef(0)
   const startTranslateXRef = useRef(0)

   const handlePointerDown: PointerEventHandler<HTMLDivElement> = event => {
+    if (!hasDeleteAction) return
     if (event.button !== 0) return
     pointerIdRef.current = event.pointerId
     startXRef.current = event.clientX
     startTranslateXRef.current = translateX
     setIsDragging(true)
     event.currentTarget.setPointerCapture(event.pointerId)
   }

   const handlePointerMove: PointerEventHandler<HTMLDivElement> = event => {
-    if (!isDragging || pointerIdRef.current !== event.pointerId) return
+    if (!hasDeleteAction || !isDragging || pointerIdRef.current !== event.pointerId) return

     const deltaX = event.clientX - startXRef.current
     const nextTranslateX = Math.min(
       0,
       Math.max(-ACTION_WIDTH, startTranslateXRef.current + deltaX)
@@
   return (
     <div className="relative  overflow-hidden">
-      <div className="absolute right-0 top-0 h-full flex items-center">
-        <SocialAction onDelete={onDelete} />
-      </div>
+      {hasDeleteAction && (
+        <div className="absolute right-0 top-0 h-full flex items-center">
+          <SocialAction onDelete={onDelete} />
+        </div>
+      )}

       <div
         className="bg-white"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/social/ui/SocialList.tsx` around lines 57 - 149,
SwipeableSocialItem renders swipe UI and SocialAction even when onDelete is
undefined; update SwipeableSocialItem to disable swipe interactions and avoid
rendering the SocialAction button when onDelete is not provided: guard the
rendering of the absolute action container and the SocialAction component on
onDelete, and short-circuit pointer handlers
(handlePointerDown/handlePointerMove/handlePointerUp/handlePointerCancel or
early-return from finishDrag) when onDelete is falsy so the item is not
draggable; keep existing behavior when onDelete is present (use
ACTION_WIDTH/OPEN_THRESHOLD logic unchanged).
src/pages/manager/social/index.tsx (1)

81-87: ⚠️ Potential issue | 🟠 Major

Room identifier is lost in navigation and SocialChatPage has no way to resolve which chat was selected.

Navigation at line 86 drops item.id when calling navigate('/manager/social/chat'), and the route path contains no parameter. The SocialChatPage component imports only useNavigate and useState, with no calls to useParams(), useSearchParams(), useLocation(), or any context/state readers. The page displays hardcoded content (hardcoded name "이수연" on line 23 and demo messages) regardless of which room is clicked.

The route must be updated to include a room identifier (e.g., /manager/social/chat/:roomId), or the component must read the room from state, query params, or an explicit source (context, localStorage, etc.). Currently, selection of different rooms has no effect on what is displayed.

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

In `@src/pages/manager/social/index.tsx` around lines 81 - 87, The click handler
on the room list closes the popup then calls navigate('/manager/social/chat')
which drops item.id so SocialChatPage cannot resolve which room to show; change
navigation to include the room identifier (e.g.,
navigate(`/manager/social/chat/${item.id}`) or navigate('/manager/social/chat',
{ state: { roomId: item.id } }) and update your route to accept a param
(/manager/social/chat/:roomId) or ensure the page reads the ID from
location.state or query params. Then update SocialChatPage to read the room
identifier (useParams() if you add :roomId, or useLocation()/useSearchParams()
if passing state/query) and use that ID to load/render the correct room instead
of the hardcoded name/messages.
🧹 Nitpick comments (2)
src/pages/manager/social/index.tsx (1)

56-72: Avoid re-implementing the popup search trigger here.

This reintroduces a second search-button variant with inline hex colors, so it will drift from src/features/social/common/SocialSearch.tsx. Reuse the shared component or extract a shared styled primitive instead.

Based on learnings: In the alter-client repository, avoid hardcoding colors in TSX files.

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

In `@src/pages/manager/social/index.tsx` around lines 56 - 72, The duplicated
search button UI in the Social page should be replaced with the shared search
component instead of re-implementing inline-styled button; import and use the
existing SocialSearch component (from
src/features/social/common/SocialSearch.tsx) in place of the hardcoded <button>
(the button block in this file around the map render), remove the inline hex
color usages and any duplicated markup so styling is driven by the shared
component or a shared styled primitive, and ensure the map over chatRooms and
surrounding layout remain unchanged.
src/features/social/hooks/useChatRoomsViewModel.ts (1)

10-16: Avoid encoding unknown unread state as false.

ChatRoomListResponseDto currently has no unread fields, but Line 32 sets unread: false and this value is passed directly to UI in src/pages/manager/social/index.tsx (Line 35-43). That makes every room look read instead of “unknown/not provided”.

Proposed refactor
 export interface SocialListItemViewData {
   id: number
   name: string
   message: string
   timeAgo: string
-  unread: boolean
+  unread: boolean | null
 }
@@
   return {
     id: room.id,
     name: room.opponentName,
     message: room.latestMessageContent,
     timeAgo: toRelativeTime(room.updatedAt),
-    unread: false,
+    unread: null,
   }
 }

Then let the UI hide unread indicators when unread === null.

Also applies to: 24-33

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

In `@src/features/social/hooks/useChatRoomsViewModel.ts` around lines 10 - 16,
SocialListItemViewData and the mapping in useChatRoomsViewModel currently force
unread: false when ChatRoomListResponseDto has no unread field; change the
SocialListItemViewData.unread type to boolean | null and set unread to null in
the mapper where the DTO lacks an unread value (e.g. in useChatRoomsViewModel
mapping logic that builds SocialListItemViewData from ChatRoomListResponseDto),
and update any consumers (the UI that renders the list) to hide the unread
indicator when unread === null rather than treating it as read; apply the same
nullable change to all mapping sites referenced in the comment (around the
existing mapping block).
🤖 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/features/social/hooks/useChatRoomsViewModel.ts`:
- Around line 18-22: The toRelativeTime function always appends the past suffix
'전', causing future timestamps to display incorrectly; change it to compute now
(Date.now()), compare the parsed date to now, and choose the suffix
conditionally ('후' for future, '전' for past) while still returning '-' for
invalid dates and keeping formatDistanceToNowStrict with locale: ko; update the
return in toRelativeTime to branch on date > now and append the appropriate
suffix.

In `@src/features/social/ui/SocialList.tsx`:
- Line 2: The build is failing because the import TrashIcon from
'@/assets/icons/social/Trash.svg' in SocialList.tsx points to a missing asset;
either add the missing Trash.svg file to that assets folder or update the import
in SocialList.tsx to reference an existing icon file (e.g., the correct SVG name
or a shared icon component) and update any usage of TrashIcon accordingly so the
import resolves.
- Around line 20-21: ACTION_WIDTH is out of sync with the action container width
causing overshoot; update the constant ACTION_WIDTH in SocialList.tsx to match
the actual action container width (change from 72 to 60) and recompute
OPEN_THRESHOLD accordingly (e.g., OPEN_THRESHOLD = ACTION_WIDTH * 0.45), and
also update any other hardcoded widths or uses of the old 72 value elsewhere in
this file (the block around the later action/gesture logic) so the swipe
distance and revealed action width stay consistent.

In `@src/shared/ui/common/Docbar.tsx`:
- Around line 118-124: The pathByTab mapping uses scope (from useAuthStore)
which may be null during rehydration and causes manager users to be routed to
user paths; update the logic that builds or consumes pathByTab (references:
pathByTab, scope, useAuthStore, TabKey) to avoid defaulting to user routes when
scope is null — either (A) gate tab navigation until scope !== null (disable
taps or return early in the tab click/route handler), or (B) derive the fallback
role from the current pathname (e.g., inspect window.location.pathname) when
scope is null and only then pick manager vs user routes, ensuring no direct
default to '/user/*' while scope is unresolved. Ensure the chosen change is
applied where pathByTab is read (tab click/navigation) so no navigation occurs
to user routes during auth rehydration.

---

Outside diff comments:
In `@src/features/social/ui/SocialList.tsx`:
- Around line 57-149: SwipeableSocialItem renders swipe UI and SocialAction even
when onDelete is undefined; update SwipeableSocialItem to disable swipe
interactions and avoid rendering the SocialAction button when onDelete is not
provided: guard the rendering of the absolute action container and the
SocialAction component on onDelete, and short-circuit pointer handlers
(handlePointerDown/handlePointerMove/handlePointerUp/handlePointerCancel or
early-return from finishDrag) when onDelete is falsy so the item is not
draggable; keep existing behavior when onDelete is present (use
ACTION_WIDTH/OPEN_THRESHOLD logic unchanged).

In `@src/pages/manager/social/index.tsx`:
- Around line 81-87: The click handler on the room list closes the popup then
calls navigate('/manager/social/chat') which drops item.id so SocialChatPage
cannot resolve which room to show; change navigation to include the room
identifier (e.g., navigate(`/manager/social/chat/${item.id}`) or
navigate('/manager/social/chat', { state: { roomId: item.id } }) and update your
route to accept a param (/manager/social/chat/:roomId) or ensure the page reads
the ID from location.state or query params. Then update SocialChatPage to read
the room identifier (useParams() if you add :roomId, or
useLocation()/useSearchParams() if passing state/query) and use that ID to
load/render the correct room instead of the hardcoded name/messages.

---

Nitpick comments:
In `@src/features/social/hooks/useChatRoomsViewModel.ts`:
- Around line 10-16: SocialListItemViewData and the mapping in
useChatRoomsViewModel currently force unread: false when ChatRoomListResponseDto
has no unread field; change the SocialListItemViewData.unread type to boolean |
null and set unread to null in the mapper where the DTO lacks an unread value
(e.g. in useChatRoomsViewModel mapping logic that builds SocialListItemViewData
from ChatRoomListResponseDto), and update any consumers (the UI that renders the
list) to hide the unread indicator when unread === null rather than treating it
as read; apply the same nullable change to all mapping sites referenced in the
comment (around the existing mapping block).

In `@src/pages/manager/social/index.tsx`:
- Around line 56-72: The duplicated search button UI in the Social page should
be replaced with the shared search component instead of re-implementing
inline-styled button; import and use the existing SocialSearch component (from
src/features/social/common/SocialSearch.tsx) in place of the hardcoded <button>
(the button block in this file around the map render), remove the inline hex
color usages and any duplicated markup so styling is driven by the shared
component or a shared styled primitive, and ensure the map over chatRooms and
surrounding layout remain unchanged.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 85b0ff84-a638-4905-8a88-e2b942544ee2

📥 Commits

Reviewing files that changed from the base of the PR and between b962acb and 6852dc7.

⛔ Files ignored due to path filters (1)
  • src/assets/icons/social/trash.svg is excluded by !**/*.svg
📒 Files selected for processing (12)
  • src/features/social/api/chatroom.ts
  • src/features/social/common/SocialSearch.tsx
  • src/features/social/hooks/useChatRoomsViewModel.ts
  • src/features/social/index.ts
  • src/features/social/types/chatroom.ts
  • src/features/social/ui/SocialList.tsx
  • src/pages/manager/social/index.tsx
  • src/shared/stores/useDocStore.ts
  • src/shared/ui/common/Docbar.tsx
  • src/shared/ui/common/FloatingActionButton.tsx
  • src/shared/ui/manager/social/SocialCategory.tsx
  • src/shared/ui/manager/social/SocialSearch.tsx
💤 Files with no reviewable changes (3)
  • src/shared/ui/common/FloatingActionButton.tsx
  • src/shared/ui/manager/social/SocialSearch.tsx
  • src/shared/ui/manager/social/SocialCategory.tsx

Comment on lines +18 to +22
function toRelativeTime(dateString: string): string {
const date = new Date(dateString)
if (Number.isNaN(date.getTime())) return '-'
return `${formatDistanceToNowStrict(date, { locale: ko })} 전`
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Handle future timestamps in toRelativeTime.

Line 21 always appends , so future updatedAt values render incorrectly as past. Please branch suffix by comparing with current time.

Proposed fix
 function toRelativeTime(dateString: string): string {
   const date = new Date(dateString)
   if (Number.isNaN(date.getTime())) return '-'
-  return `${formatDistanceToNowStrict(date, { locale: ko })} 전`
+  const label = formatDistanceToNowStrict(date, { locale: ko })
+  return date.getTime() > Date.now() ? `${label} 후` : `${label} 전`
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function toRelativeTime(dateString: string): string {
const date = new Date(dateString)
if (Number.isNaN(date.getTime())) return '-'
return `${formatDistanceToNowStrict(date, { locale: ko })} 전`
}
function toRelativeTime(dateString: string): string {
const date = new Date(dateString)
if (Number.isNaN(date.getTime())) return '-'
const label = formatDistanceToNowStrict(date, { locale: ko })
return date.getTime() > Date.now() ? `${label} 후` : `${label} 전`
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/social/hooks/useChatRoomsViewModel.ts` around lines 18 - 22, The
toRelativeTime function always appends the past suffix '전', causing future
timestamps to display incorrectly; change it to compute now (Date.now()),
compare the parsed date to now, and choose the suffix conditionally ('후' for
future, '전' for past) while still returning '-' for invalid dates and keeping
formatDistanceToNowStrict with locale: ko; update the return in toRelativeTime
to branch on date > now and append the appropriate suffix.

Comment thread src/features/social/ui/SocialList.tsx Outdated
Comment on lines +20 to 21
const ACTION_WIDTH = 72
const OPEN_THRESHOLD = ACTION_WIDTH * 0.45
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Keep the swipe distance and revealed action width in sync.

ACTION_WIDTH is 72px, but the action container is 60px wide. Fully opening a row will overshoot and leave an empty gutter on the right.

Also applies to: 142-146

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

In `@src/features/social/ui/SocialList.tsx` around lines 20 - 21, ACTION_WIDTH is
out of sync with the action container width causing overshoot; update the
constant ACTION_WIDTH in SocialList.tsx to match the actual action container
width (change from 72 to 60) and recompute OPEN_THRESHOLD accordingly (e.g.,
OPEN_THRESHOLD = ACTION_WIDTH * 0.45), and also update any other hardcoded
widths or uses of the old 72 value elsewhere in this file (the block around the
later action/gesture logic) so the swipe distance and revealed action width stay
consistent.

Comment on lines 118 to 124
const pathByTab: Record<TabKey, string> = {
home: scope === 'MANAGER' ? '/manager/home' : '/user/home',
search: '/user/job-lookup-map',
message: '/message',
message: scope === 'MANAGER' ? '/manager/social' : '/user/message',
repute: '/repute',
my: '/my',
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Don't fall back to user routes while scope is still null.

useAuthStore initializes scope as null, so a manager can be sent to /user/message if they tap before auth rehydration finishes. Please gate tab navigation until scope resolves, or derive the fallback from the current pathname instead of defaulting to user routes.

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

In `@src/shared/ui/common/Docbar.tsx` around lines 118 - 124, The pathByTab
mapping uses scope (from useAuthStore) which may be null during rehydration and
causes manager users to be routed to user paths; update the logic that builds or
consumes pathByTab (references: pathByTab, scope, useAuthStore, TabKey) to avoid
defaulting to user routes when scope is null — either (A) gate tab navigation
until scope !== null (disable taps or return early in the tab click/route
handler), or (B) derive the fallback role from the current pathname (e.g.,
inspect window.location.pathname) when scope is null and only then pick manager
vs user routes, ensuring no direct default to '/user/*' while scope is
unresolved. Ensure the chosen change is applied where pathByTab is read (tab
click/navigation) so no navigation occurs to user routes during auth
rehydration.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (1)
src/features/social/ui/SocialList.tsx (1)

20-21: ⚠️ Potential issue | 🟡 Minor

Swipe open width is still out of sync with the action panel width.

Line 20 uses ACTION_WIDTH = 72, but Line 142 renders a 60px action panel. This still causes overshoot/gutter at full open state.

Suggested fix
-const ACTION_WIDTH = 72
+const ACTION_WIDTH = 60
 const OPEN_THRESHOLD = ACTION_WIDTH * 0.45

Also applies to: 142-142

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

In `@src/features/social/ui/SocialList.tsx` around lines 20 - 21, The swipe-open
width constant (ACTION_WIDTH) is inconsistent with the rendered action panel
size causing overshoot; update the code so the visual width and threshold come
from the same source: either set ACTION_WIDTH = 60 to match the rendered 60px
panel and recompute OPEN_THRESHOLD (e.g., ACTION_WIDTH * 0.45 or 0.5), or change
the JSX that renders the action panel to use the ACTION_WIDTH constant for its
style/width; ensure both ACTION_WIDTH and OPEN_THRESHOLD are the single source
of truth used by the component (SocialList and its swipe handlers) so the open
threshold and panel width stay in sync.
🤖 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/features/social/ui/SocialList.tsx`:
- Around line 46-49: The badge currently renders a fabricated number via
"unreadCount ?? 1" in the SocialList component; change the UI to avoid
defaulting to 1 when unreadCount is unknown by rendering a simple dot or
fallback badge when "unread" is true but "unreadCount" is null/undefined, and
only show the numeric count when "unreadCount" is a valid number (e.g., >0);
update the JSX that references unread and unreadCount in SocialList.tsx to
conditionally render either the numeric badge or a dot/fallback UI accordingly.
- Around line 147-149: The button in SocialList.tsx already sets
aria-label="삭제", so the <img src={TrashIcon} ... /> should be made decorative to
avoid redundant announcements: change the image's alt to an empty string and
mark it decorative (e.g., add aria-hidden="true" or role="presentation") so
screen readers announce only the button label; update the <img> that references
TrashIcon accordingly.

---

Duplicate comments:
In `@src/features/social/ui/SocialList.tsx`:
- Around line 20-21: The swipe-open width constant (ACTION_WIDTH) is
inconsistent with the rendered action panel size causing overshoot; update the
code so the visual width and threshold come from the same source: either set
ACTION_WIDTH = 60 to match the rendered 60px panel and recompute OPEN_THRESHOLD
(e.g., ACTION_WIDTH * 0.45 or 0.5), or change the JSX that renders the action
panel to use the ACTION_WIDTH constant for its style/width; ensure both
ACTION_WIDTH and OPEN_THRESHOLD are the single source of truth used by the
component (SocialList and its swipe handlers) so the open threshold and panel
width stay in sync.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: a4b70f34-85b0-4a9a-8c20-1d79e67aa63f

📥 Commits

Reviewing files that changed from the base of the PR and between 6852dc7 and 6692df8.

📒 Files selected for processing (1)
  • src/features/social/ui/SocialList.tsx

Comment on lines +46 to +49
{unread ? (
<div className="flex h-5 min-w-5 items-center justify-center rounded-full bg-sub px-1 text-white typography-body03-regular">
{unreadCount ?? 1}
</div>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Avoid defaulting unknown unread count to 1.

Line 48 (unreadCount ?? 1) can display an inaccurate count when count data is not available yet. Prefer rendering a dot/fallback UI instead of a fabricated number.

Suggested fix
-          {unread ? (
-            <div className="flex h-5 min-w-5 items-center justify-center rounded-full bg-sub px-1 text-white typography-body03-regular">
-              {unreadCount ?? 1}
-            </div>
-          ) : null}
+          {unread ? (
+            unreadCount != null ? (
+              <div className="flex h-5 min-w-5 items-center justify-center rounded-full bg-sub px-1 text-white typography-body03-regular">
+                {unreadCount}
+              </div>
+            ) : (
+              <div className="h-2 w-2 rounded-full bg-sub" aria-label="읽지 않음" />
+            )
+          ) : null}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{unread ? (
<div className="flex h-5 min-w-5 items-center justify-center rounded-full bg-sub px-1 text-white typography-body03-regular">
{unreadCount ?? 1}
</div>
{unread ? (
unreadCount != null ? (
<div className="flex h-5 min-w-5 items-center justify-center rounded-full bg-sub px-1 text-white typography-body03-regular">
{unreadCount}
</div>
) : (
<div className="h-2 w-2 rounded-full bg-sub" aria-label="읽지 않음" />
)
) : null}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/social/ui/SocialList.tsx` around lines 46 - 49, The badge
currently renders a fabricated number via "unreadCount ?? 1" in the SocialList
component; change the UI to avoid defaulting to 1 when unreadCount is unknown by
rendering a simple dot or fallback badge when "unread" is true but "unreadCount"
is null/undefined, and only show the numeric count when "unreadCount" is a valid
number (e.g., >0); update the JSX that references unread and unreadCount in
SocialList.tsx to conditionally render either the numeric badge or a
dot/fallback UI accordingly.

Comment on lines +147 to +149
aria-label="삭제"
>
삭제
<img src={TrashIcon} alt="삭제" />
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Icon alt text duplicates the button accessible name.

Line 147 already provides aria-label="삭제". Keeping Line 149 as alt="삭제" may be announced redundantly by screen readers. Make the icon decorative.

Suggested fix
-        <img src={TrashIcon} alt="삭제" />
+        <img src={TrashIcon} alt="" aria-hidden="true" />
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
aria-label="삭제"
>
삭제
<img src={TrashIcon} alt="삭제" />
aria-label="삭제"
>
<img src={TrashIcon} alt="" aria-hidden="true" />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/social/ui/SocialList.tsx` around lines 147 - 149, The button in
SocialList.tsx already sets aria-label="삭제", so the <img src={TrashIcon} ... />
should be made decorative to avoid redundant announcements: change the image's
alt to an empty string and mark it decorative (e.g., add aria-hidden="true" or
role="presentation") so screen readers announce only the button label; update
the <img> that references TrashIcon accordingly.

@dohy-eon dohy-eon merged commit 7cec684 into dev Apr 29, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants