Courses — Code Map
API — apps/api/src/courses/
courses.module.tscourses.controller.ts— split routing:- Trainer-side:
@Controller()with explicitorganizations/:orgId/courses[/…]paths. - Buyer-side:
me/courses[/…]paths (no org param). - Public:
@Public()onpublic/courses/:idandpublic/courses/:id/checkout.
- Trainer-side:
courses.service.ts—create | findAll | findById | update | publish | archive | setDay | setPrice | listMyEntitlements | getMyEntitlement | startCourse | restartCourse | markWorkoutComplete | unmarkWorkoutComplete | getCourseWorkout | listEnrollments | findPublicById | ensureEntitlementForCheckout | attachLeadForBuyer.course-checkout.service.ts— orchestrates the buy flow (categories A/B), gates by published status + active plan, callsPaymentService.createCourseHostedPayment.courses.service.unit.spec.ts— unit tests.dto/courses.dto.ts—CreateCourseDto,UpdateCourseDto,SetCourseDayDto,SetCoursePriceDto,StartCourseDto,CourseCheckoutDto.
API — adjacent
apps/api/src/payments/services/payment.service.ts:213—createCourseHostedPayment(setsmetadata.courseEntitlementId).apps/api/src/payments/services/webhook-processing.service.ts:199 / :332— handlesmetadata.courseEntitlementIdon payment-completed / payment-failed. Owns the cross-org guard.apps/api/src/leads/— lead lifecycle (source='course_purchase').apps/api/src/organization-leads/— junction table for category-B linking.
Shared
libs/shared/src/lib/schemas/course.schema.ts— Zod schemas forCourseResponse,PublicCourseResponse,CourseEntitlementResponse.libs/shared/src/lib/constants/course-errors.ts(or similar) —COURSE_ALREADY_OWNEDconstant exported.
DB
libs/db/src/lib/schema/courses.ts—course_configs,course_workouts,course_entitlements,course_workout_completions.libs/db/src/lib/schema/enums.ts—courseEntitlementStatus,coursePreviewType,publishStatus,planType(coursevalue),deliveryMode(coursevalue),leadSource(course_purchasevalue).libs/db/src/lib/schema/scheduling.ts—programstable (the parent row).libs/db/src/lib/schema/payments.ts—planstable (course plans live here withtype='course'andprogram_idset).
Web — apps/web/src/
app/[lang]/buy/courses/[id]/page.tsx— public checkout entry point. Clerk email-code sign-in/sign-up flow; POSTs/public/courses/:id/checkoutonce authed.app/[lang]/(protected)/library/[id]/page.tsx— buyer’s player.app/[lang]/(protected)/dashboard/courses/— trainer-side CRUD + curriculum editor + enrollments view.components/courses/course-dialog.tsx— trainer dialog for creating / editing the high-level course.components/courses/course-dialog.driver.tsx,course-dialog.int.spec.tsx— driver + tests.components/course-player/:player-header.tsxday-nav.tsx,day-tile.tsxworkout-view.tsxfull-program-sheet.tsx
Cross-references
apps/api/src/minisites/— the public storefront link points at/buy/courses/:idon the gym’s minisite host.apps/api/src/notifications/templates/payment-receipt.ts— email used bysendPaymentReceiptafter a paid course purchase (only fires for category-A buyers today; see Gaps in README).apps/api/src/tasks/—cancellation_reviewandmanual_refundtask types are not specific to courses but apply when refunding a course purchase.
i18n keys
courses.buy.*— public checkout (email step, code step, signed-in confirm, redirect copy, error messages includingerrorAlreadyOwned).courses.*— trainer dialog + player labels.