[feat] 소셜 페이지 채팅목록 API 연동#14
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning
|
| 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
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 | 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.
Comment @coderabbitai help to get the list of available commands and usage tips.
There was a problem hiding this comment.
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 | 🟠 MajorDon't expose swipe-delete UI when no delete handler is wired.
SwipeableSocialItemstays swipeable and renders a destructive action even whenonDeleteisundefined. The current caller insrc/pages/manager/social/index.tsx:36-42passes 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 | 🟠 MajorRoom identifier is lost in navigation and SocialChatPage has no way to resolve which chat was selected.
Navigation at line 86 drops
item.idwhen callingnavigate('/manager/social/chat'), and the route path contains no parameter. TheSocialChatPagecomponent imports onlyuseNavigateanduseState, with no calls touseParams(),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 asfalse.
ChatRoomListResponseDtocurrently has no unread fields, but Line 32 setsunread: falseand this value is passed directly to UI insrc/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
⛔ Files ignored due to path filters (1)
src/assets/icons/social/trash.svgis excluded by!**/*.svg
📒 Files selected for processing (12)
src/features/social/api/chatroom.tssrc/features/social/common/SocialSearch.tsxsrc/features/social/hooks/useChatRoomsViewModel.tssrc/features/social/index.tssrc/features/social/types/chatroom.tssrc/features/social/ui/SocialList.tsxsrc/pages/manager/social/index.tsxsrc/shared/stores/useDocStore.tssrc/shared/ui/common/Docbar.tsxsrc/shared/ui/common/FloatingActionButton.tsxsrc/shared/ui/manager/social/SocialCategory.tsxsrc/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
| function toRelativeTime(dateString: string): string { | ||
| const date = new Date(dateString) | ||
| if (Number.isNaN(date.getTime())) return '-' | ||
| return `${formatDistanceToNowStrict(date, { locale: ko })} 전` | ||
| } |
There was a problem hiding this comment.
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.
| 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.
| const ACTION_WIDTH = 72 | ||
| const OPEN_THRESHOLD = ACTION_WIDTH * 0.45 |
There was a problem hiding this comment.
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.
| 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', | ||
| } |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
src/features/social/ui/SocialList.tsx (1)
20-21:⚠️ Potential issue | 🟡 MinorSwipe 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.45Also 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
📒 Files selected for processing (1)
src/features/social/ui/SocialList.tsx
| {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> |
There was a problem hiding this comment.
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.
| {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.
| aria-label="삭제" | ||
| > | ||
| 삭제 | ||
| <img src={TrashIcon} alt="삭제" /> |
There was a problem hiding this comment.
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.
| 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.
ID
변경 내용
구현 사항
구현 시연 (필요 시)
참고 사항 (필요 시)
Summary by CodeRabbit
New Features
Improvements