Skip to content

Kurs ve Paket Vitrin Sayfaları

Kurs detay (course.$id) ve paket vitrin (bundle.$slug) sayfalarının ortak düzeni, PPP fiyatları, affiliate ref, checkout ve kurs sayfasındaki paket upsell bileşenleri.

Öğrenci tarafında tek kurs ve kurs paketi (bundle) satın almadan önce görülen pazarlama / vitrin sayfaları aynı görsel dilde tasarlanmıştır: genişlik max-w-[1340px], koyu gradient hero, iki sütunlu ana grid ve sağda yapışkan fiyat kartı (lg:absolute lg:-top-[360px]). Bu sayfa course.$id.tsx ve bundle.$slug.tsx dosyalarındaki loader mantığı, UI bileşenleri ve GraphQL uçlarını özetler.


ÖğeAçıklama
Herobg-gradient-to-br from-slate-900 via-slate-800 to-slate-900, üstte başlık / alt başlık / meta (breadcrumb veya kategori).
Ana içerikgrid — sol lg:col-span-2 (içerik listesi, müfredat, açıklama vb.), sağ lg:col-span-1 (satın alma kartı).
Sağ kartlg:relative sütun içinde kart lg:absolute lg:-top-[360px] ile hero alanına görsel olarak “binen” konumda; üstte promo video veya kapak (aspect-video), altında fiyat ve aksiyonlar.
Mobillg: önekli mutlak konum kalkar; kart akışta sol içeriğin altında veya üstünde (DOM sırasına göre) görünür.

Paket vitrin (bundle.$slug.tsx): Promo video yalnızca sağ kartın üstünde gösterilir (hero üzerinde tam genişlik medya bandı yok). Öncelik: promoVideoId + BUNNY_LIBRARY_ID → Bunny iframe; aksi halde thumbnailUrl; ikisi de yoksa koyu placeholder.

Kurs detay (course.$id.tsx): Promo video veya kapak yine sağ sütun kartının üstünde; hero’da yalnızca metin ve rozetler.


Kurs detay sayfası — app/routes/course.$id.tsx

Section titled “Kurs detay sayfası — app/routes/course.$id.tsx”
  • Kurs: getCourseById (slug veya UUID).
  • Müfredat: getCourseCurriculum; önizleme dersleri için imzalı URL (generateSignedVideoUrl) — BUNNY_VIDEO_SECURITY_KEY gerekir.
  • Paket ilişkisi: getBundleByCourseId — kurs bir pakete dahilse bundle + bundleRegionalPrice (PPP) hesaplanır.
  • Bölgesel fiyat: getRegionalPrice(db, course.priceTierId, country)regionalPrice.
  • Affiliate: URL ?ref=... varsa Set-Cookie: coursio_affiliate=... (30 gün, HttpOnly, SameSite=Lax). Detay: Affiliate Programı.
  • Dil: Loader’a lang eklenir; meta ve JSON-LD için kullanılır.
  • Satın alma: createStripeCheckoutSession (GraphQL), sepete ekleme, abonelikle kayıt, kayıtlı kart ile checkout yönlendirmesi.
  • E-posta doğrulama: Satın alma öncesi emailVerified kontrolü; doğrulama e-postası aksiyonu.
  • FrequentlyBoughtTogether: Kursun dahil olduğu paket için fiyat (PPP’li bundleRegionalPrice veya ham fiyat), “Paketi sepete ekle” → addBundleToCart.
  • BundlePromoBox: Paket başlığı, özet fiyat, “Paketi görüntüle” → /:lang/bundle/:slug.
  • meta export: Title, description, Open Graph, Twitter Card, canonical, hreflang alternates.
  • JSON-LD: Course, Offer, isteğe bağlı AggregateRating. Detay: SEO ve Rich Snippets.

Paket vitrin sayfası — app/routes/bundle.$slug.tsx

Section titled “Paket vitrin sayfası — app/routes/bundle.$slug.tsx”
  • Paket: getBundleBySlug(db, slug); yoksa 404.
  • Kurs satırı fiyatları: Her kurs için priceTierId varsa getRegionalPrice ile displayPrice / displayCurrency zenginleştirilir.
  • Paket PPP: getBundleRegionalPrice(db, bundle.id, country)originalAmount, discountedAmount, currency, discountPercentage uyumu; hata durumunda sessizce düşülür, kurs toplamları kullanılır.
  • İndirim penceresi: isBundleDiscountPeriodActive({ discountValidFrom, discountValidUntil }) — aktif değilse satın al butonu devre dışı ve uyarı metni gösterilir (~/lib/bundle-discount-window).
  • Affiliate: ?ref= ile gelindiğinde coursio_affiliate cookie (kurs sayfasıyla aynı mantık).
  • Mutation: createBundleCheckoutSession(bundleId: $bundleId) — dönüş: Stripe Checkout URL; giriş yoksa GraphQL hatası → login yönlendirmesi + toast.
  • İstemci: fetch("/api/graphql", { credentials: "include" }) — oturum çerezi için.

Bu route’ta ayrı bir meta() export’u yok; sayfa başlığı ve sosyal önizleme büyük ölçüde global layout (root.tsx, $lang.tsx) ve varsayılanlar ile sınırlı kalabilir. İleride kurs sayfasıyla aynı seviyede OG/JSON-LD eklenmesi önerilir. Mevcut durum: SEO ve Rich Snippets sayfasındaki global kurallar geçerlidir.


Eğitmen tarafı — paket oluşturma ve medya

Section titled “Eğitmen tarafı — paket oluşturma ve medya”
RouteAçıklama
/:lang/instructor/bundlesPaket listesi; yeni paket oluşturma (modal).
/:lang/instructor/bundles/:id/manageBaşlık, açıklama, fiyat, kurs seçimi, kapak görseli ve promo video yükleme (API route’ları ile Bunny / depolama).

Vitrinde görünen thumbnailUrl ve promoVideoId bu yönetim ekranından beslenir.


İşlemMutation / kullanım
Paket checkout URLcreateBundleCheckoutSession(bundleId, affiliateCode?)
Sepete paketaddBundleToCart(bundleId)

Şema: app/graphql/schema.ts. Tam resolver listesi: API Referansı ve GraphQL.


Kurs ve paket fiyatları için getRegionalPrice ve paket toplamı için getBundleRegionalPrice kullanımı Bölgesel Fiyatlandırma (PPP) sayfasında anlatılır.


  • app/routes/course.$id.tsx — Kurs vitrin; FrequentlyBoughtTogether, BundlePromoBox, regional + bundle PPP.
  • app/routes/bundle.$slug.tsx — Paket vitrin; promo kart içinde, checkout.
  • app/lib/pricing-engine.tsgetBundleRegionalPrice, getRegionalPrice.
  • app/lib/bundle-discount-window.ts — Paket indirim tarih aralığı kontrolü.
  • app/routes/instructor.bundles._index.tsx, app/routes/instructor.bundles.$id.manage.tsx — Eğitmen paket yönetimi.

KonuKurs detayPaket vitrin
Dosyacourse.$id.tsxbundle.$slug.tsx
Promo medyaSağ kart üstüSağ kart üstü (hero’da tam genişlik yok)
PPPregionalPrice + isteğe bağlı bundleRegionalPricebundleRegionalPrice + kurs satırı displayPrice
SEO meta + JSON-LDVar (meta + script)Şu an yok; global layout
Affiliate cookieLoader ?ref=Aynı

İlgili: Öğrenme Deneyimi (Student Experience) (öğrenme player’ı), Checkout & Webhooks, Route Haritası.