Skip to content

Commit bd109ba

Browse files
committed
Remove chapter access locks and open all course content
1 parent 473d175 commit bd109ba

2 files changed

Lines changed: 12 additions & 96 deletions

File tree

packages/frontend/src/features/courses/ChapterLessonsPage.tsx

Lines changed: 4 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@
55
*/
66

77
import { useCallback, useEffect, useMemo } from 'react';
8-
import { useParams, useNavigate, Link } from 'react-router-dom';
8+
import { useParams, useNavigate } from 'react-router-dom';
99
import { useTranslation } from 'react-i18next';
1010
import { useQueryClient } from '@tanstack/react-query';
1111
import i18n from 'i18next';
1212
import type { SupportedLanguage } from '@/types/simulator';
1313
import { CourseGrid } from './components/CourseGrid';
1414
import { LessonCard } from './components/LessonCard';
1515
import { useStore } from '@/stores/store';
16-
import { ChevronLeft, BookOpen, Target, CheckCircle2, Circle, Lock } from 'lucide-react';
16+
import { ChevronLeft, BookOpen, Target, CheckCircle2, Circle } from 'lucide-react';
1717
import { useIsMobile, useChapter, useChapterProgress } from '@/hooks';
1818
import { getLessonFull } from '@/services/courses';
1919

