Skip to content

fix: 관리자 코스 수강 상태 분기 반영#683

Merged
Hyeonjun0527 merged 1 commit into
developfrom
fix/course-viewer-admin-optin
May 22, 2026
Merged

fix: 관리자 코스 수강 상태 분기 반영#683
Hyeonjun0527 merged 1 commit into
developfrom
fix/course-viewer-admin-optin

Conversation

@Hyeonjun0527

@Hyeonjun0527 Hyeonjun0527 commented May 22, 2026

Copy link
Copy Markdown
Member

작업 내용

  • viewerStatus=ADMIN을 프론트 타입/분기에서 별도 상태로 추가했습니다.
  • 코스 상세 CTA/배지를 PAID 단일 판단이 아니라 hasFullAccess, isFreeEnrolled, isPaidEnrolled, canFreeEnroll 기준으로 분리했습니다.
  • 관리자 무료수강신청 성공 후 A-02 코스 상세를 재조회하도록 했습니다.
  • 학습여정에서 관리자 미수강 상태는 “관리자 권한으로 미리보기 중”으로 표시하고, 실제 무료수강/결제 상태와 full access UX를 분리했습니다.
  • 상태 해석 헬퍼와 단위 테스트를 추가했습니다.

백엔드 계약 확인

  • study-platform-mvp origin/devViewerStatus.ADMIN enum 확인
  • CourseDetailResponse.admin(...)에서 viewerStatus=ADMIN, hasFullAccess=true, 실제 수강 플래그(isFreeEnrolled/isPaidEnrolled) 별도 반환 확인

검증

  • vitest run --project unit src/components/pages/class/course-viewer-status.test.ts 통과
  • yarn lint:fix 통과 (기존 warning 유지)
  • yarn prettier:fix 통과
  • yarn typecheck 통과

Summary by CodeRabbit

Release Notes

  • New Features

    • 코스 수강 상태(관리자 권한, 결제 수강, 무료수강 등)가 사이드바에 명확하게 표시됩니다.
    • 무료 코스 등록 후 코스 상세 정보가 실시간으로 갱신됩니다.
  • Refactor

    • 코스 접근 권한 판별 로직이 통합되어 일관성이 개선되었습니다.
  • Tests

    • 코스 상태 판별 기능에 대한 테스트가 추가되었습니다.

Review Change Stack

@vercel

vercel Bot commented May 22, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
study-platform-client-dev Ready Ready Preview, Comment May 22, 2026 11:45pm

@coderabbitai

coderabbitai Bot commented May 22, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

PR은 코스 수강 권한 상태 판별 로직을 중앙화하는 헬퍼 함수 체계를 도입하고, 세 개의 주요 컴포넌트(ClassDetailPage, ClassDetailSidebar, RoadmapTab)에서 기존 viewerStatus 직접 조건식을 헬퍼 기반 파생 플래그로 전환합니다. ViewerStatus 타입에 'ADMIN' 상태를 추가하며, 무료 코스 등록 후 상세 데이터를 재조회하는 동작을 추가합니다.

Changes

코스 뷰어 상태 판별 헬퍼 시스템

