Skip to content

Öğrenme Deneyimi (Student Experience)

Ders izleme paneli (learn.$slug), ilerleme takibi (api.update-progress), sertifika sistemi (PDF indirme).

Coursio’da öğrenciler kursa kayıt olduktan sonra ders izleme paneli ile içeriği takip eder, ilerleme anlık kaydedilir ve kurs bitiminde sertifika indirebilir. Bu sayfa Course Player (learn.$slug.tsx) yapısını, api.update-progress.ts ile ilerleme kaydını ve api.certificate.$id.download.ts ile dinamik PDF sertifika üretimini açıklar.

Dosya: app/routes/learn.$slug.tsx

URL: /{lang}/learn/{courseSlug} (örn. /tr/learn/react-egitimi-abc12).

  1. Auth: Oturum kontrolü; yoksa 401.
  2. Kurs: getCourseBySlug(db, params.slug) ile kurs bulunur; yoksa 404.
  3. Erişim: checkEnrollment(db, userId, course.id) — kayıt yoksa 403.
  4. Müfredat ve ilerleme (paralel):
    • getCourseCurriculumWithResources(db, course.id) → bölümler, dersler, quiz’ler, kod egzersizleri, kaynaklar.
    • getCompletedLessonIds(db, userId, course.id) → tamamlanan ders ID’leri (user_lessons_progress.completed = true).
    • getCompletedQuizIds, getCompletedExerciseIds → tamamlanan quiz ve egzersiz ID’leri.
  5. Video URL’leri: Kayıtlı kullanıcı (veya önizleme dersi) için her video dersine generateSignedVideoUrl(lesson.bunnyVideoId, libraryId, securityKey) ile imzalı URL eklenir (lesson.signedVideoUrl).

Loader çıktısı: course, curriculum, completedLessons, completedQuizzes, completedExercises, bunnyLibraryId.

  • Header: Geri (my-courses), kurs başlığı, müfredat menü toggle (mobil), “Sertifika kazan” metni.
  • Ana alan:
    • Video ders: Bunny embed iframe (signedVideoUrl veya fallback embed URL), oynatma/duraklatma, süre takibi. Video bittiğinde otomatik tamamlama (toggleComplete) ve isteğe bağlı sonraki derse geçiş (geri sayım).
    • Quiz: QuizPlayer — sorular, cevaplar, tamamlanınca revalidate ve sonraki içeriğe geçiş.
    • Kod egzersizi: CodingWorkspace — dil, başlangıç kodu, test, tamamlanınca onComplete → revalidate.
  • Sekmeler (içerik / Q&A / notlar): İçerik kaynakları, Soru-Cevap (lazy CourseQA), Notlar (lazy CourseNotes).
  • Yan panel (sidebar): Bölümler (Accordion), her bölümde ders / quiz / egzersiz listesi; tamamlananlar checkbox ile işaretli; tıklanınca activeLesson / activeQuiz / activeExercise değişir. İlerleme özeti: “X / Y tamamlandı”.
  • Ders tamamlama: Kullanıcı checkbox ile “tamamladım” işaretleyebilir veya video sonuna gelince otomatik tamamlanır. Her iki durumda da toggleLessonCompletion(lessonId, courseId, completed: true) GraphQL mutation’ı çağrılır; user_lessons_progress.completed = true yapılır. Tamamlama sonrası izlenme süresi varsa stopWatchingTracking(true) veya reportWatchedTime(0, true) ile son kez raporlanır.
  • İzlenme süresi (learn sayfası): reportWatchedTime(minutes, lessonCompleted) → GraphQL recordLearningActivity(minutes, lessonCompleted, courseId) (streak ve abonelik izlenme logu). Periyodik (örn. 10 sn) veya ders bitişinde dakika bazlı gönderim.

Endpoint: POST /api/update-progress
Dosya: app/routes/api.update-progress.ts

Amaç: Video izlerken saniye bazında ilerlemenin anlık (heartbeat) kaydedilmesi.

Beklenen gövde (JSON):

  • userId, lessonId, courseId, instructorId (string)
  • secondsWatched (number, > 0)
  • isSubscription (boolean)

İşlemler (tek transaction):

  1. user_lessons_progress: userId + lessonId için kayıt yoksa insert (watchedSeconds: secondsWatched); varsa watchedSeconds = watchedSeconds + secondsWatched (onConflictDoUpdate).
  2. daily_activities: userId + bugünün tarihi (YYYY-MM-DD) için totalWatchedSeconds artırılır (streak/günlük aktivite).
  3. subscription_watch_logs: isSubscription === true ise userId, instructorId, tarih için durationSeconds artırılır (abonelik havuzu payı).