@@ -46,7 +46,6 @@ export function ChapterLessonsPage() {
4646
const navigate = useNavigate();
4747
const queryClient = useQueryClient();
4848
const setPageTitle = useStore((s) => s.setPageTitle);
49-
const appUser = useStore((s) => s.appUser);
5049
const langColor = getLanguageColor(lang);
5150
const isMobile = useIsMobile();
5251
const locale = i18n.resolvedLanguage || i18n.language;
@@ -55,10 +54,6 @@ export function ChapterLessonsPage() {
5554
const { data: chapter, isLoading, isError, error } = useChapter(chapterId);
5655
const { data: chapterProgressData } = useChapterProgress(chapterId);
5756

58-
// 구독 시스템 제거됨 - 챕터 2 이상은 로그인만 필요
59-
const accessDenied = !!chapter && !appUser && chapter.order >= 2;
60-
const accessReason = accessDenied ? t('chapter.login_required_for_chapter') : '';
61-
6257
const progressByLessonId = useMemo(() => {
6358
const map = new Map<string, LessonProgressSnapshot | null>();
6459
for (const lesson of chapterProgressData?.lessons ?? []) {
@@ -75,7 +70,7 @@ export function ChapterLessonsPage() {
7570
// - 첫 미완료 레슨 1개만 예열
7671
// WHY: 이전에는 레슨 전체를 순차 프리페치해서 이동 시 체감 지연을 유발함
7772
useEffect(() => {
78-
if (!chapter?.lessons || chapter.lessons.length === 0 || accessDenied) return;
73+
if (!chapter?.lessons || chapter.lessons.length === 0) return;
7974

8075
const lessons = [...chapter.lessons].sort((a, b) => a.order - b.order);
8176
const firstIncomplete = lessons.find((l) => getLessonProgress(l as LessonWithProgress)?.status !== 'completed');
@@ -87,7 +82,7 @@ export function ChapterLessonsPage() {
8782
queryFn: () => getLessonFull(targetLessonId),
8883
staleTime: 5 * 60 * 1000,
8984
});
90-
}, [chapter, queryClient, accessDenied, getLessonProgress, locale]);
85+
}, [chapter, queryClient, getLessonProgress, locale]);
9186

9287
// hover 시 해당 레슨 프리페치
9388
const handlePrefetch = useCallback((lessonId: string) => {
@@ -139,53 +134,6 @@ export function ChapterLessonsPage() {
139134
);
140135
}
141136

142-
// 구독 필요 (접근 거부)
143-
if (accessDenied && chapter) {
144-
return (
145-
<div className="min-h-screen py-4 px-3 md:px-12 lg:px-16">
146-
{/* 뒤로가기 버튼 */}
147-
<button
148-
onClick={() => navigate(`/courses/${lang}`)}
149-
className="group flex items-center gap-2 text-[var(--theme-dashboard-text-muted)] hover:text-[#FFD700] transition-colors mb-6"
150-
>
151-
<ChevronLeft className="w-8 h-8 group-hover:-translate-x-1 transition-transform" />
152-
<span className="text-2xl font-semibold tracking-wider uppercase">{t('chapter.back_to_chapters')}</span>
153-
</button>
154-
155-
{/* 잠금 안내 */}
156-
<div className="flex flex-col items-center justify-center py-20">
157-
<div
158-
className="w-24 h-24 rounded-full flex items-center justify-center mb-6"
159-
style={{ backgroundColor: `${langColor}30` }}
160-
>
161-
<Lock className="w-12 h-12" style={{ color: langColor }} />
162-
</div>
163-
<h2 className="text-2xl font-bold text-[var(--theme-dashboard-title)] mb-3">
164-
{chapter.title}
165-
</h2>
166-
<p className="text-[var(--theme-dashboard-text-muted)] mb-6 text-center max-w-md">
167-
{accessReason}
168-
</p>
169-
<div className="flex flex-col sm:flex-row gap-3">
170-
<Link
171-
to="/"
172-
className="px-6 py-3 bg-gradient-to-r from-amber-400 to-orange-500 text-white rounded-xl font-semibold hover:shadow-lg transition-shadow flex items-center gap-2"
173-
>
174-
{t('auth.login')}
175-
</Link>
176-
<button
177-
onClick={() => navigate(`/courses/${lang}`)}
178-
className="px-6 py-3 border border-[var(--theme-dashboard-card-border)] rounded-xl font-semibold hover:bg-[var(--theme-layout-top-bar-button-hover)] transition-colors"
179-
style={{ color: 'var(--theme-dashboard-text)' }}
180-
>
181-
{t('chapter.view_other_chapters')}
182-
</button>
183-
</div>
184-
</div>
185-
</div>
186-
);
187-
}
188-
189137
return (
190138
<div className="min-h-screen py-4 px-3 md:px-12 lg:px-16">
191139
{/* 뒤로가기 버튼 */}

packages/frontend/src/features/courses/LanguageCoursePage.tsx

Lines changed: 8 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import type { ChapterWithLessons } from '@/types';
66
import { CourseGrid } from './components/CourseGrid';
77
import { ChapterCard } from './components/ChapterCard';
88
import { useStore } from '@/stores/store';
9-
import { ChevronLeft, Lock } from 'lucide-react';
9+
import { ChevronLeft } from 'lucide-react';
1010
import { useIsMobile, useLanguageCourse } from '@/hooks';
1111
import { CBrandIcon } from '@/components/ui/CBrandIcon';
1212

@@ -42,7 +42,7 @@ export function LanguageCoursePage({ langOverride }: LanguageCoursePageProps = {
4242
const lang = langOverride ?? routeLang;
4343
const navigate = useNavigate();
4444
const setPageTitle = useStore((s) => s.setPageTitle);
45-
const appUser = useStore((s) => s.appUser);
45+
const authLoading = useStore((s) => s.authLoading);
4646

4747
// TanStack Query: loading, error, data 자동 관리
4848
const { data, isLoading, isError, error } = useLanguageCourse(lang);
@@ -106,8 +106,8 @@ export function LanguageCoursePage({ langOverride }: LanguageCoursePageProps = {
106106
const isMobile = useIsMobile();
107107

108108
// 데이터에서 language와 chapters 추출
109-
const language = data;
110109
const chapters = data?.chapters || [];
110+
const pageLoading = authLoading || isLoading;
111111

112112
// 페이지 제목 설정
113113
useEffect(() => {
@@ -189,7 +189,7 @@ export function LanguageCoursePage({ langOverride }: LanguageCoursePageProps = {
189189
{langInfo.name}
190190
</h1>
191191
<p className="text-[var(--theme-dashboard-text-muted)] text-sm">
192-
{isLoading ? t('common.loading') : `${chapters.length} ${t('courses.chapters')} · ${totalLessons} ${t('courses.lessons')}`}
192+
{pageLoading ? t('common.loading') : `${chapters.length} ${t('courses.chapters')} · ${totalLessons} ${t('courses.lessons')}`}
193193
</p>
194194
<p className="text-[var(--theme-dashboard-text-muted)] text-xs mt-1">
195195
{langInfo.description}
@@ -202,7 +202,7 @@ export function LanguageCoursePage({ langOverride }: LanguageCoursePageProps = {
202202

203203
{/* 챕터 리스트 */}
204204
<div style={{ marginTop: '80px' }}>
205-
{isLoading ? (
205+
{pageLoading ? (
206206
<div className="flex items-center justify-center py-20">
207207
<div className="text-center">
208208
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-[var(--theme-dashboard-accent)] mx-auto mb-4"></div>
@@ -236,30 +236,14 @@ export function LanguageCoursePage({ langOverride }: LanguageCoursePageProps = {
236236
// 백엔드에서 계산된 진행률 사용 (DRY 원칙)
237237
const { total, completed, percentage: progress } = getChapterProgress(chapter);
238238

239-
// 이전 챕터들이 모두 완료되었는지 확인
240-
const previousChaptersComplete = chapters.slice(0, index).every(ch => {
241-
const { total: prevTotal, completed: prevCompleted } = getChapterProgress(ch);
242-
return prevTotal > 0 && prevCompleted === prevTotal;
243-
});
244-
245-
const isSequentialLocked = language?.isSequential
246-
? index > 0 && !previousChaptersComplete && completed === 0
247-
: false;
248-
249-
// 비로그인: 챕터 2 이상 로그인 필요
250-
const needsLogin = !appUser && chapter.order >= 2;
251-
252239
return (
253240
<div key={chapter.id}>
254241
<button
255-
onClick={() => !isSequentialLocked && navigate(`/courses/${lang}/${chapter.id}`)}
256-
disabled={isSequentialLocked}
257-
className="w-full p-4 text-left transition-colors hover:bg-[var(--theme-layout-top-bar-button-hover)] disabled:opacity-50 disabled:cursor-not-allowed"
242+
onClick={() => navigate(`/courses/${lang}/${chapter.id}`)}
243+
className="w-full p-4 text-left transition-colors hover:bg-[var(--theme-layout-top-bar-button-hover)]"
258244
>
259245
<div className="flex items-center justify-between gap-3">
260246
<h3 className="text-base font-semibold text-[#333] flex items-center gap-2 flex-shrink-0">
261-
{isSequentialLocked && <Lock className="w-4 h-4 text-[var(--theme-dashboard-text-muted)]" />}
262-
{needsLogin && !isSequentialLocked && <Lock className="w-4 h-4 text-gray-400" />}
263247
{chapter.title}
264248
</h3>
265249
<div className="flex items-center gap-2 flex-shrink-0">
@@ -295,22 +279,8 @@ export function LanguageCoursePage({ langOverride }: LanguageCoursePageProps = {
295279
{chapters.map((chapter, index) => {
296280
// 백엔드에서 계산된 진행률 사용 (DRY 원칙)
297281
const { total, completed } = getChapterProgress(chapter);
298-
299-
// 이전 챕터들이 모두 완료되었는지 확인
300-
const previousChaptersComplete = chapters.slice(0, index).every(ch => {
301-
const { total: prevTotal, completed: prevCompleted } = getChapterProgress(ch);
302-
return prevTotal > 0 && prevCompleted === prevTotal;
303-
});
304-
305282
const isComplete = total > 0 && completed === total;
306-
// isSequential이 false면 모든 챕터 즉시 열림
307-
const isLocked = language?.isSequential
308-
? index > 0 && !previousChaptersComplete && completed === 0
309-
: false;
310-
const isActive = !isComplete && !isLocked && (index === 0 || previousChaptersComplete);
311-
312-
// 비로그인: 챕터 2 이상 로그인 필요
313-
const needsLogin = !appUser && chapter.order >= 2;
283+
const isActive = !isComplete && index === 0;
314284

315285
return (
316286
<ChapterCard
@@ -319,9 +289,7 @@ export function LanguageCoursePage({ langOverride }: LanguageCoursePageProps = {
319289
languageId={lang}
320290
lessonCount={total}
321291
completedCount={completed}
322-
isLocked={isLocked}
323292
isActive={isActive}
324-
needsLogin={needsLogin}
325293
/>
326294
);
327295
})}

0 commit comments

Comments
 (0)