[FEAT] 강사 시안 페이지 퍼블리싱#32
Conversation
📝 WalkthroughWalkthrough이 PR은 강사 커미션별 시안 선택 페이지를 새로 구현하고, 선택 흐름을 기존 기능과 통합하며, 모듈의 import 경로를 상대 경로로 정규화합니다. 시안 데이터 모델, 카드·선택 섹션 UI, 페이지 라우트를 추가하고 공유 컴포넌트를 확장합니다. Changes시안 선택 페이지 및 컴포넌트 구현
모듈 import 경로 정규화
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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 |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (3)
src/shared/ui/SidebarMenu.tsx (1)
26-38: ⚡ Quick win조건부 Tailwind 클래스 조합을
cn()으로 통일해주세요.현재는 템플릿 문자열과 문자열 덧셈이 혼용되어 있어 조건이 늘어날 때 유지보수가 어려워집니다.
cn()으로 통일하면 선택/호버 상태 클래스 관리가 더 안전해집니다.제안 diff
- const content = ( + const content = ( <div - className={`flex flex-row items-center gap-2 ${isSelected ? "text-gray-90" : "text-gray-80"}`} + className={cn("flex flex-row items-center gap-2", isSelected ? "text-gray-90" : "text-gray-80")} > @@ - const className = - "bg-gray-5 rounded-8 hover:bg-gray-20 group w-58 cursor-pointer px-5 py-3 block transition-colors duration-150" + - (isSelected ? " bg-gray-20" : ""); + const className = cn( + "bg-gray-5 rounded-8 hover:bg-gray-20 group w-58 cursor-pointer px-5 py-3 block transition-colors duration-150", + isSelected && "bg-gray-20", + );As per coding guidelines "TailwindCSS - 조건부 클래스 문자열을 직접 이어붙이는 경우 cn() 유틸리티 사용을 제안해주세요."
🤖 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/shared/ui/SidebarMenu.tsx` around lines 26 - 38, The component mixes template strings and string concatenation for Tailwind classes (the inline div's className and the outer const className), so replace both with the cn() utility to compose conditional classes: import cn if missing, use cn(...) to combine base classes and conditional ones driven by isSelected/isActive (preserve group-hover and hover classes and the size class on icons), and update the JSX to pass cn(...) as the className for the element and to set the const className via cn(...) instead of string concatenation.Source: Coding guidelines
src/widgets/instructor/choose/ui/DraftCheckSection.tsx (2)
35-38: ⚡ Quick win조건부 클래스 문자열에 cn() 유틸리티 사용을 권장합니다.
템플릿 리터럴로 조건부 클래스를 직접 이어붙이는 대신
cn()유틸리티를 사용하면 더 안전하고 읽기 쉬운 코드가 됩니다.♻️ 제안하는 수정
+import { cn } from "`@/shared/lib/utils`"; + ... <NextButton - className={`size-12 ${page < totalPages - 1 ? "cursor-pointer" : "cursor-default opacity-30"}`} + className={cn( + "size-12", + page < totalPages - 1 ? "cursor-pointer" : "cursor-default opacity-30" + )} onClick={() => setPage(p => Math.min(totalPages - 1, p + 1))} />As per coding guidelines: "조건부 클래스 문자열을 직접 이어붙이는 경우 cn() 유틸리티 사용을 제안해주세요."
🤖 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/widgets/instructor/choose/ui/DraftCheckSection.tsx` around lines 35 - 38, The NextButton component is being given a conditional className via a template literal; replace that with the cn() utility to produce a safer, clearer class string. Update the className on NextButton to call cn('size-12', { 'cursor-pointer': page < totalPages - 1, 'cursor-default opacity-30': page >= totalPages - 1 }) (and ensure cn is imported where DraftCheckSection.tsx uses NextButton), leaving the onClick handler (setPage(...)) unchanged.Source: Coding guidelines
31-34: ⚡ Quick win조건부 클래스 문자열에 cn() 유틸리티 사용을 권장합니다.
템플릿 리터럴로 조건부 클래스를 직접 이어붙이는 대신
cn()유틸리티를 사용하면 더 안전하고 읽기 쉬운 코드가 됩니다.♻️ 제안하는 수정
+import { cn } from "`@/shared/lib/utils`"; + ... <PrevButton - className={`size-12 ${page > 0 ? "cursor-pointer" : "cursor-default opacity-30"}`} + className={cn( + "size-12", + page > 0 ? "cursor-pointer" : "cursor-default opacity-30" + )} onClick={() => setPage(p => Math.max(0, p - 1))} />As per coding guidelines: "조건부 클래스 문자열을 직접 이어붙이는 경우 cn() 유틸리티 사용을 제안해주세요."
🤖 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/widgets/instructor/choose/ui/DraftCheckSection.tsx` around lines 31 - 34, Replace the inline template literal used for PrevButton's className with the cn() utility to build conditional classes: find the PrevButton JSX that uses className={`size-12 ${page > 0 ? "cursor-pointer" : "cursor-default opacity-30"}`} and change it to call cn("size-12", { "cursor-pointer": page > 0, "cursor-default opacity-30": page <= 0 }) so class logic is clearer and safer (ensure cn is imported where DraftCheckSection.tsx defines PrevButton and setPage/page are still used as-is).Source: Coding guidelines
🤖 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/instructor/choose/model/choose.ts`:
- Around line 13-35: The exported constant names commissionDraftsData (and the
related exported draftDetailsData) violate the SCREAMING_SNAKE_CASE rule; rename
them to COMMISSION_DRAFTS_DATA and DRAFT_DETAILS_DATA respectively, update all
internal references and exports in the defining module (e.g., change the export
identifiers in choose.ts) and then update every import site that consumes these
symbols (notably index.ts, ui/DraftCard.tsx, and
app/instructor/choose/[commissionId]/page.tsx) so imports use the new names.
---
Nitpick comments:
In `@src/shared/ui/SidebarMenu.tsx`:
- Around line 26-38: The component mixes template strings and string
concatenation for Tailwind classes (the inline div's className and the outer
const className), so replace both with the cn() utility to compose conditional
classes: import cn if missing, use cn(...) to combine base classes and
conditional ones driven by isSelected/isActive (preserve group-hover and hover
classes and the size class on icons), and update the JSX to pass cn(...) as the
className for the element and to set the const className via cn(...) instead of
string concatenation.
In `@src/widgets/instructor/choose/ui/DraftCheckSection.tsx`:
- Around line 35-38: The NextButton component is being given a conditional
className via a template literal; replace that with the cn() utility to produce
a safer, clearer class string. Update the className on NextButton to call
cn('size-12', { 'cursor-pointer': page < totalPages - 1, 'cursor-default
opacity-30': page >= totalPages - 1 }) (and ensure cn is imported where
DraftCheckSection.tsx uses NextButton), leaving the onClick handler
(setPage(...)) unchanged.
- Around line 31-34: Replace the inline template literal used for PrevButton's
className with the cn() utility to build conditional classes: find the
PrevButton JSX that uses className={`size-12 ${page > 0 ? "cursor-pointer" :
"cursor-default opacity-30"}`} and change it to call cn("size-12", {
"cursor-pointer": page > 0, "cursor-default opacity-30": page <= 0 }) so class
logic is clearer and safer (ensure cn is imported where DraftCheckSection.tsx
defines PrevButton and setPage/page are still used as-is).
🪄 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 Plus
Run ID: 4c6f724b-0b82-4293-af29-ee7b9dcaa26b
📒 Files selected for processing (23)
src/app/instructor/choose/[commissionId]/page.tsxsrc/app/instructor/layout.tsxsrc/app/instructor/revision/[commissionId]/page.tsxsrc/features/instructor/choose/index.tssrc/features/instructor/choose/model/choose.tssrc/features/instructor/choose/ui/DraftCard.tsxsrc/features/instructor/home/index.tssrc/features/instructor/home/model/home.tssrc/features/instructor/home/ui/DraftSubmissionStatusRow.tsxsrc/features/instructor/my/index.tssrc/features/instructor/write/index.tssrc/features/signup/index.tssrc/shared/ui/Button.tsxsrc/shared/ui/SidebarMenu.tsxsrc/shared/ui/Thumbnail.tsxsrc/widgets/instructor/choose/config/choose.tssrc/widgets/instructor/choose/index.tssrc/widgets/instructor/choose/ui/DraftCheckSection.tsxsrc/widgets/instructor/home/index.tssrc/widgets/instructor/home/ui/DraftSubmissionStatusSection.tsxsrc/widgets/instructor/my/index.tssrc/widgets/instructor/revision/index.tssrc/widgets/instructor/write/index.ts
| export const commissionDraftsData: CommissionWithDrafts[] = [ | ||
| { | ||
| commissionId: 11, | ||
| title: "해커스톡 왕초보 영어 - 기초 문법편", | ||
| drafts: [ | ||
| { draftId: 41, thumbnailUrl: "/images/thumbnail_mock.jpg" }, | ||
| { draftId: 42, thumbnailUrl: "/images/thumbnail_mock.jpg" }, | ||
| { draftId: 43, thumbnailUrl: "/images/thumbnail_mock.jpg" }, | ||
| { draftId: 44, thumbnailUrl: "/images/thumbnail_mock.jpg" }, | ||
| ], | ||
| }, | ||
| { | ||
| commissionId: 15, | ||
| title: "고등 국어 문학 - 현대시 집중", | ||
| drafts: [ | ||
| { draftId: 51, thumbnailUrl: "/images/thumbnail_mock.jpg" }, | ||
| { draftId: 52, thumbnailUrl: "/images/thumbnail_mock.jpg" }, | ||
| { draftId: 53, thumbnailUrl: "/images/thumbnail_mock.jpg" }, | ||
| { draftId: 54, thumbnailUrl: "/images/thumbnail_mock.jpg" }, | ||
| { draftId: 55, thumbnailUrl: "/images/thumbnail_mock.jpg" }, | ||
| ], | ||
| }, | ||
| ]; |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win
exported 상수는 SCREAMING_SNAKE_CASE 명명 규칙을 따라야 합니다.
commissionDraftsData와 draftDetailsData는 exported 상수이므로 프로젝트의 명명 규칙에 따라 COMMISSION_DRAFTS_DATA와 DRAFT_DETAILS_DATA로 작성해야 합니다.
♻️ 제안하는 수정
-export const commissionDraftsData: CommissionWithDrafts[] = [
+export const COMMISSION_DRAFTS_DATA: CommissionWithDrafts[] = [
{
commissionId: 11,-export const draftDetailsData: Record<number, DraftDetail> = {
+export const DRAFT_DETAILS_DATA: Record<number, DraftDetail> = {
commissionId: 11,이 변경을 적용하는 경우, 해당 상수를 import하는 모든 파일도 함께 수정해야 합니다:
src/features/instructor/choose/index.tssrc/features/instructor/choose/ui/DraftCard.tsxsrc/app/instructor/choose/[commissionId]/page.tsx
Based on learnings: "exported constants should be named in SCREAMING_SNAKE_CASE (e.g., BASIC_INFO_FIELDS, PAGE_OPTIONS)."
Also applies to: 44-144
🤖 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/instructor/choose/model/choose.ts` around lines 13 - 35, The
exported constant names commissionDraftsData (and the related exported
draftDetailsData) violate the SCREAMING_SNAKE_CASE rule; rename them to
COMMISSION_DRAFTS_DATA and DRAFT_DETAILS_DATA respectively, update all internal
references and exports in the defining module (e.g., change the export
identifiers in choose.ts) and then update every import site that consumes these
symbols (notably index.ts, ui/DraftCard.tsx, and
app/instructor/choose/[commissionId]/page.tsx) so imports use the new names.
Source: Learnings
📢 PR 유형
어떤 변경 사항이 있었나요?
📌 관련 이슈번호
✅ Key Changes
시안 선택 페이지 구현
commissionId기반으로 목데이터 연결, 동적 타이틀 표시/instructor)으로 라우팅DraftCard컴포넌트DraftCheckSection위젯홈 시안 제출 현황 행
index.ts절대 경로 → 상대 경로 변경 (home,my,write,signup등)📸 스크린샷 or 실행영상
default.mp4
🎸 기타 사항 or 추가 코멘트
Summary by CodeRabbit
릴리스 노트
New Features
Bug Fixes
UI Updates