Skip to content

Bundles

The Bundles app lets instructors package multiple courses together and sell them at a discounted price. Each bundle has its own pricing, thumbnail, and publication status, and tracks sales and revenue independently.

  • Bundle creation with title, description, thumbnail, pricing, and compare-at price
  • Course aggregation — each bundle references multiple courses with sort ordering
  • Sales and revenue tracking displayed as dashboard stats
  • Status management with draft, published, and inactive states
RoutePurpose
/business/apps/bundlesDashboard with stats and bundle list
/business/apps/bundles/newCreate a new bundle
/business/apps/bundles/[id]Edit an individual bundle

The app uses types from $lib/apps/bundles/types/index.js backed by the ext_bundles database schema.

TypeKey fields
Bundleid, instructor_id, title, slug, price, compare_price, status, course_count, sales_count, thumbnail_url
BundleCoursebundle_id, course_id, sort_order — join table linking bundles to courses
BundlePurchasebundle_id, buyer_id, payment_id, amount, status (pending, completed, refunded)
BundleStatstotal_bundles, published_bundles, total_sales, total_revenue
  • BundleWithCourses — bundle with its associated course links
  • BundleWithInstructor — bundle with instructor profile data
  • BundlePurchaseWithBundle — purchase record with full bundle details

The load function calls two service functions from $lib/apps/bundles/services/index.js:

FunctionDescription
getInstructorStats(userId)Returns BundleStats for the instructor
listInstructorBundles(userId, {})Returns all bundles owned by the instructor

The dashboard has three sections:

  • Stats grid — four Card components showing Total Bundles, Published, Total Sales, and Revenue (formatted as USD currency)
  • Bundle list — a Card with clickable rows showing thumbnail (or stack icon placeholder), title, course count, sales count, date, price, and status badge
  • Empty state — simple text prompt when no bundles exist
  • draftsecondary variant
  • publisheddefault variant
  • inactivedestructive variant
  • Courses app dependency — bundles reference courses from the Courses app via course_id in the BundleCourse join table
  • Compare pricing — the compare_price field enables strikethrough/discount pricing display on public-facing pages
  • Instructor scoping — all queries are scoped to instructor_id, ensuring instructors only see their own bundles