Layer / File(s) Summary
타입 확장 및 헬퍼 함수 기초
src/types/api/course.types.ts, src/components/pages/class/course-viewer-status.ts
ViewerStatus에 'ADMIN' 상태를 추가하고, isAdminViewer, isCourseFreeEnrolled, isCoursePaidEnrolled, hasCourseFullAccess, canShowCourseFreeEnrollCta, getCourseViewerStatusLabel 등 6개 헬퍼 함수를 신규 작성하여 코스 수강/권한 상태 판별 및 UI 라벨 생성을 중앙화합니다.
헬퍼 함수 테스트
src/components/pages/class/course-viewer-status.test.ts
ADMIN, ADMIN(무료수강 전환), PAID 시나리오별로 헬퍼 함수들의 boolean 반환값과 라벨 문자열을 검증하는 테스트 3개를 추가합니다.
ClassDetailPage 헬퍼 기반 마이그레이션
src/app/(landing)/class/[slug]/page.tsx
import에 5개 헬퍼를 추가하고, 코스 상세 조회 훅에 refetchCourseDetail을 포함하며, ctaLabelviewerStatusLabel을 헬퍼로 계산하도록 변경합니다. handleStartCourse에서 무료 등록 조건을 canShowCourseFreeEnrollCta로 변경하고, 성공 후 refetchCourseDetail()을 호출하며, ClassDetailSidebar에 viewerStatusLabel prop을 전달합니다.
ClassDetailSidebar 뷰어 상태 라벨 표시
src/components/pages/class/class-detail-sidebar.tsx
ClassDetailSidebarPropsviewerStatusLabel 선택적 필드를 추가하고, 함수 시그니처에서 이를 구조분해로 받으며, 값이 존재할 때만 회색 배지 형태의 상태 라벨을 조건부로 렌더링합니다.
RoadmapTab 헬퍼 기반 로직 전환
src/components/pages/class/roadmap-tab.tsx
import에 4개 헬퍼를 추가하고, isFreeEnrolled, isPaidEnrolled, hasFullAccess, isAdminPreview 파생 변수를 계산합니다. 초기 접속/무료 소진/다음 레슨 안내 안내창 표시 조건과 하단 스티키 결제 CTA 표시 조건을 동일하게 마이그레이션하며, 관리자 프리뷰 문구를 타이틀 영역에 조건부 렌더링합니다.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • code-zero-to-one/study-platform-client#635: 무료 코스 등록 후 handleStartCourse 실행 흐름(조건 처리, 캐시 무효화, 내비게이션)을 수정한 PR로, 본 PR의 refetchCourseDetail 추가 및 canShowCourseFreeEnrollCta 조건 변경과 동일한 "무료 코스 등록 후 레슨 접근" 로직을 건드려 코드 레벨 연관이 있습니다.

Suggested labels

🐛 bug

Poem

🐰 코스의 상태를 가려내고,
헬퍼 함수로 조건을 단화하는 것,
마치 당근을 갈래갈래 정렬하는 것처럼 깔끔하군요!
ADMIN부터 무료수강까지, 모든 흐름이 한 곳에
이제 코드도 행복해 보입니다. 🥕✨

🚥 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목은 관리자 코스 수강 상태 분기 추가라는 핵심 변경사항을 명확하게 요약하고 있으며, 제공된 변경 사항들과 완전히 일치합니다.
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/course-viewer-admin-optin

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@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.

Caution

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

⚠️ Outside diff range comments (3)
src/types/api/course.types.ts (1)

47-52: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

src/types 직접 수정 금지 가이드 위반입니다.

ViewerStatus 변경 자체는 의도에 맞지만, Line 47-52처럼 src/types 내 타입을 직접 수정하면 저장소 규칙을 깨게 됩니다. API 스키마/코드젠 원천에서 타입을 갱신한 뒤 반영해 주세요.

As per coding guidelines, src/types/**/*.ts: "Type definitions in types/ directory must never be auto-modified".

🤖 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/types/api/course.types.ts` around lines 47 - 52, You modified the
generated ViewerStatus type in src/types/api/course.types.ts which violates the
rule that files under src/types must not be edited directly; revert any manual
edits to ViewerStatus and instead update the authoritative API schema or
OpenAPI/GraphQL source that defines ViewerStatus, then run the project’s codegen
task (the generator that produces src/types) to regenerate the type so the
change is persisted properly; ensure the regenerated ViewerStatus includes the
intended variants ('ANONYMOUS', 'LOGIN_ONLY', 'FREE_ENROLLED', 'PAID', 'ADMIN')
and commit only the source schema and generated output produced by the codegen.
src/app/(landing)/class/[slug]/page.tsx (1)

143-147: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

무료수강 성공 후 재조회 실패가 학습 진입을 막고 있습니다.

Line 143에서 재조회 실패가 발생하면 catch로 떨어져 router.push가 실행되지 않습니다. 등록이 이미 성공한 케이스까지 실패 처리되어 UX가 끊깁니다. 재조회는 비차단으로 처리하고 이동은 계속 진행하는 편이 안전합니다.

수정 예시 diff
     if (canShowCourseFreeEnrollCta(courseDetail)) {
       try {
         await createCourseFreeEnrollment.mutateAsync(courseDetail.courseId);
-        await refetchCourseDetail();
+        try {
+          await refetchCourseDetail();
+        } catch {
+          showToast('수강 상태 갱신에 실패했지만 학습은 바로 시작할 수 있어요.', 'info');
+        }
         showToast('무료 코스 등록이 완료되었어요.');
       } catch {
         showToast('무료 코스 등록 중 오류가 발생했어요.', 'error');
         return;
       }
     }
🤖 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/app/`(landing)/class/[slug]/page.tsx around lines 143 - 147, The catch
block currently treats refetchCourseDetail failures as fatal and prevents
router.push; change the flow so refetchCourseDetail is non-blocking: after the
registration call, call refetchCourseDetail().catch(err => { showToast('재조회 실패',
'warning') /* or log */ }); then always call showToast('무료 코스 등록이 완료되었어요.') and
router.push(...) regardless of refetch result; i.e., remove refetchCourseDetail
from the try/catch that blocks navigation or handle it with its own .catch so
registration success still navigates.
src/components/pages/class/roadmap-tab.tsx (1)

