fix(exercise): persist completion policy and improve dev build workflow (#754)

* fix(exercise): persist completion policy and pass threshold on save

buildExerciseUpdateFields accepted completionPolicy and passThreshold in
the validation schema but never mapped them to the database update, so
exercise settings appeared to save while reverting on refresh.

Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com>

* fix(db): build workspace deps before compiling

@cio/db imports @cio/utils/notifications, which only exists after
@cio/utils is built. Building db in isolation failed when utils dist
was missing or stale.

- Add a db prebuild step for question-types, utils, and email
- Clean dist and tsbuildinfo before utils and email builds so
  incremental tsc cannot skip emission after dist was removed

Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com>

* chore(dev): build api workspace deps before api:dev

Run turbo build for @cio/api and @cio/jobs-worker dependencies before
starting the dev servers, so missing dist/ folders do not break startup.

Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com>

* feat(course): improve exercise progression grading UX

---------

Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com>
This commit is contained in:
Rotimi Best
2026-07-15 03:26:56 +01:00
committed by GitHub
co-authored by Rotimi Best
parent fdc19aac41
commit bfafc685e0
53 changed files with 2026 additions and 285 deletions
@@ -2038,6 +2038,7 @@
"modal": {
"title": "Course requirements complete!",
"subtitle": "Congratulations — you can view and download your certificate anytime.",
"subtitle_no_certificate": "Congratulations — you have completed this course.",
"view_certificate": "View certificate",
"later": "Close",
"checking": "Checking eligibility…",
@@ -2478,6 +2479,10 @@
"settings_completion_submitted": "Submitted — any submission counts",
"settings_completion_passed": "Passed — minimum score required",
"settings_pass_threshold": "Pass threshold (%)",
"settings_completion_passed_helper": "Students must reach the passing score before this exercise counts as complete.",
"settings_manual_grading_warning": "Manually graded submissions must be graded before they can count as passed.",
"settings_single_attempt_warning": "With one attempt, failed students need instructor action before they can continue.",
"settings_existing_submissions_warning": "Changing pass criteria can affect existing completion and progression.",
"settings_save": "Save settings",
"submissions": "Submissions",
"save": "Save",
@@ -2512,7 +2517,27 @@
"check": "Check",
"auto_graded_badge": "Auto-grade",
"manual_grading_required_badge": "Manual grading required",
"question_auto_graded_badge": "Auto graded",
"question_manual_grading_badge": "Manual grading",
"points_required_auto_grade": "Points must be greater than 0 for auto-graded exercises.",
"zero_points_warning": "Set points above 0, otherwise students won't be graded for this question.",
"guard": {
"title": "Review before saving",
"attempts_title": "What happens if a student fails?",
"description": "This exercise only counts as complete when a student passes.",
"retry_option": "Let them retry until they pass",
"retry_option_description": "Students can resubmit as many times as needed.",
"single_attempt_option": "One attempt only",
"single_attempt_option_description": "A student who fails stays blocked until you manually change their submission status.",
"recommended": "Recommended",
"manual_notice_title": "Some questions need manual grading",
"manual_notice_description": "A student's pass isn't final until you grade these questions:",
"cancel": "Cancel",
"save_anyway": "Save anyway",
"save_continue": "Save and continue",
"question_fallback": "Question {number}",
"zero_points_field": "Passed completion needs at least one point available."
},
"description": {
"title": "Title",
"heading": "Description",
@@ -2605,6 +2630,10 @@
"status_pending": "Status: Pending Review",
"status_graded": "Status: Review Completed",
"status_submitted": "Status: Submitted",
"passed_completion_result_title": "Passed",
"passed_completion_result_description": "You scored {scorePercent}%. This exercise required {requiredPercent}% to count as complete.",
"not_passed_completion_result_title": "Not passed yet",
"not_passed_completion_result_description": "You scored {scorePercent}%. This exercise requires {requiredPercent}% to count as complete.",
"checking_submission": "Checking your submission...",
"instructor_feedback": "Instructor feedback",
"back": "Back",
@@ -2616,14 +2645,25 @@
"heading": "Summary",
"question_chart": "Question Charts",
"responses": "Responses",
"attempts": "Attempts",
"no_responses": "No responses yet",
"other": "Other"
},
"individual": {
"heading": "Individual",
"answers": "Individual Answers",
"answers_for": "{name}'s answers",
"no": "No answer was provided",
"student_avatar": "Student"
"student_avatar": "Student",
"attempts_count": "{count} attempts",
"attempt_label": "Attempt {number}",
"date_unknown": "Date unknown",
"status_awaiting_manual": "Awaiting manual grading",
"status_completed": "Graded",
"status_pending": "Pending",
"counts_as_complete": "Counts as complete",
"passed": "Passed",
"not_passed": "Not passed"
},
"submissions": "Submissions",
"loading_submissions": "Loading submissions...",
@@ -3545,6 +3585,12 @@
"determines": "This determines if your course displays on your landing page",
"published": "Published",
"unpublished": "Unpublished",
"locked_content": {
"title": "{count} course item(s) are locked",
"description": "Enrolled students won't be able to take locked lessons or exercises until you unlock them.",
"description_live": "Locked lessons and exercises stay hidden from students until you unlock them — useful if you release content week by week.",
"unlock_all": "Unlock all"
},
"type": "Course type",
"course_type_desc": "Update the course type for a more personalized experience.",
"course_type_learn_more": "Learn more",
@@ -4094,6 +4140,7 @@
},
"exercise": {
"points_required_auto_grade": "Set a positive point value for every question before saving.",
"passed_requires_points": "Passed completion needs at least one point available.",
"success": "Saved Successfully",
"submission_updated": "Submission updated to",
"error_fetching": "Fetching exercises failed, please reload the page"
@@ -4125,6 +4172,7 @@
},
"course_settings": {
"success": {
"unlocked_all_content": "All course items unlocked",
"download": "Download Complete",
"saved": "Saved successfully",
"successful_integration": "Integration successful",