Skip to content

feat: 곡 오류 신고 기능 추가 (#217)#218

Merged
GulSam00 merged 2 commits intodevelopfrom
feat/217-addSongReport
May 2, 2026
Merged

feat: 곡 오류 신고 기능 추가 (#217)#218
GulSam00 merged 2 commits intodevelopfrom
feat/217-addSongReport

Conversation

@GulSam00
Copy link
Copy Markdown
Owner

@GulSam00 GulSam00 commented May 2, 2026

Summary

  • 검색 결과 카드에 오류 신고 모달 추가 — 가수 번역 / 곡 번역 / TJ 번호 / 금영 번호 4가지 카테고리
  • 라이브 미리보기: 선택한 카테고리 행이 강조되고 입력값이 원본 → 새 값 형태로 즉시 반영
  • 숫자 카테고리 (num_tj / num_ky)는 5자리 숫자만 입력 가능, 「데이터 없음」 체크박스로 suggested_value: null 제출
  • BFF: POST /api/songs/report → Supabase song_reports 테이블 insert
  • TanStack Query mutation 훅 (useReportSongMutation) + ReportCategory / ReportStatus 타입 정의

DB 작업 필요

ALTER TABLE song_reports ALTER COLUMN suggested_value DROP NOT NULL;
ALTER TABLE song_reports
  ADD COLUMN status TEXT NOT NULL DEFAULT 'pending'
  CHECK (status IN ('pending', 'applied', 'rejected'));

status 컬럼은 다음 작업 (info 페이지 신고 목록/삭제)에서 활용 예정

Test plan

  • 비로그인 상태에서 신고 버튼 → 로그인 유도 토스트
  • 카테고리 미선택 제출 → "신고 항목을 선택해주세요" 토스트
  • 텍스트 카테고리 (가수/곡 번역): 100자 제한, 빈 입력 거부
  • 숫자 카테고리 (TJ/금영): 숫자만 입력, 5자 제한, 「데이터 없음」 체크 시 input disabled + null 제출
  • 카테고리 전환 시 입력값/체크박스 초기화
  • 카드 미리보기: 선택 항목 강조 + → 새 값 표시, 입력 후 토글 시 모달 높이 변동 없음
  • 중복 신고 시 409 에러 → "이미 신고하신 항목입니다" 토스트

Closes #217

🤖 Generated with Claude Code

GulSam00 added 2 commits May 2, 2026 21:34
- 검색 결과 카드에 오류 신고 모달 추가 (가수/곡 번역, TJ/금영 번호 카테고리)
- 라이브 미리보기 카드 (선택된 항목 강조 + 입력값 → 새 값 표시)
- 숫자 카테고리는 5자리 숫자만 입력, "데이터 없음" 체크박스로 null 제출 가능
- POST /api/songs/report (BFF), TanStack Query mutation, ReportCategory/ReportStatus 타입 정의
@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented May 2, 2026

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

Project Deployment Actions Updated (UTC)
singcode Ready Ready Preview, Comment May 2, 2026 0:36am

@qodo-code-review
Copy link
Copy Markdown

Review Summary by Qodo

Add song error reporting feature with live preview and API integration

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Add song error reporting feature with modal dialog
  - Four report categories: artist translation, title translation, TJ number, KY number
  - Live preview card showing selected category highlight and new value
• Implement BFF API endpoint for song report submission
  - Validates input based on category type (text vs numeric)
  - Handles duplicate reports with 409 conflict response
• Add TanStack Query mutation hook and report type definitions
• Add Radix UI radio group component for category selection
Diagram
flowchart LR
  A["SearchResultCard"] -->|"opens"| B["ReportSongModal"]
  B -->|"selects category"| C["Live Preview Card"]
  C -->|"shows highlight"| D["Selected Field"]
  B -->|"submits report"| E["useReportSongMutation"]
  E -->|"POST /api/songs/report"| F["BFF API Route"]
  F -->|"validates input"| G["Category Validation"]
  G -->|"inserts to DB"| H["song_reports Table"]
  H -->|"409 conflict"| I["Duplicate Check"]
Loading

Grey Divider

File Changes

1. apps/web/src/app/api/songs/report/route.ts ✨ Enhancement +90/-0

BFF API endpoint for song report submission

apps/web/src/app/api/songs/report/route.ts


2. apps/web/src/lib/api/reportSong.ts ✨ Enhancement +15/-0

API client function for song report

apps/web/src/lib/api/reportSong.ts


3. apps/web/src/queries/reportSongQuery.ts ✨ Enhancement +23/-0

TanStack Query mutation hook for reporting

apps/web/src/queries/reportSongQuery.ts


View more (9)
4. apps/web/src/types/report.ts ✨ Enhancement +25/-0

Report category and status type definitions

apps/web/src/types/report.ts


5. apps/web/src/components/ReportSongModal.tsx ✨ Enhancement +317/-0

Modal component with live preview and form validation

apps/web/src/components/ReportSongModal.tsx


6. apps/web/src/components/ui/radio-group.tsx ✨ Enhancement +45/-0

Radix UI radio group component wrapper

apps/web/src/components/ui/radio-group.tsx


7. apps/web/src/app/search/SearchResultCard.tsx ✨ Enhancement +43/-4

Add report button and modal integration to card

apps/web/src/app/search/SearchResultCard.tsx


8. apps/web/package.json Dependencies +1/-0

Add Radix UI radio group dependency

apps/web/package.json


9. apps/web/src/utils/getArtistAlias.ts Formatting +2/-1

Reorder imports for consistency

apps/web/src/utils/getArtistAlias.ts


10. apps/web/src/app/privacy/page.tsx Formatting +7/-4

Reformat text wrapping in privacy policy

apps/web/src/app/privacy/page.tsx


11. apps/web/public/sitemap-0.xml ⚙️ Configuration changes +2/-2

Update sitemap timestamps

apps/web/public/sitemap-0.xml


12. pnpm-lock.yaml Dependencies +34/-0

Update lock file with radio group dependency

pnpm-lock.yaml


Grey Divider

Qodo Logo

@qodo-code-review
Copy link
Copy Markdown

qodo-code-review Bot commented May 2, 2026

Code Review by Qodo

🐞 Bugs (4) 📘 Rule violations (1) 📎 Requirement gaps (2)

Context used

Grey Divider


Action required

1. Report modal missing textarea 📎 Requirement gap ≡ Correctness
Description
The new ReportSongModal UI does not include the required optional description textarea element;
it only provides an Input for suggested_value. This does not meet the modal UI requirements in
the checklist.
Code

apps/web/src/components/ReportSongModal.tsx[R267-304]

+        <div className="flex flex-col gap-2">
+          <Label htmlFor="report-suggested-value">올바른 정보</Label>
+          <Input
+            id="report-suggested-value"
+            placeholder={
+              isNumberMode
+                ? '5자리 숫자로 입력해주세요.'
+                : '어떤 내용으로 바뀌어야 하는지 적어주세요.'
+            }
+            value={isNoData ? NO_DATA_LABEL : suggestedValue}
+            onChange={e => handleSuggestedValueChange(e.target.value)}
+            maxLength={inputMaxLength}
+            inputMode={isNumberMode ? 'numeric' : undefined}
+            pattern={isNumberMode ? '[0-9]*' : undefined}
+            disabled={isNoData}
+            required
+          />
+          <Label
+            htmlFor="report-no-data"
+            className={cn(
+              'text-muted-foreground flex items-center gap-2 text-sm',
+              isNumberMode ? 'cursor-pointer' : 'invisible',
+            )}
+            aria-hidden={!isNumberMode}
+          >
+            <Checkbox
+              id="report-no-data"
+              checked={isNoData}
+              onCheckedChange={checked => handleNoDataChange(checked === true)}
+              disabled={!isNumberMode}
+              tabIndex={isNumberMode ? 0 : -1}
+            />
+            데이터 없음 (해당 노래방에 등록되지 않은 곡)
+          </Label>
+          <div className={cn('text-muted-foreground self-end text-xs', isNoData && 'invisible')}>
+            {suggestedValue.length} / {inputMaxLength}
+          </div>
+        </div>
Evidence
PR Compliance ID 1 requires the report dialog to include a (optional) description textarea, but
the added modal only renders an Input field and no textarea for description.

SearchResultCard에 오류 신고 UI(버튼/모달) 제공
apps/web/src/components/ReportSongModal.tsx[267-304]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The report modal is missing the required (optional) description `textarea` element.

## Issue Context
Checklist requires the modal to contain a single-select category list plus a description textarea (optional) and cancel/submit buttons.

## Fix Focus Areas
- apps/web/src/components/ReportSongModal.tsx[267-304]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. /api/reports endpoint missing 📎 Requirement gap ≡ Correctness
Description
The PR implements POST /api/songs/report instead of the required POST /api/reports. This breaks
the required BFF endpoint contract in the compliance checklist.
Code

apps/web/src/app/api/songs/report/route.ts[R14-20]

+export async function POST(request: Request): Promise<NextResponse<ApiResponse<void>>> {
+  try {
+    const supabase = await createClient();
+    const userId = await getAuthenticatedUser(supabase);
+
+    const { songId, category, suggested_value } = await request.json();
+
Evidence
PR Compliance ID 4 requires implementing POST /api/reports, but the added route handler is located
at src/app/api/songs/report/route.ts (therefore POST /api/songs/report).

BFF 엔드포인트 POST /api/reports 구현(클라이언트→BFF→Supabase)
apps/web/src/app/api/songs/report/route.ts[14-20]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The compliance checklist requires `POST /api/reports`, but the PR adds `POST /api/songs/report`.

## Issue Context
Next.js route handler path determines the public API route.

## Fix Focus Areas
- apps/web/src/app/api/songs/report/route.ts[14-20]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Added ui/radio-group.tsx file 📘 Rule violation ⚙ Maintainability
Description
A new shadcn/ui-style component file was added under apps/web/src/components/ui/, which is
disallowed by the checklist. This reduces upgradeability/maintainability of the vendor-like UI
layer.
Code

apps/web/src/components/ui/radio-group.tsx[R1-45]

+'use client';
+
+import * as RadioGroupPrimitive from '@radix-ui/react-radio-group';
+import { CircleIcon } from 'lucide-react';
+import * as React from 'react';
+
+import { cn } from '@/utils/cn';
+
+function RadioGroup({
+  className,
+  ...props
+}: React.ComponentProps<typeof RadioGroupPrimitive.Root>) {
+  return (
+    <RadioGroupPrimitive.Root
+      data-slot="radio-group"
+      className={cn('grid gap-3', className)}
+      {...props}
+    />
+  );
+}
+
+function RadioGroupItem({
+  className,
+  ...props
+}: React.ComponentProps<typeof RadioGroupPrimitive.Item>) {
+  return (
+    <RadioGroupPrimitive.Item
+      data-slot="radio-group-item"
+      className={cn(
+        'border-input text-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 aspect-square size-4 shrink-0 rounded-full border shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50',
+        className,
+      )}
+      {...props}
+    >
+      <RadioGroupPrimitive.Indicator
+        data-slot="radio-group-indicator"
+        className="relative flex items-center justify-center"
+      >
+        <CircleIcon className="fill-primary absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2" />
+      </RadioGroupPrimitive.Indicator>
+    </RadioGroupPrimitive.Item>
+  );
+}
+
+export { RadioGroup, RadioGroupItem };
Evidence
PR Compliance ID 7 forbids any changes under src/components/ui/, but this PR adds
radio-group.tsx in that directory.

CLAUDE.md
apps/web/src/components/ui/radio-group.tsx[1-45]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Files under `apps/web/src/components/ui/` must not be changed/added as part of feature work.

## Issue Context
This PR adds `apps/web/src/components/ui/radio-group.tsx` and imports it from feature code.

## Fix Focus Areas
- apps/web/src/components/ui/radio-group.tsx[1-45]
- apps/web/src/components/ReportSongModal.tsx[6-13]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

4. 숫자 자릿수 불일치 🐞 Bug ≡ Correctness
Description
ReportSongModal은 "5자리" 입력을 안내하지만 클라이언트/서버 검증은 1~5자리도 허용하여(빈 값만 거부) 1~4자리 번호가 그대로 song_reports에 저장될 수
있습니다. 이로 인해 사용자 기대와 다른 잘못된 신고 데이터가 누적됩니다.
Code

apps/web/src/app/api/songs/report/route.ts[R51-56]

+      if (isNumberCategory && !/^\d{1,5}$/.test(trimmed)) {
+        return NextResponse.json(
+          { success: false, error: 'Invalid number format' },
+          { status: 400 },
+        );
+      }
Evidence
UI는 숫자 모드에서 5자리 입력을 유도하지만(handleSuggestedValueChange는 최대 5자리로만 자르고, 제출 시 정확히 5자리인지 확인하지 않음), API도 숫자
카테고리에 대해 ^\d{1,5}$로 검증하여 1~4자리도 유효로 처리합니다. 따라서 부분 입력이 서버를 통과해 DB에 저장될 수 있습니다.

apps/web/src/components/ReportSongModal.tsx[111-147]
apps/web/src/components/ReportSongModal.tsx[271-283]
apps/web/src/app/api/songs/report/route.ts[51-57]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
숫자 카테고리(num_tj/num_ky) 신고 값이 UI 문구(5자리)와 달리 1~4자리도 통과/저장될 수 있습니다.

### Issue Context
- UI: 숫자 모드 placeholder가 "5자리 숫자로 입력"이지만, 길이 검증은 최대 길이만 제한합니다.
- API: 숫자 검증이 `^\d{1,5}$`라서 1~4자리도 허용합니다.

### Fix Focus Areas
- apps/web/src/components/ReportSongModal.tsx[125-140]
- apps/web/src/components/ReportSongModal.tsx[267-283]
- apps/web/src/app/api/songs/report/route.ts[51-57]

### Suggested fix
- 정책을 결정해 일관되게 맞추세요.
 - **정확히 5자리만 허용**이 맞다면:
   - 클라이언트: 숫자 모드 + !isNoData일 때 `trimmedSuggestedValue.length === 5` 체크 후 아니면 에러 토스트.
   - 서버: 정규식을 `^\d{5}$`로 변경.
 - **최대 5자리 허용**이 맞다면:
   - UI placeholder/가이드 문구를 "최대 5자리"로 수정(서버는 그대로 유지).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. 텍스트 길이 서버 미검증 🐞 Bug ⛨ Security
Description
report API는 suggested_value(텍스트 카테고리)에 대해 길이 제한을 서버에서 검증하지 않아, 클라이언트 제한을 우회한 과도한 길이의 값이 그대로 DB에 저장될
수 있습니다. 이는 저장공간/쿼리 비용 증가 및 데이터 품질 저하로 이어집니다.
Code

apps/web/src/app/api/songs/report/route.ts[R43-63]

+    } else if (typeof suggested_value === 'string') {
+      const trimmed = suggested_value.trim();
+      if (!trimmed) {
+        return NextResponse.json(
+          { success: false, error: 'Missing suggested_value' },
+          { status: 400 },
+        );
+      }
+      if (isNumberCategory && !/^\d{1,5}$/.test(trimmed)) {
+        return NextResponse.json(
+          { success: false, error: 'Invalid number format' },
+          { status: 400 },
+        );
+      }
+      normalizedSuggestedValue = trimmed;
+    } else {
+      return NextResponse.json(
+        { success: false, error: 'Invalid suggested_value' },
+        { status: 400 },
+      );
+    }
Evidence
클라이언트는 입력 컴포넌트 maxLength로 100자를 제한하지만, 서버는 trim/빈값/형식만 확인하고(텍스트 카테고리의 상한 검증 없음) 바로 insert합니다. 따라서 직접
API 호출로 긴 문자열을 보내면 서버가 이를 차단하지 않습니다.

apps/web/src/components/ReportSongModal.tsx[101-104]
apps/web/src/components/ReportSongModal.tsx[269-283]
apps/web/src/app/api/songs/report/route.ts[43-70]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
서버가 텍스트 카테고리(artist_translation/title_translation)의 `suggested_value` 최대 길이를 검증하지 않아, 클라이언트 제약을 우회한 큰 페이로드가 저장될 수 있습니다.

### Issue Context
클라이언트의 `maxLength`는 브라우저 UI 제약일 뿐이며, API 직접 호출로 손쉽게 우회 가능합니다.

### Fix Focus Areas
- apps/web/src/app/api/songs/report/route.ts[32-63]

### Suggested fix
- 서버에 최대 길이 상수(예: 100)를 두고, `!isNumberCategory`일 때 `trimmed.length > 100`이면 400 반환.
- (선택) 숫자 카테고리도 `trimmed.length === 5` 같은 정책을 함께 적용하면 데이터 정합성이 좋아집니다.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


6. 잘못된 JSON은 500 반환 🐞 Bug ☼ Reliability
Description
POST /api/songs/report에서 request.json() 파싱 실패(빈/잘못된 JSON)도 catch로 흡수되어 500으로 응답합니다. 클라이언트 요청 오류가 서버
오류로 분류되어 모니터링/디버깅이 왜곡됩니다.
Code

apps/web/src/app/api/songs/report/route.ts[R19-20]

+    const { songId, category, suggested_value } = await request.json();
+
Evidence
request.json() 호출은 SyntaxError 등을 throw할 수 있는데, 현재는 별도 처리 없이 외부 catch에서 500으로 응답합니다. 따라서 잘못된 바디는
400이 아닌 500으로 처리됩니다.

apps/web/src/app/api/songs/report/route.ts[19-20]
apps/web/src/app/api/songs/report/route.ts[80-89]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`await request.json()` 실패가 500으로 처리됩니다.

### Issue Context
빈 body/깨진 JSON은 클라이언트 오류(400)로 분류하는 것이 정상이며, 500은 알람/에러율 집계에 노이즈를 만듭니다.

### Fix Focus Areas
- apps/web/src/app/api/songs/report/route.ts[14-30]
- apps/web/src/app/api/songs/report/route.ts[80-89]

### Suggested fix
- `request.json()`을 별도 try/catch로 감싸고, 파싱 실패 시 `NextResponse.json(..., { status: 400 })`을 반환하세요.
- 예: 
 - `let body; try { body = await request.json(); } catch { return 400 }`

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (1)
7. 에러 메시지 미노출 🐞 Bug ◔ Observability
Description
useReportSongMutation은 AxiosError 발생 시 서버가 내려준 JSON error를 읽지 않고 error.message만 토스트로 노출합니다. 검증
실패(400) 같은 케이스에서 사용자는 원인 대신 "status code 400"만 보게 됩니다.
Code

apps/web/src/queries/reportSongQuery.ts[R13-21]

+    onError: error => {
+      console.error('Report error:', error);
+      if (error instanceof AxiosError && error.response?.status === 409) {
+        toast.error('이미 신고하신 항목입니다.');
+        return;
+      }
+      const message = error instanceof Error ? error.message : '신고 접수 실패';
+      toast.error(message);
+    },
Evidence
API는 다양한 400 응답에 대해 error 문자열을 JSON 바디로 반환하지만, mutation은 error.response.data를 사용하지 않아 해당 메시지를 사용자에게
전달하지 못합니다.

apps/web/src/queries/reportSongQuery.ts[13-21]
apps/web/src/app/api/songs/report/route.ts[21-63]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
서버의 상세 에러 메시지가 클라이언트 토스트에 표시되지 않습니다.

### Issue Context
현재는 `AxiosError.message`를 사용해 "Request failed with status code 400" 같은 비액션 메시지가 노출됩니다.

### Fix Focus Areas
- apps/web/src/queries/reportSongQuery.ts[13-21]

### Suggested fix
- `AxiosError<ApiResponse<unknown>>` 형태로 보고 `error.response?.data?.error`를 우선 사용하세요.
- 예:
 - `const apiMessage = error instanceof AxiosError ? error.response?.data?.error : undefined;`
 - `toast.error(apiMessage ?? error.message ?? '신고 접수 실패');`

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

@GulSam00 GulSam00 merged commit 64e2ea0 into develop May 2, 2026
2 checks passed
@GulSam00 GulSam00 deleted the feat/217-addSongReport branch May 2, 2026 12:36
Comment on lines +267 to +304
<div className="flex flex-col gap-2">
<Label htmlFor="report-suggested-value">올바른 정보</Label>
<Input
id="report-suggested-value"
placeholder={
isNumberMode
? '5자리 숫자로 입력해주세요.'
: '어떤 내용으로 바뀌어야 하는지 적어주세요.'
}
value={isNoData ? NO_DATA_LABEL : suggestedValue}
onChange={e => handleSuggestedValueChange(e.target.value)}
maxLength={inputMaxLength}
inputMode={isNumberMode ? 'numeric' : undefined}
pattern={isNumberMode ? '[0-9]*' : undefined}
disabled={isNoData}
required
/>
<Label
htmlFor="report-no-data"
className={cn(
'text-muted-foreground flex items-center gap-2 text-sm',
isNumberMode ? 'cursor-pointer' : 'invisible',
)}
aria-hidden={!isNumberMode}
>
<Checkbox
id="report-no-data"
checked={isNoData}
onCheckedChange={checked => handleNoDataChange(checked === true)}
disabled={!isNumberMode}
tabIndex={isNumberMode ? 0 : -1}
/>
데이터 없음 (해당 노래방에 등록되지 않은 곡)
</Label>
<div className={cn('text-muted-foreground self-end text-xs', isNoData && 'invisible')}>
{suggestedValue.length} / {inputMaxLength}
</div>
</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.

Action required

1. Report modal missing textarea 📎 Requirement gap ≡ Correctness

The new ReportSongModal UI does not include the required optional description textarea element;
it only provides an Input for suggested_value. This does not meet the modal UI requirements in
the checklist.
Agent Prompt
## Issue description
The report modal is missing the required (optional) description `textarea` element.

## Issue Context
Checklist requires the modal to contain a single-select category list plus a description textarea (optional) and cancel/submit buttons.

## Fix Focus Areas
- apps/web/src/components/ReportSongModal.tsx[267-304]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +14 to +20
export async function POST(request: Request): Promise<NextResponse<ApiResponse<void>>> {
try {
const supabase = await createClient();
const userId = await getAuthenticatedUser(supabase);

const { songId, category, suggested_value } = await request.json();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

2. /api/reports endpoint missing 📎 Requirement gap ≡ Correctness

The PR implements POST /api/songs/report instead of the required POST /api/reports. This breaks
the required BFF endpoint contract in the compliance checklist.
Agent Prompt
## Issue description
The compliance checklist requires `POST /api/reports`, but the PR adds `POST /api/songs/report`.

## Issue Context
Next.js route handler path determines the public API route.

## Fix Focus Areas
- apps/web/src/app/api/songs/report/route.ts[14-20]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +1 to +45
'use client';

import * as RadioGroupPrimitive from '@radix-ui/react-radio-group';
import { CircleIcon } from 'lucide-react';
import * as React from 'react';

import { cn } from '@/utils/cn';

function RadioGroup({
className,
...props
}: React.ComponentProps<typeof RadioGroupPrimitive.Root>) {
return (
<RadioGroupPrimitive.Root
data-slot="radio-group"
className={cn('grid gap-3', className)}
{...props}
/>
);
}

function RadioGroupItem({
className,
...props
}: React.ComponentProps<typeof RadioGroupPrimitive.Item>) {
return (
<RadioGroupPrimitive.Item
data-slot="radio-group-item"
className={cn(
'border-input text-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 aspect-square size-4 shrink-0 rounded-full border shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50',
className,
)}
{...props}
>
<RadioGroupPrimitive.Indicator
data-slot="radio-group-indicator"
className="relative flex items-center justify-center"
>
<CircleIcon className="fill-primary absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2" />
</RadioGroupPrimitive.Indicator>
</RadioGroupPrimitive.Item>
);
}

export { RadioGroup, RadioGroupItem };
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

3. Added ui/radio-group.tsx file 📘 Rule violation ⚙ Maintainability

A new shadcn/ui-style component file was added under apps/web/src/components/ui/, which is
disallowed by the checklist. This reduces upgradeability/maintainability of the vendor-like UI
layer.
Agent Prompt
## Issue description
Files under `apps/web/src/components/ui/` must not be changed/added as part of feature work.

## Issue Context
This PR adds `apps/web/src/components/ui/radio-group.tsx` and imports it from feature code.

## Fix Focus Areas
- apps/web/src/components/ui/radio-group.tsx[1-45]
- apps/web/src/components/ReportSongModal.tsx[6-13]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

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