Skip to content

feat: 프로필 통계와 채팅 신고·차단 기능 추가#26

Merged
aryu1217 merged 11 commits into
mainfrom
feat/profile-stats-moderation-modals
Jul 12, 2026
Merged

feat: 프로필 통계와 채팅 신고·차단 기능 추가#26
aryu1217 merged 11 commits into
mainfrom
feat/profile-stats-moderation-modals

Conversation

@aryu1217

@aryu1217 aryu1217 commented Jul 12, 2026

Copy link
Copy Markdown
Member

변경 내용

  • 공개/설정 프로필에 실제 큐잉 횟수와 음악력을 표시하고, 이용 시간은 API 추가 전까지 기존 하드코딩 UI를 유지했습니다.
  • 음악력 조회·추천, 사용자 차단, 채팅 메시지 신고 API와 React Query 캐시 경계를 구현했습니다.
  • ChatArea에 회원·비회원·구형 메시지 조건별 신고/차단 메뉴와 접근 가능한 모달을 연결했습니다.
  • 차단 성공 즉시 해당 사용자의 기존·신규 채팅을 숨기고, 새로고침 후 서버 차단 안내 메시지도 표시하지 않도록 했습니다.
  • 음악력 추천·비추천 컨트롤을 프로필 패널 우측 하단으로 이동했습니다.
  • Vitest와 Testing Library를 도입하고 CI를 npm ci → lint → test → build 순서로 확장했습니다.
  • 기능 단위 한국어 Conventional Commit 정책을 하네스에 추가했습니다.

변경 이유

프로필의 더미 통계를 실제 서버 데이터로 교체하고, 채팅에서 부적절한 사용자와 메시지를 직접 신고·차단할 수 있는 관리 흐름이 필요했습니다. API payload, React Query cache, 모달 pending/error, 메시지 식별자 호환이 한 흐름에서 어긋나지 않도록 자동 테스트와 독립 QA를 함께 추가했습니다.

영향 범위

  • UI / 사용자 흐름
  • API 요청·응답 계약
  • React Query 캐시
  • WebSocket / 실시간 상태
  • 인증 / 보안
  • 문서 / 개발 도구

검증

  • npm run lint
  • npm run test — 12 files, 36 tests pass
  • npm run build
  • 관련 자동화 시나리오 확인
  • 실패·로딩·빈 상태 확인

검증한 주요 경계:

  • 음악력 GET/POST 경로와 추천 후 음악력·공개 프로필 캐시 동기화
  • 추천 가능/본인/비로그인/이미 추천/조회 실패 버튼 조건
  • 차단 확인·완료·실패·중복 제출 방지와 팔로우·검색 캐시 무효화
  • 차단 성공 직후 sender slug 기반 채팅 제거, 이후 실시간 메시지 제거, 새로고침 후 차단 안내 센티널 제거
  • 신고 다중 선택, 화면 순서 줄바꿈 payload, 성공 닫기, 실패 선택 유지
  • 본인·회원·비회원·구형 메시지의 관리 메뉴 조건과 outside/Escape/scroll 닫기

기능별 커밋

  1. chore: 기능 단위 한국어 커밋 정책 추가 — 배포 하네스와 실행 계획
  2. test: 프론트 컴포넌트 테스트 환경 추가 — Vitest/Testing Library/CI
  3. feat: 프로필 음악력과 큐잉 통계 표시 — 프로필 통계, 음악력 API·캐시·조건 테스트
  4. feat: 사용자 차단 기능 추가 — 차단 API·캐시·모달 테스트
  5. feat: 채팅 메시지 신고 기능 추가 — 신고 API·payload·모달 테스트
  6. feat: 채팅 메시지 관리 메뉴 연결 — 메시지 조건별 메뉴·닫힘·포커스 테스트
  7. fix: 차단 채팅과 프로필 UI 후속 수정 — CodeRabbit 리뷰, 차단 메시지 즉시 제거, 이용 시간/음악력 UI 후속 변경

