Keep provider-specific details in data.message and return a stable, human-readable top-level success message for /payments/direct responses.
Co-authored-by: Cursor <cursoragent@cursor.com>
Align MPESA direct payment with TELEBIRR_USSD by routing it through the provider's full checkout payload proxy endpoint for consistent gateway behavior.
Co-authored-by: Cursor <cursoragent@cursor.com>
Return per-module lesson and practice aggregates under unit modules listing so clients can render module depth without additional queries.
Co-authored-by: Cursor <cursoragent@cursor.com>
Expose per-unit aggregate counts under catalog-course units listing so clients can display unit depth without extra chained requests.
Co-authored-by: Cursor <cursoragent@cursor.com>
Add units, modules, and lessons aggregate counts per catalog course so clients can render hierarchy depth without extra API calls.
Co-authored-by: Cursor <cursoragent@cursor.com>
When updating a module sort_order to an occupied position in the same course, perform an atomic swap in a transaction instead of failing with a unique constraint error.
Made-with: Cursor
When PUT payload omits title, status, or shuffle_questions, reuse current persisted values so updates do not write invalid empty status values.
Made-with: Cursor
Require INITIAL_ASSESSMENT titles to follow the Level Test A1/A2/B1/B2 format and ensure passing_score is always present on create and update.
Made-with: Cursor
Update lesson and practice completion flows to cascade module/course/program progress only when both lesson completion and related published practice completion criteria are met, and align progress counters with the new rule.
Made-with: Cursor
Add POST /api/v1/files/refresh-url to issue fresh presigned URLs from object keys, minio:// references, or stale presigned URLs so clients can refresh media links before render.
Made-with: Cursor
Resolve false OTP already used/expired responses during registration by loading OTP rows using user_id plus submitted otp code and validating usage/expiry on the matched row.
Made-with: Cursor
Extend GetQuestionSetItems and GetPublishedQuestionsInSet queries to match
paginated fields; map audio answer join in repository instead of nils.
Made-with: Cursor
Seed A1-A2, B1-B2, and C1-C2 only on their matching programs; add migration
000050 for existing databases. Document mapping in domain.
Made-with: Cursor
- resolveCourseManagementPractice falls back to SUB_MODULE PRACTICE question_sets
- Synthetic practice uses id 0 and question_set_id for orphan sets
- Align GET practice and /detail with resolver; sync question_count after load
Made-with: Cursor
- GetSubModulePracticeByID matches sub_module_practices.id or question_set_id
- Prefer primary id when both could match (ORDER BY + LIMIT 1)
- Set Success/StatusCode on practice GET/detail and GetQuestionSetsByOwner
Made-with: Cursor
- Drop question_sets.set_type = PRACTICE filter so sub_module_practices rows list correctly
- Set Success and StatusCode on GET sub-modules/:id/practices response
- Return empty JSON array instead of null for no practices
Made-with: Cursor
Course-management GET/PUT used GetSubModuleLessonByID with is_active=TRUE,
which returned no row for inactive lessons. Align with other ByID lookups
and allow admins to view and edit inactive lessons.
Made-with: Cursor
GetSubModuleLessonByID filters is_active=true, so refetch failed with 500
after soft-deactivating. Use RETURNING row from the update instead.
Made-with: Cursor
Introduce dedicated APIs for submodule lesson detail/update and subcategory listing (including Human Language), with SQL/query wiring and handler routing updates.
Made-with: Cursor
Expose APIs to list lessons by submodule and fetch a single lesson by ID, including title, description, intro video URL, and question count.
Made-with: Cursor
Expose GET /course-management/courses/:courseId/learning-path and build response from unified hierarchy tables so first-time Flows tab loads no longer fail with Cannot GET.
Made-with: Cursor
Expose GET /course-management/categories/:categoryId/courses so legacy tab/API callers no longer fail with Cannot GET during initial content load.
Made-with: Cursor
Expose delete support for top-level course categories and cascade removal of linked sub-categories/courses for content-management cleanup.
Made-with: Cursor
Add a module delete API route and handler so level/module removal actions remove modules directly instead of only deleting sub-modules.
Made-with: Cursor
Introduce a compatibility delete route and handler for course sub-categories and cascade-delete their linked courses to support admin content cleanup flows.
Made-with: Cursor
Allow course creation payloads to include sub_category_id and persist it so newly created language paths appear in unified hierarchy views.
Made-with: Cursor
Expose /course-management/human-language/hierarchy as an alias to the unified hierarchy handler so older admin clients continue working without 404s.
Made-with: Cursor
Handle missing course_sub_categories table by serving hierarchy data from legacy categories/courses queries so content pages keep loading until unified hierarchy migration is applied.
Made-with: Cursor
Enhance hierarchy parsing to group Module-N and Module-N.M naming into stable module/sub-module structures under each CEFR level for consistent rendering.
Made-with: Cursor
Expose a dedicated human-language hierarchy endpoint aligned to category/subcategory/course/level/module/sub-module structure and add a complete learner mobile integration guide.
Made-with: Cursor
Introduce separate CEFR-based human language lesson APIs for create, update, and level-filtered retrieval while keeping existing non-language course hierarchy endpoints unchanged.
Made-with: Cursor