feat: add course catalog, enrollment, progress tracking, quizzes, and reviews

Backend changes:
- Add Enrollment and LessonProgress models to track user progress
- Add UserRole enum (USER, MODERATOR, ADMIN)
- Add course verification and moderation fields
- New CatalogModule: public course browsing, publishing, verification
- New EnrollmentModule: enroll, progress tracking, quiz submission, reviews
- Add quiz generation endpoint to LessonsController

Frontend changes:
- Redesign course viewer: proper course UI with lesson navigation, progress bar
- Add beautiful typography styles for course content (prose-course)
- Fix first-login bug with token exchange retry logic
- New pages: /catalog (public courses), /catalog/[id] (course details), /learning (enrollments)
- Add LessonQuiz component with scoring and results
- Update sidebar navigation: add Catalog and My Learning links
- Add publish/verify buttons in course editor
- Integrate enrollment progress tracking with backend

All courses now support: sequential progression, quiz tests, reviews, ratings,
author verification badges, and full marketplace publishing workflow.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
root
2026-02-06 10:44:05 +00:00
parent dab726e8d1
commit 2ed65f5678
23 changed files with 1796 additions and 78 deletions

View File

@ -57,16 +57,30 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
setApiToken(null);
return;
}
api
.exchangeToken(session.access_token)
.then(({ accessToken }) => {
setApiToken(accessToken);
setLoading(false);
})
.catch(() => {
setApiToken(null);
setLoading(false);
});
let attempt = 0;
const maxRetries = 3;
const tryExchange = () => {
api
.exchangeToken(session.access_token)
.then(({ accessToken }) => {
setApiToken(accessToken);
setLoading(false);
})
.catch(() => {
attempt++;
if (attempt < maxRetries) {
// Retry with exponential backoff (500ms, 1500ms, 3500ms)
setTimeout(tryExchange, 500 * Math.pow(2, attempt));
} else {
setApiToken(null);
setLoading(false);
}
});
};
tryExchange();
}, [session?.access_token]);
// Exchange Supabase token for backend JWT; keep loading true until done so API calls wait for JWT