리뷰 포인트

  • recommendedByMe === false가 확인된 경우에만 추천 버튼을 활성화하는 fail-closed 조건
  • 차단 성공 시 follow/search query invalidation 범위
  • 신고 사유를 화면 순서대로 줄바꿈한 reason payload와 비공개 방 헤더
  • ChatArea가 소유하는 단일 메뉴/모달 대상 상태와 식별자별 액션 판정
  • 차단 안내 메시지는 현재 서버 응답에 관찰된 차단된 사용자의 채팅입니다 센티널을 기준으로 숨기는 방어 로직

위험 및 후속 작업

  • 신규 API 계약은 staging 실응답으로 검증되지 않았습니다.
  • 신고 사유의 피그마 원문이 제공되지 않아 네 문구를 상수로 격리했습니다. 디자인 원문 확인 시 상수 대조가 필요합니다.
  • 메뉴는 Tab/Escape 접근이 가능하지만 완전한 ARIA menu 방향키 패턴은 후속 보강 여지가 있습니다.
  • 음악력 추천 취소 API가 없어 아래 화살표는 사유 안내와 함께 비활성화했습니다.
  • Vercel 외부 배포 check는 실패 상태이며 GitHub Actions의 lint/test/build는 통과했습니다.

추적 정보

  • Execution plan: docs/exec-plans/active/2026-07-12-profile-stats-moderation/
  • Selected skills: queuing-feature-delivery, queuing-orchestrator, queuing-api-boundary, queuing-ui-flow, frontend-architecture-guardrails, queuing-qa-reviewer, github:yeet
  • QA result: 초기 독립 리뷰와 PR 후속 리뷰 모두 targeted fix 후 pass

화면 변경

프로필 통계와 채팅 관리 메뉴/모달 UI가 변경되었습니다. 로컬 자동 테스트와 production build로 검증했으며 스크린샷은 첨부하지 않았습니다.

@vercel

vercel Bot commented Jul 12, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
queuing Ready Ready Preview, Comment Jul 12, 2026 3:09pm

@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@aryu1217, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 42 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d97e5bdf-b27c-4d87-a1b2-3e13f78c9275

📥 Commits

Reviewing files that changed from the base of the PR and between c7b0142 and 260da8d.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml, !**/pnpm-lock.yaml
📒 Files selected for processing (7)
  • docs/exec-plans/active/2026-07-12-profile-stats-moderation/delivery-state.md
  • docs/exec-plans/active/2026-07-12-profile-stats-moderation/handoff.md
  • docs/exec-plans/active/2026-07-12-profile-stats-moderation/qa-report.md
  • docs/exec-plans/active/2026-07-12-profile-stats-moderation/review-findings.md
  • docs/exec-plans/active/README.md
  • src/features/room/profile/ui/RoomProfilePanel.test.tsx
  • src/features/room/profile/ui/RoomProfilePanel.tsx
📝 Walkthrough

Walkthrough

프로필 통계를 실제 데이터로 표시하고 음악력 조회·추천을 추가했다. 채팅에는 사용자 차단·메시지 신고 모달과 관리 메뉴가 연결됐으며, 관련 API·캐시 처리·접근성 동작·테스트 및 CI 검증 절차가 보강됐다.

Changes

프로필 통계 및 음악력

Layer / File(s) Summary
통계 계약과 음악력 API
src/features/user/..., src/shared/lib/formatOptionalStat.ts
사용자 통계 타입, 음악력 조회·추천 API, React Query 키와 캐시 갱신 훅이 추가됐다.
프로필·설정 통합
src/features/room/profile/..., src/features/settings/...
프로필과 설정 화면이 음악력·큐잉 횟수를 포맷해 표시하고 추천 상태와 오류를 처리한다.

사용자 차단

