Platform ADMIN callers no longer hit 403 on these endpoints; bulk changes to platform users.role ADMIN remain restricted to SUPER_ADMIN, while team_members.team_role ADMIN is still eligible under path ADMIN.
Co-authored-by: Cursor <cursoragent@cursor.com>
Expose POST /admin/roles/:role/bulk-deactivate and bulk-reactivate for platform users and team_members, mirroring deactivate/reactivate semantics and optional team member exclusions.
Co-authored-by: Cursor <cursoragent@cursor.com>
Expose GET /api/v1/admin/users/:user_id/recent-activity (progress.get_any_user) merging account creation and LMS completion milestones, with optional practice rows.
Co-authored-by: Cursor <cursoragent@cursor.com>
Expose GET /api/v1/admin/users/:user_id/lms-learning-activity for progress.get_any_user so admins see program/course/module/lesson completions and practices from stored completion rows.
Co-authored-by: Cursor <cursoragent@cursor.com>
Filtering matches user profile country/region (case-insensitive trim) and derived subscription state in SQL so pagination totals stay correct.
Co-authored-by: Cursor <cursoragent@cursor.com>
Null encodes when there is no active plan so clients see explicit subscription state; Swagger regenerated and GET /users description updated accordingly.
Co-authored-by: Cursor <cursoragent@cursor.com>
Support all-time, year, year+month, and custom from/to query params with filtered metrics and time-series charts.
Co-authored-by: Cursor <cursoragent@cursor.com>
Resolve practice hierarchy using module, lesson, or course linkage and only run module-level cascade when module context exists.
Co-authored-by: Cursor <cursoragent@cursor.com>
Treat existing PRACTICE sets as completable even when not published, while keeping sequence enforcement only for published practices.
Co-authored-by: Cursor <cursoragent@cursor.com>
Prioritize resolving lms_practices.id before falling back to question_set.id to avoid false 404 responses caused by cross-table ID collisions.
Co-authored-by: Cursor <cursoragent@cursor.com>
Accept either question-set IDs or LMS practice IDs and recognize LMS owner types so valid practice completions no longer return practice-not-found responses.
Co-authored-by: Cursor <cursoragent@cursor.com>
Expose POST /progress/practices/:id/complete so practice completions are recorded through the existing CompletePractice flow and included in learner progress tracking.
Co-authored-by: Cursor <cursoragent@cursor.com>
Parse FCM_SERVICE_ACCOUNT_KEY, validate project_id, and provide firebase.Config{ProjectID} during FCM client initialization to prevent missing-project-id messaging failures.
Co-authored-by: Cursor <cursoragent@cursor.com>
Add ensureFCMClient() so push APIs retry FCM initialization at request time and return actionable initialization errors when the service account key is empty or invalid.
Co-authored-by: Cursor <cursoragent@cursor.com>
Log DB_URL alongside FCM_SERVICE_ACCOUNT_KEY during test-push requests and keep compose env-file wiring aligned with current local debug setup.
Co-authored-by: Cursor <cursoragent@cursor.com>
Add request-time logging in the test push endpoint so FCM_SERVICE_ACCOUNT_KEY can be verified during each API call, not only at service startup.
Co-authored-by: Cursor <cursoragent@cursor.com>
Add a notification-service startup log to print FCM_SERVICE_ACCOUNT_KEY for runtime verification during push notification troubleshooting.
Co-authored-by: Cursor <cursoragent@cursor.com>
Implement public FAQ read endpoints and admin CRUD with RBAC, persistence, and migrations, then regenerate Swagger and add a complete Postman collection so frontend/admin teams can integrate and validate the feature end-to-end.
Co-authored-by: Cursor <cursoragent@cursor.com>
Expose has_practice booleans for LMS and pre-exam hierarchy entities, wire SQL/repository mappings, and regenerate SQLC/Swagger artifacts. Also update the Resend sender display name for outbound emails.
Co-authored-by: Cursor <cursoragent@cursor.com>
Allow builder-native response kinds like OPTION to resolve to DYNAMIC so schema-driven definition creation succeeds.
Co-authored-by: Cursor <cursoragent@cursor.com>
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