Not: Ders “tamamlandı” bilgisi (completed: true) bu endpoint’te güncellenmez; tamamlama GraphQL toggleLessonCompletion ile yapılır. Bu API sadece izlenen süre birikimini günceller.

  • useLearningTracker (app/hooks/useLearningTracker.ts): Oynatma durumu ve currentTimeSeconds ile periyodik heartbeat; fetch("/api/update-progress", { method: "POST", body: JSON.stringify({ courseId, lessonId, instructorId, secondsWatched, isSubscription }) }). (userId istemci tarafında eklenmeli veya API session’dan alınacak şekilde genişletilebilir.)
  • learn.$slug.tsx içinde izlenme raporu ağırlıklı olarak GraphQL recordLearningActivity (dakika + lessonCompleted) ve ders bitişinde toggleLessonCompletion ile yapılır.
  • getCompletedLessonIds (app/lib/db-queries.ts): user_lessons_progress tablosunda userId, courseId ve completed = true olan kayıtların lessonId listesini döner. Sidebar’daki tikler ve “X / Y tamamlandı” bu listeye göre hesaplanır.

Sertifika, tüm dersler tamamlandığında oluşturulur. GraphQL toggleLessonCompletion mutation’ında, bir ders completed: true yapıldıktan sonra:

  1. Kurstaki toplam ders sayısı (sadece lesson, quiz/egzersiz sayılmıyor) hesaplanır.
  2. Kullanıcının bu kursta completed = true olan ders sayısı hesaplanır.
  3. Bu iki sayı eşitse ve o kullanıcı+kurs için certificates tablosunda kayıt yoksa, yeni sertifika kaydı eklenir: userId, courseId, certificateCode (benzersiz), fullName, courseTitle, instructorName, issueDate.

Böylece kurs bitiminde sertifika otomatik oluşur.

URL: GET /api/certificates/:id/download
Dosya: app/routes/api.certificate.$id.download.ts

Akış:

  1. Auth: Session kontrolü; giriş yoksa 401.
  2. Yetki: Sertifika id ve userId ile sorgulanır; kayıt kullanıcıya ait değilse 404.
  3. Kurs süresi: İlgili kurstaki tüm derslerin lessons.duration toplamı (saniye) alınır.
  4. Doğrulama linki: origin + "/verify/" + cert.certificateCode (örn. https://site.com/verify/CRT-xyz).
  5. Referans no: Kategori kodu (categoryToCode) + sertifika kodunun son 6 karakteri (örn. ABC-123456).
  6. QR kod: QRCode.toDataURL(verifyUrl) ile Base64 Data URL üretilir (PDF’e gömülecek).
  7. PDF: CertificateTemplate React bileşeni (@react-pdf/renderer) ile renderToStream(pdfElement) çağrılır; data olarak sertifika alanları, durationSeconds, referenceNumber, qrDataUrl verilir.
  8. Yanıt: Content-Type: application/pdf, Content-Disposition: attachment; filename="certificate-{code}.pdf", Cache-Control: no-store.

Dosya: app/components/CertificateTemplate.tsx

  • @react-pdf/renderer kullanır: Document, Page, Text, View, StyleSheet, Image.
  • Tek sayfa: arka plan, dekoratif çember/yıldız, başlık (“Sertifika” / “Certificate”), fullName, courseTitle, “tamamladı” metni, süre (dakika/saat), referans numarası, issueDate, QR kod (Image src={data.qrDataUrl}).
  • Görsel stil: Beyaz içerik alanı, mavi tonlarında arka plan; PDF çıktısı yazdırma ve paylaşım için uygundur.
  • Public URL: /verify/:code — giriş gerekmez; certificateCode ile sertifika sorgulanır, varsa detay sayfası (veya doğrulama sonucu) gösterilir. Sertifika indirme ise yine /api/certificates/:id/download ile kullanıcıya özeldir.
KonuAçıklama
Course Playerlearn.$slug.tsx: loader (enrollment, curriculum, completed*, signedVideoUrl); header + ana alan (video/quiz/egzersiz) + sekmeler + sidebar (bölüm/ders listesi, ilerleme).
İlerlemeDakika/tamamlama: GraphQL recordLearningActivity + toggleLessonCompletion. Saniye birikimi: POST /api/update-progress (userLessonsProgress.watchedSeconds, dailyActivities, subscriptionWatchLogs).
Tamamlamauser_lessons_progress.completed = true → getCompletedLessonIds; tüm dersler tamamlanınca sertifika kaydı oluşturulur.
SertifikaKurs bitince otomatik sertifika kaydı; indirme: GET /api/certificates/:id/download → CertificateTemplate ile PDF stream; QR + referans no ile doğrulama.

İlgili mimari: Eğitmen ve Kurs Yönetimi (müfredat, video), Checkout & Webhooks (kayıt).

  • app/routes/learn.$slug.tsx — Ders izleme paneli (Course Player); loader, müfredat, signedVideoUrl.
  • app/routes/my-courses.learning.tsx — Kurslarım listesi.
  • app/routes/api.update-progress.ts — Video izleme ilerlemesi (saniye) kaydı.
  • app/routes/api.certificate.$id.download.ts — Sertifika PDF indirme.
  • app/components/CertificateTemplate.tsx — Sertifika PDF şablonu (@react-pdf/renderer).
  • app/hooks/useLearningTracker.ts — İzlenme süresi heartbeat.
  • app/lib/streak.ts — recordLearningActivity, getStreakData, getWeeklyActivity.
  • app/lib/db-queries.ts — getCompletedLessonIds, getCourseCurriculumWithResources.