Layer / File(s) Summary
차단 API와 mutation
src/features/follow/blocked/api/*, src/features/follow/blocked/hooks/*, src/features/follow/model/queryKeys.ts
인코딩된 사용자 slug로 차단 요청을 보내고 팔로우·검색 캐시를 무효화한다.
차단 모달
src/features/follow/blocked/ui/*
확인·진행·성공·실패 상태와 포커스 복원, 중복 제출 방지를 처리한다.

채팅 신고

Layer / File(s) Summary
신고 계약과 요청
src/features/room/chat/api/*, src/features/room/chat/constants/*, src/features/room/chat/hooks/*
신고 사유를 화면 순서대로 결합하고 메시지 식별자·방 비밀번호와 함께 신고 API로 전송한다.
신고 모달
src/features/room/chat/ui/ReportChatMessageModal*
복수 사유 선택, 요청 중 잠금, 오류 표시, 성공 후 닫힘과 포커스 복원을 구현한다.

채팅 관리 메뉴

Layer / File(s) Summary
메시지 액션 판정과 메뉴 UI
src/features/room/chat/model/*, src/features/room/chat/ui/ChatArea*
작성자·식별자에 따라 신고·차단 메뉴를 표시하고 외부 클릭, Escape, 스크롤 닫힘과 포커스 복원을 처리한다. 차단된 발신자와 서버 안내 메시지도 숨긴다.
화면 호출부와 검증
src/features/room/page/ui/RoomPlaybackScreen.tsx, src/features/room/chat/ui/ChatArea.test.tsx
모바일·데스크톱 채팅 영역에 사용자·방 정보를 전달하고 메뉴, 모달, 차단 후 숨김 동작을 검증한다.

전달 및 테스트 기반

Layer / File(s) Summary
기능별 전달 정책
.agents/skills/..., docs/exec-plans/active/..., .github/pull_request_template.md
기능별 커밋 계획, API·UI 계약, QA·handoff 상태와 PR 작성 항목을 문서화했다.
테스트 실행과 CI
package.json, vitest.config.ts, vitest.setup.ts, .github/workflows/ci.yml, src/shared/test/*
Vitest/jsdom 환경과 test 스크립트를 추가하고 CI에서 lint, test, build를 순서대로 실행한다.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant ChatArea
  participant BlockUserModal
  participant BlockUserAPI
  participant ChatMessageList
  User->>ChatArea: 메시지 관리 메뉴 선택
  ChatArea->>BlockUserModal: 차단 대상 표시
  BlockUserModal->>BlockUserAPI: 사용자 차단 요청
  BlockUserAPI-->>BlockUserModal: 성공 또는 오류
  BlockUserModal-->>ChatArea: 완료 상태와 닫힘 전달
  ChatArea->>ChatMessageList: 차단 sender slug 필터 적용
Loading

Possibly related PRs

🚥 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
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.
Title check ✅ Passed 제목이 프로필 통계와 채팅 신고·차단 추가라는 핵심 변경을 간결하게 잘 요약합니다.
Description check ✅ Passed 템플릿의 주요 섹션을 대부분 갖추고 있으며 변경 이유, 검증, 위험, 추적 정보까지 구체적으로 작성했습니다.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/profile-stats-moderation-modals

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.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request configures a frontend testing environment using Vitest, jsdom, and Testing Library, and establishes a Korean Conventional Commit policy. It replaces dummy profile statistics with actual music power and queuing count data, removing the usage time field. Additionally, it implements APIs, React Query hooks, and accessible modal dialogs for retrieving/recommending music power, blocking users, and reporting chat messages, which are integrated into the chat area's message management menu. Comprehensive unit and integration tests have been added for these features. There are no review comments, so I have no feedback to provide.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

@aryu1217

Copy link
Copy Markdown
Member Author

@codex review

@aryu1217
aryu1217 marked this pull request as ready for review July 12, 2026 13:36

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 9

🧹 Nitpick comments (4)
src/features/user/profile/api/fetchMusicPower.ts (1)

6-14: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

DTO → Domain 매핑 분리를 권장

MusicPowerResponsetargetUserSlug 필드는 API 응답 메타데이터로, UI에서 직접 소비할 필요가 없습니다. useRecommendMusicPower.onSuccess에서 캐시 키로 사용하기 위해 포함된 필드이지만, API 반환 타입을 도메인 타입(예: MusicPowermusicPower, recommendedByMe만 포함)으로 정규화하고 targetUserSlug는 별도 DTO 타입에 두면 계층 책임이 더 명확해집니다.

As per coding guidelines **/api/**/*.ts*: "반환 타입은 Domain(User 등)으로 정규화" — 현재 MusicPowerResponse가 DTO와 도메인 타입을 겸하고 있습니다.