167-195: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Optional chaining 사용이 일관되지 않습니다.

Line 170, 195에서 course.freeLessonCount는 optional chaining 없이 접근하지만, line 171에서 course?.canPurchase는 optional chaining을 사용합니다. courseundefined일 가능성이 있으므로 일관성과 안전성을 위해 통일하는 것이 좋습니다.

🛡️ 권장 수정
 isFreeEnrolled &&
   !hasFullAccess &&
-  completedLessons >= (course.freeLessonCount ?? 0) &&
+  completedLessons >= (course?.freeLessonCount ?? 0) &&
   course?.canPurchase ? (
 (hasFullAccess ||
   (isFreeEnrolled &&
     completedLessons > 0 &&
-    completedLessons < (course.freeLessonCount ?? 0))) ? (
+    completedLessons < (course?.freeLessonCount ?? 0))) ? (
🤖 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/components/pages/class/roadmap-tab.tsx` around lines 167 - 195, The
conditional uses optional chaining inconsistently — replace direct access to
course.freeLessonCount with course?.freeLessonCount (and keep the existing
nullish fallback) so all checks around completedLessons, isFreeEnrolled,
hasFullAccess, and course?.canPurchase are safe when course is undefined; update
both occurrences in the ternary branches that reference freeLessonCount (the
conditions around the payment CTA and the nextAccessibleLesson branch) to use
course?.freeLessonCount.
🤖 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.

Outside diff comments:
In `@src/app/`(landing)/class/[slug]/page.tsx:
- Around line 143-147: The catch block currently treats refetchCourseDetail
failures as fatal and prevents router.push; change the flow so
refetchCourseDetail is non-blocking: after the registration call, call
refetchCourseDetail().catch(err => { showToast('재조회 실패', 'warning') /* or log */
}); then always call showToast('무료 코스 등록이 완료되었어요.') and router.push(...)
regardless of refetch result; i.e., remove refetchCourseDetail from the
try/catch that blocks navigation or handle it with its own .catch so
registration success still navigates.

In `@src/components/pages/class/roadmap-tab.tsx`:
- Around line 167-195: The conditional uses optional chaining inconsistently —
replace direct access to course.freeLessonCount with course?.freeLessonCount
(and keep the existing nullish fallback) so all checks around completedLessons,
isFreeEnrolled, hasFullAccess, and course?.canPurchase are safe when course is
undefined; update both occurrences in the ternary branches that reference
freeLessonCount (the conditions around the payment CTA and the
nextAccessibleLesson branch) to use course?.freeLessonCount.

In `@src/types/api/course.types.ts`:
- Around line 47-52: You modified the generated ViewerStatus type in
src/types/api/course.types.ts which violates the rule that files under src/types
must not be edited directly; revert any manual edits to ViewerStatus and instead
update the authoritative API schema or OpenAPI/GraphQL source that defines
ViewerStatus, then run the project’s codegen task (the generator that produces
src/types) to regenerate the type so the change is persisted properly; ensure
the regenerated ViewerStatus includes the intended variants ('ANONYMOUS',
'LOGIN_ONLY', 'FREE_ENROLLED', 'PAID', 'ADMIN') and commit only the source
schema and generated output produced by the codegen.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 454ef9b5-1b1a-4725-a503-75f6cf4fbf44

📥 Commits

Reviewing files that changed from the base of the PR and between 9829760 and 8c0904b.

📒 Files selected for processing (6)
  • src/app/(landing)/class/[slug]/page.tsx
  • src/components/pages/class/class-detail-sidebar.tsx
  • src/components/pages/class/course-viewer-status.test.ts
  • src/components/pages/class/course-viewer-status.ts
  • src/components/pages/class/roadmap-tab.tsx
  • src/types/api/course.types.ts

@Hyeonjun0527 Hyeonjun0527 merged commit b3d89e5 into develop May 22, 2026
15 checks passed
@Hyeonjun0527 Hyeonjun0527 deleted the fix/course-viewer-admin-optin branch May 22, 2026 23:49
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