♻️ 제안: DTO와 도메인 타입 분리
 // model/types.ts
 export type MusicPowerResponse = {
   musicPower: number;
   recommendedByMe: boolean;
   targetUserSlug: string;
 };
+
+// UI가 소비하는 도메인 타입
+export type MusicPower = {
+  musicPower: number;
+  recommendedByMe: boolean;
+};

 // api/fetchMusicPower.ts
+import type { MusicPower } from "../model/types";
+
 export async function fetchMusicPower(
   userSlug: string,
-): Promise<MusicPowerResponse> {
+): Promise<MusicPower> {
   const { data } = await axiosInstance.get<ApiResponse<MusicPowerResponse>>(
     `/api/v1/user-profiles/${encodeURIComponent(userSlug)}/music-power`,
   );
-  return unwrapApiResponse(data);
+  const { targetUserSlug: _, ...domain } = unwrapApiResponse(data);
+  return domain;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/features/user/profile/api/fetchMusicPower.ts` around lines 6 - 14,
Separate the DTO and domain representations used by fetchMusicPower: keep
targetUserSlug in a dedicated API response DTO, but return the domain type
MusicPower containing only musicPower and recommendedByMe. Update
fetchMusicPower to unwrap the DTO and explicitly map it to MusicPower, and
adjust useRecommendMusicPower.onSuccess to obtain the target slug from its
existing request context rather than the normalized API result.

Source: Path instructions

src/features/room/profile/ui/RoomProfilePanel.tsx (1)

21-23: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

formatStat 헬퍼가 ProfileStats.tsx와 완전히 동일하게 중복 정의되어 있습니다.

두 feature(room/profile, settings)에 동일한 로직이 각각 존재합니다. 공유 UI 헬퍼는 src/shared에 두어야 합니다.

♻️ 제안: 공유 헬퍼로 추출
// src/shared/lib/formatStat.ts
export function formatStat(value: number | undefined) {
  return typeof value === "number" ? value.toLocaleString("ko-KR") : "-";
}
-function formatStat(value: number | undefined) {
-  return typeof value === "number" ? value.toLocaleString("ko-KR") : "-";
-}
+import { formatStat } from "`@/src/shared/lib/formatStat`";
"Shared API/UI helpers live in `src/shared`" 가이드라인에 따라 제안합니다.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/features/room/profile/ui/RoomProfilePanel.tsx` around lines 21 - 23, 중복
정의된 formatStat 헬퍼를 src/shared의 공유 유틸리티로 추출하고, RoomProfilePanel과 ProfileStats에서
해당 공유 함수를 import해 사용하도록 변경하세요. 기존 숫자 포맷팅 및 undefined 처리 동작은 그대로 유지하세요.

Source: Coding guidelines

src/features/room/profile/ui/RoomProfilePanel.test.tsx (1)

42-46: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

as unknown as 이중 캐스팅으로 목 타입 검사가 무력화됩니다.

recommendMutationUseMutationResult의 일부 필드만 가지고 있어 unknown을 거쳐 강제 캐스팅했습니다. Partial<ReturnType<typeof useRecommendMusicPower>>로 선언하면 unknown 없이 단일 캐스팅으로 타입 안전성을 유지할 수 있습니다.

♻️ 제안 수정
-const recommendMutation = {
+const recommendMutation: Partial<ReturnType<typeof useRecommendMusicPower>> = {
   error: null,
   isPending: false,
   mutate: vi.fn(),
 };
-    vi.mocked(useRecommendMusicPower).mockReturnValue(
-      recommendMutation as unknown as ReturnType<typeof useRecommendMusicPower>,
-    );
+    vi.mocked(useRecommendMusicPower).mockReturnValue(
+      recommendMutation as ReturnType<typeof useRecommendMusicPower>,
+    );
"any/과도한 as 캐스팅을 지양" 지침에 따라 제안합니다.

Also applies to: 85-87

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/features/room/profile/ui/RoomProfilePanel.test.tsx` around lines 42 - 46,
Update the recommendMutation mock used by the RoomProfilePanel tests to be
declared as Partial<ReturnType<typeof useRecommendMusicPower>>, then replace the
existing as unknown as double casts at both referenced usages with a single
appropriate cast. Preserve the mock’s existing fields and behavior while
avoiding unknown or excessive type assertions.

Source: Path instructions

src/features/follow/blocked/ui/BlockUserModal.tsx (1)

20-20: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

isCompleteblockUser.isSuccess의 파생 복제 상태입니다.

useBlockUser()(useMutation)가 이미 isSuccess를 제공하고, resetBlockUser() 호출 시 isSuccess도 함께 초기화되므로 별도의 isComplete state와 onSuccess: () => setIsComplete(true) 콜백은 중복입니다. 파생 가능한 값을 별도 state로 유지하면 두 상태가 어긋날 여지(예: mutate 재호출 순서)를 만듭니다.

♻️ 제안하는 리팩터
 export default function BlockUserModal({ onClose, target }: Props) {
-  const [isComplete, setIsComplete] = useState(false);
   const closeButtonRef = useRef<HTMLButtonElement>(null);
   const confirmButtonRef = useRef<HTMLButtonElement>(null);
   const previousFocusRef = useRef<HTMLElement | null>(null);
   const blockUser = useBlockUser();
   const resetBlockUser = blockUser.reset;
   const open = Boolean(target);
   const handleClose = useCallback(() => {
     if (!blockUser.isPending) {
-      setIsComplete(false);
       resetBlockUser();
       onClose();
     }
   }, [blockUser.isPending, onClose, resetBlockUser]);
@@
   useEffect(() => {
-    if (isComplete) {
+    if (blockUser.isSuccess) {
       closeButtonRef.current?.focus();
     }
-  }, [isComplete]);
+  }, [blockUser.isSuccess]);
@@
   const handleConfirm = () => {
     if (blockUser.isPending) {
       return;
     }

-    blockUser.mutate(target.slug, {
-      onSuccess: () => setIsComplete(true),
-    });
+    blockUser.mutate(target.slug);
   };
@@
-          {isComplete ? (
+          {blockUser.isSuccess ? (

As per path instructions, "파생 값은 state로 두지 말고 계산(useMemo는 필요할 때만)."

Also applies to: 27-33, 52-56, 62-70, 90-106

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/features/follow/blocked/ui/BlockUserModal.tsx` at line 20, Remove the
redundant isComplete state and its setIsComplete updates in BlockUserModal. Use
blockUser.isSuccess from useBlockUser() directly for completion checks and
preserve resetBlockUser() as the mutation reset path, without introducing
separate derived state.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/features/follow/blocked/ui/BlockUserModal.test.tsx`:
- Around line 58-78: Update the test case “요청 중 중복 제출과 닫기를 막는다” to await the
resolution of the mocked blockUser promise and flush its resulting React state
updates inside act. Ensure the completion handler and pending-state cleanup
finish before the test exits and before RTL cleanup runs, while preserving the
existing assertions and interaction flow.

In `@src/features/follow/blocked/ui/BlockUserModal.tsx`:
- Around line 52-56: Update the focus management effect around isComplete in
BlockUserModal to focus confirmButtonRef.current when the blocking operation
fails, while retaining closeButtonRef.current?.focus() for successful
completion. Use the existing blockUser.isPending and failure state to
distinguish retryable failures, ensuring keyboard and screen-reader users can
immediately retry.

In `@src/features/room/chat/model/chatMessages.ts`:
- Around line 179-196: Update getChatMessageManagementActions to return no
management actions when currentUser is null, before evaluating the message
ownership or messageKey/senderSlug checks. Ensure ChatArea’s handleBlock and
handleReport also guard against a missing currentUser, and add coverage in
chatMessages.test.ts for unauthenticated users receiving neither action.

In `@src/features/room/chat/ui/ChatArea.module.css`:
- Around line 72-84: Update the .menu positioning behavior so management menus
near the bottom of the .list scroll container are not clipped by .list or .root
overflow; either render the menu through a portal outside those containers or
implement placement that opens upward when insufficient space remains below.

In `@src/features/room/chat/ui/ChatArea.tsx`:
- Around line 93-105: Update the management menu button aria-label in ChatArea
so it includes a stable message-specific identifier, such as a concise
message-content fragment or timestamp, alongside message.senderNickname. Ensure
each rendered message menu has a distinguishable accessible name while
preserving the existing Korean label context.

In `@src/features/room/chat/ui/ReportChatMessageModal.module.css`:
- Around line 66-76: Replace the deprecated clip declaration in .visuallyHidden
with clip-path: inset(50%), preserving the existing visually hidden behavior and
all other accessibility-related styles.

In `@src/features/room/profile/ui/RoomProfilePanel.tsx`:
- Around line 91-117: Update recommendationLabel in RoomProfilePanel so it
explicitly handles !targetSlug before the musicPowerQuery.data fallback, using
the same “준비 중” wording as the follow button for the unavailable slug state;
preserve the existing labels and ordering for login, loading, query failure,
already-recommended, and pending states.

In `@src/features/settings/ui/ProfileSettingsTab.module.css`:
- Around line 258-261: Update the mobile `profileStats` styles under `@media
(max-width: 760px)` to use `grid-template-columns: 1fr` for a single-column
layout, and remove the obsolete `flex: 0 0 auto` declaration from the grid-based
`profileStats` rule.

In `@src/features/user/profile/api/musicPower.test.ts`:
- Around line 35-44: Update the recommendMusicPower test to also assert that the
function returns the mocked response payload with musicPower set to 13 and
recommendedByMe set to true, while preserving the existing encoded-URL request
assertion.

---

Nitpick comments:
In `@src/features/follow/blocked/ui/BlockUserModal.tsx`:
- Line 20: Remove the redundant isComplete state and its setIsComplete updates
in BlockUserModal. Use blockUser.isSuccess from useBlockUser() directly for
completion checks and preserve resetBlockUser() as the mutation reset path,
without introducing separate derived state.

In `@src/features/room/profile/ui/RoomProfilePanel.test.tsx`:
- Around line 42-46: Update the recommendMutation mock used by the
RoomProfilePanel tests to be declared as Partial<ReturnType<typeof
useRecommendMusicPower>>, then replace the existing as unknown as double casts
at both referenced usages with a single appropriate cast. Preserve the mock’s
existing fields and behavior while avoiding unknown or excessive type
assertions.

In `@src/features/room/profile/ui/RoomProfilePanel.tsx`:
- Around line 21-23: 중복 정의된 formatStat 헬퍼를 src/shared의 공유 유틸리티로 추출하고,
RoomProfilePanel과 ProfileStats에서 해당 공유 함수를 import해 사용하도록 변경하세요. 기존 숫자 포맷팅 및
undefined 처리 동작은 그대로 유지하세요.

In `@src/features/user/profile/api/fetchMusicPower.ts`:
- Around line 6-14: Separate the DTO and domain representations used by
fetchMusicPower: keep targetUserSlug in a dedicated API response DTO, but return
the domain type MusicPower containing only musicPower and recommendedByMe.
Update fetchMusicPower to unwrap the DTO and explicitly map it to MusicPower,
and adjust useRecommendMusicPower.onSuccess to obtain the target slug from its
existing request context rather than the normalized API result.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0f9f2775-f985-4b9c-bb4b-e348373d196d

📥 Commits

Reviewing files that changed from the base of the PR and between 0703b7c and b14633b.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (55)
  • .agents/skills/queuing-feature-delivery/SKILL.md
  • .agents/skills/queuing-feature-delivery/references/delivery-policy.md
  • .github/pull_request_template.md
  • .github/workflows/ci.yml
  • docs/exec-plans/active/2026-07-12-profile-stats-moderation/api-contract.md
  • docs/exec-plans/active/2026-07-12-profile-stats-moderation/change-summary.md
  • docs/exec-plans/active/2026-07-12-profile-stats-moderation/delivery-state.md
  • docs/exec-plans/active/2026-07-12-profile-stats-moderation/handoff.md
  • docs/exec-plans/active/2026-07-12-profile-stats-moderation/implementation-notes.md
  • docs/exec-plans/active/2026-07-12-profile-stats-moderation/plan.md
  • docs/exec-plans/active/2026-07-12-profile-stats-moderation/qa-report.md
  • docs/exec-plans/active/2026-07-12-profile-stats-moderation/request-summary.md
  • docs/exec-plans/active/2026-07-12-profile-stats-moderation/ui-flow.md
  • docs/exec-plans/active/README.md
  • package.json
  • src/features/follow/blocked/api/blockUser.test.ts
  • src/features/follow/blocked/api/blockUser.ts
  • src/features/follow/blocked/hooks/useBlockUser.test.tsx
  • src/features/follow/blocked/hooks/useBlockUser.ts
  • src/features/follow/blocked/ui/BlockUserModal.module.css
  • src/features/follow/blocked/ui/BlockUserModal.test.tsx
  • src/features/follow/blocked/ui/BlockUserModal.tsx
  • src/features/follow/model/queryKeys.ts
  • src/features/room/chat/api/reportChatMessage.test.ts
  • src/features/room/chat/api/reportChatMessage.ts
  • src/features/room/chat/constants/reportReasons.ts
  • src/features/room/chat/hooks/useReportChatMessage.ts
  • src/features/room/chat/model/chatMessages.test.ts
  • src/features/room/chat/model/chatMessages.ts
  • src/features/room/chat/ui/ChatArea.module.css
  • src/features/room/chat/ui/ChatArea.test.tsx
  • src/features/room/chat/ui/ChatArea.tsx
  • src/features/room/chat/ui/ReportChatMessageModal.module.css
  • src/features/room/chat/ui/ReportChatMessageModal.test.tsx
  • src/features/room/chat/ui/ReportChatMessageModal.tsx
  • src/features/room/page/ui/RoomPlaybackScreen.tsx
  • src/features/room/profile/ui/RoomProfilePanel.module.css
  • src/features/room/profile/ui/RoomProfilePanel.test.tsx
  • src/features/room/profile/ui/RoomProfilePanel.tsx
  • src/features/settings/ui/ProfileSettingsTab.module.css
  • src/features/settings/ui/ProfileSettingsTab.tsx
  • src/features/settings/ui/components/ProfileStats.test.tsx
  • src/features/settings/ui/components/ProfileStats.tsx
  • src/features/user/model/queryKeys.ts
  • src/features/user/model/types.ts
  • src/features/user/profile/api/fetchMusicPower.ts
  • src/features/user/profile/api/musicPower.test.ts
  • src/features/user/profile/api/recommendMusicPower.ts
  • src/features/user/profile/hooks/useMusicPower.ts
  • src/features/user/profile/hooks/useRecommendMusicPower.test.tsx
  • src/features/user/profile/hooks/useRecommendMusicPower.ts
  • src/features/user/profile/model/types.ts
  • src/shared/test/testingLibrarySetup.test.tsx
  • vitest.config.ts
  • vitest.setup.ts

Comment thread src/features/follow/blocked/ui/BlockUserModal.test.tsx
Comment thread src/features/follow/blocked/ui/BlockUserModal.tsx Outdated
Comment thread src/features/room/chat/model/chatMessages.ts
Comment thread src/features/room/chat/ui/ChatArea.module.css
Comment thread src/features/room/chat/ui/ChatArea.tsx
Comment thread src/features/room/chat/ui/ReportChatMessageModal.module.css
Comment thread src/features/room/profile/ui/RoomProfilePanel.tsx
Comment on lines +258 to +261
display: grid;
flex: 0 0 auto;
align-items: flex-start;
justify-content: space-between;
grid-template-columns: repeat(2, minmax(0, 1fr));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== CSS outline ==\n'
ast-grep outline src/features/settings/ui/ProfileSettingsTab.module.css --view expanded || true

printf '\n== CSS relevant lines ==\n'
sed -n '240,380p' src/features/settings/ui/ProfileSettingsTab.module.css

printf '\n== ProfileStats usages ==\n'
rg -n "profileStats|statItem|ProfileStats" src/features/settings -S

Repository: Queuing-org/frontend

Length of output: 3967


모바일 레이아웃을 grid 기준으로 같이 수정하세요. profileStatsgrid로 바꿨는데 @media (max-width: 760px)는 아직 flex-direction: column만 바꾸고 있어서 모바일에서 1열 스택이 되지 않습니다. grid-template-columns: 1fr를 추가하고, 더 이상 의미 없는 flex: 0 0 auto는 제거하세요.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/features/settings/ui/ProfileSettingsTab.module.css` around lines 258 -
261, Update the mobile `profileStats` styles under `@media (max-width: 760px)`
to use `grid-template-columns: 1fr` for a single-column layout, and remove the
obsolete `flex: 0 0 auto` declaration from the grid-based `profileStats` rule.

Comment thread src/features/user/profile/api/musicPower.test.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/features/room/profile/ui/RoomProfilePanel.tsx`:
- Around line 172-175: RoomProfilePanel의 이용 시간 placeholder를 ProfileStats.tsx와
동일하게 “개발중입니다.”로 통일하세요. 해당 컴포넌트의 cardValue 텍스트만 수정하고 주변 구조는 유지하세요.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6ac0fab2-ca53-4f66-a391-a858e893753d

📥 Commits

Reviewing files that changed from the base of the PR and between b14633b and c7b0142.

📒 Files selected for processing (23)
  • docs/exec-plans/active/2026-07-12-profile-stats-moderation/change-summary.md
  • docs/exec-plans/active/2026-07-12-profile-stats-moderation/delivery-state.md
  • docs/exec-plans/active/2026-07-12-profile-stats-moderation/implementation-notes.md
  • docs/exec-plans/active/2026-07-12-profile-stats-moderation/plan.md
  • docs/exec-plans/active/2026-07-12-profile-stats-moderation/qa-report.md
  • docs/exec-plans/active/2026-07-12-profile-stats-moderation/request-summary.md
  • docs/exec-plans/active/2026-07-12-profile-stats-moderation/review-findings.md
  • docs/exec-plans/active/README.md
  • src/features/follow/blocked/ui/BlockUserModal.test.tsx
  • src/features/follow/blocked/ui/BlockUserModal.tsx
  • src/features/room/chat/model/chatMessages.test.ts
  • src/features/room/chat/model/chatMessages.ts
  • src/features/room/chat/ui/ChatArea.module.css
  • src/features/room/chat/ui/ChatArea.test.tsx
  • src/features/room/chat/ui/ChatArea.tsx
  • src/features/room/chat/ui/ReportChatMessageModal.module.css
  • src/features/room/profile/ui/RoomProfilePanel.module.css
  • src/features/room/profile/ui/RoomProfilePanel.test.tsx
  • src/features/room/profile/ui/RoomProfilePanel.tsx
  • src/features/settings/ui/components/ProfileStats.test.tsx
  • src/features/settings/ui/components/ProfileStats.tsx
  • src/features/user/profile/api/musicPower.test.ts
  • src/shared/lib/formatOptionalStat.ts
🚧 Files skipped from review as they are similar to previous changes (9)
  • docs/exec-plans/active/2026-07-12-profile-stats-moderation/change-summary.md
  • src/features/user/profile/api/musicPower.test.ts
  • docs/exec-plans/active/2026-07-12-profile-stats-moderation/request-summary.md
  • src/features/room/chat/ui/ReportChatMessageModal.module.css
  • src/features/room/profile/ui/RoomProfilePanel.test.tsx
  • src/features/follow/blocked/ui/BlockUserModal.tsx
  • src/features/follow/blocked/ui/BlockUserModal.test.tsx
  • docs/exec-plans/active/2026-07-12-profile-stats-moderation/plan.md
  • src/features/room/chat/ui/ChatArea.tsx

Comment thread src/features/room/profile/ui/RoomProfilePanel.tsx
@aryu1217
aryu1217 merged commit 3ff25c1 into main Jul 12, 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.

1 participant