Files
classroomio/packages/storybook/src/molecules/exercise-question/question-labels.ts
T
20e34e29dd feat(exercise): add open-ended thumbs up/down question type (#780)
* feat(ui): add THUMBS question type renderer

- Add seven new renderer files under renderers/thumbs/:
  thumbs-correct.ts, take.svelte, edit.svelte, preview.svelte,
  thumbs-review-toggle.svelte, review.svelte, submission.svelte
- Register THUMBS in renderer-contract.ts with edit/take/preview/submission/review
- Export ThumbsEdit, ThumbsTake, ThumbsPreview, ThumbsSubmission,
  ThumbsReview from exercise-question/index.ts
- Add THUMBS_FIXTURE, four thumbs label keys, and Thumbs story
  in storybook exercise-question files
- Add THUMBS case to question-type-modes.svelte submission builder

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

* feat(question-types): add THUMBS contracts, scoring, and seed

Introduce the THUMBS question type (id 14) with boolean answer data,
codec, auto-grading, Zod validation, DB seed/schema docs, and binary
normalize support shared with TRUE_FALSE. Dashboard wiring is deferred.

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

* feat(dashboard): wire THUMBS question type into exercise UI

Connect THUMBS option sync in edit/view modes, labels, picker icon,
and locale strings so the existing contracts surface in the dashboard.

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

* fix(ui): polish thumbs question UI and move premium upgrade icon

Widen thumbs Yes/No toggles, inherit selected icon color via custom class,
replace badge-based review correctness with solid fill states and bare
Check/X icons, add a success pill in preview, and place the premium
upgrade icon at the end of the question type select row.

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

* fix(ui): align thumbs review colors with prototypes

Tune correct/incorrect selected states so review buttons use dark
emerald/red content colors on solid fills, matching the design comps.

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

* fix(ui): address remaining PR review comments

Use a shared timestamp local in thumbs edit fallback options.
Move border-beam animation-delay after the animation shorthand.
Alias --spacing to --ui-spacing for tw-animate compatibility.

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

* fix(db): sync question_type sequence before seeding new types

Avoid duplicate key errors when question_type_id_seq lags behind rows
inserted via restore or explicit ids (e.g. THUMBS on restored databases).

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

* fix(exercise): resolve thumbs review label ICU crash

Remove {value} ICU placeholder from thumbs.review.correct_answer_label
since labels are resolved at map-build time without context. Append the
dynamic yes/no label in thumbs-review-toggle instead.

Also bind question points through the questionnaire store index to avoid
the Svelte non-reactive property binding warning in edit-mode.

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

* docs(agent): add THUMBS guidance and sync question type docs

Add shared agent question-type guide helpers in @cio/question-types and
use them across the teacher prompt, add/update_questions tool schemas,
legacy ai-assistant schemas, and MCP exercise tools.

- Document when to use THUMBS (Yes/No sentiment) vs TRUE_FALSE (factual)
- Fix stale MCP question type ids and add THUMBS=14
- Exclude disabled MATCHING/HOTSPOT from enabled type lists

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

* fix(thumbs): clarify auto-grading and add editable option labels

THUMBS is auto-graded like TRUE_FALSE; the difference is thumbs UI with
customizable Yes/No-style labels and the teacher selecting the correct
option. Render take/preview/review from question.options labels and add
label inputs in the thumbs edit renderer. Update agent and MCP docs to
stop implying THUMBS is manual or non-gradable.

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

* chore(mcp): bump version to 0.0.10

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

* fix(question-types): make THUMBS open-ended with no correct answer

THUMBS is a sentiment/poll question type, not an auto-graded binary like
TRUE_FALSE. Remove correct-answer UI, scoring, and validation; update agent
and MCP docs accordingly.

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

* fix(dashboard): stop treating THUMBS as auto-graded in editor validation

Normalize thumbs questions on load/save, show an Open-ended badge instead
of Auto, and only enforce points/correct-answer rules on auto-gradable types.

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

* revert(dashboard): remove unsolicited open-ended grading badge

THUMBS uses the existing Manual badge like other non-auto-gradable types.

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

* fix(exercise): show question title validation errors in editor

Display mapped title errors beneath the question title input instead of
only highlighting the question card border. Pass the exercise entity to
mapZodErrorsToTranslations so messages resolve to validations.exercise.*
keys, and use a translated snackbar key for save validation failures.

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

* fix(mcp): build workspace deps before MCP publish bundle

The MCP esbuild step imports @cio/question-types and @cio/utils dist
outputs. CI failed on a clean checkout because mcp:build only built the
MCP package itself. Build those dependencies first so 0.0.10 can publish.

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com>
2026-07-31 14:59:38 +01:00

189 lines
11 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Hardcoded English labels for ExerciseQuestion Storybook demos.
*
* These keys must stay in sync with apps/dashboard/src/lib/features/course/components/exercise/question-labels.ts,
* which provides translated values via getExerciseQuestionLabels(). When adding or changing labels for a question type,
* update both files.
*/
export const QUESTION_LABELS = {
'common.option_prefix': 'Option',
'common.remove': 'Remove',
'common.add_option': 'Add option',
'common.correct_badge': 'Correct',
'common.incorrect_badge': 'Incorrect',
'common.not_set': 'Not set',
'navigation.previous': 'Previous',
'navigation.next': 'Next',
'navigation.finish': 'Finish',
'question.edit.title_label': 'Title',
'question.edit.title_placeholder': 'Question prompt',
'question.edit.description_label': 'Description (optional)',
'question.edit.description_placeholder': 'Add additional context for this question',
'question.edit.add_description': 'Add description',
'question.edit.remove_description': 'Remove description',
'question.edit.media_menu_sr': 'Question extras menu',
'question.edit.add_image': 'Add image',
'question.edit.add_video': 'Add video',
'question.edit.remove_image_tooltip': 'Remove image',
'question.edit.remove_image_sr': 'Remove image',
'question.edit.remove_video_tooltip': 'Remove video',
'question.edit.remove_video_sr': 'Remove video',
'question.edit.image_alt': 'Question context',
'question.edit.video_modal_title': 'Add YouTube video',
'question.edit.video_link_label': 'YouTube link',
'question.edit.video_link_placeholder': 'https://www.youtube.com/watch?v=',
'question.edit.video_add_button': 'Add video',
'question.edit.video_invalid_link': 'Please enter a valid YouTube link.',
'question.edit.upload_error_only_images': 'Only image files are supported.',
'question.edit.upload_error_skipped': 'Some files were skipped because they are not images.',
'question.edit.upload_error_failed': 'Failed to upload image. Try again.',
'radio.edit.helper': 'Select exactly one correct option.',
'radio.edit.correct_selected_tooltip': 'Correct answer selected',
'radio.edit.mark_correct_tooltip': 'Mark as correct answer',
'radio.edit.mark_correct_sr': 'Mark option as correct',
'checkbox.edit.helper': 'Mark one or more options as correct.',
'checkbox.edit.correct_selected_tooltip': 'Correct answer selected',
'checkbox.edit.toggle_correct_tooltip': 'Toggle correct answer',
'checkbox.edit.toggle_correct_sr': 'Toggle option as correct',
'true_false.true_label': 'True',
'true_false.false_label': 'False',
'true_false.edit.correct_answer_label': 'Correct answer',
'true_false.preview.correct_value_label': 'Correct value',
'thumbs.yes_label': 'Yes',
'thumbs.no_label': 'No',
'thumbs.edit.positive_option_label': 'Thumbs up label',
'thumbs.edit.negative_option_label': 'Thumbs down label',
'thumbs.preview.helper': 'Open-ended — students choose thumbs up or down.',
'short_answer.edit.instructions_placeholder': 'Optional instructions',
'short_answer.take.placeholder': 'Type your answer',
'short_answer.preview.helper': 'Free-text answer question.',
'short_answer.review.your_answer_label': 'Submitted:',
'short_answer.review.accepted_label': 'Accepted answers',
'numeric.edit.correct_value_label': 'Correct value',
'numeric.edit.correct_value_info': 'Expected numeric answer to mark as correct.',
'numeric.edit.tolerance_label': 'Tolerance',
'numeric.edit.tolerance_info': 'Allowed +/- range from the correct value.',
'numeric.edit.correct_value_placeholder': 'Correct value',
'numeric.edit.tolerance_placeholder': 'Tolerance',
'numeric.take.placeholder': 'Enter a number',
'numeric.preview.correct_value_label': 'Correct value',
'numeric.preview.tolerance_label': 'Tolerance',
'numeric.review.your_answer_label': 'Submitted:',
'numeric.review.in_range': 'Within tolerance',
'numeric.review.out_of_range': 'Outside tolerance',
'star.edit.correct_value_label': 'Correct star value',
'star.edit.correct_value_info': 'Expected star count for full credit.',
'star.edit.max_stars_label': 'Maximum stars',
'star.edit.max_stars_info': 'Stars shown to learners (1–10).',
'star.edit.correct_value_placeholder': 'e.g. 3',
'star.edit.max_stars_placeholder': 'e.g. 5',
'star.take.helper': 'Select your rating.',
'star.take.star_sr': 'Star {n} of {max}',
'star.preview.correct_label': 'Correct answer',
'fill_blank.edit.accepted_answers_placeholder': 'Accepted answers (comma separated)',
'fill_blank.take.placeholder': 'Enter answers separated by commas',
'fill_blank.preview.accepted_answers_label': 'Accepted answers',
'fill_blank.review.your_answer_label': 'Submitted:',
'word_bank.edit.template_label': 'Sentence template',
'word_bank.edit.template_placeholder': 'Use ___ for each blank...',
'word_bank.edit.template_helper': 'Use three underscores (___) for each blank.',
'word_bank.edit.no_blanks_warning': 'Add at least one ___ blank marker in your template.',
'word_bank.edit.correct_answers_heading': 'Correct answer for each blank',
'word_bank.edit.blank_label': 'Blank',
'word_bank.edit.correct_placeholder': 'Correct term or phrase',
'word_bank.edit.distractors_heading': 'Extra words (distractors)',
'word_bank.edit.distractor_placeholder': 'Wrong option',
'word_bank.edit.add_distractor': 'Add distractor',
'word_bank.take.helper': 'Click or drag words into blanks.',
'word_bank.take.bank_heading': 'Word bank',
'word_bank.take.clear_blank_sr': 'Remove word from blank',
'word_bank.preview.filled_label': 'Expected answers in context',
'word_bank.review.your_answer_label': 'Your answers',
'word_bank.review.correct_label': 'Correct answers',
'file_upload.edit.accepted_types_placeholder': 'Select accepted file formats',
'file_upload.edit.max_size_placeholder': 'Max size (MB)',
'file_upload.take.selected_file_label': 'Selected file',
'file_upload.take.upload_button': 'Upload',
'file_upload.take.upload_progress': 'Uploading...',
'file_upload.take.upload_error': 'Upload failed',
'file_upload.take.view': 'View',
'file_upload.take.download': 'Download',
'file_upload.preview.accepted_types_label': 'Accepted types',
'file_upload.preview.max_size_label': 'Max size (MB)',
'video_recording.edit.max_duration_label': 'Max duration',
'video_recording.edit.max_duration_helper': 'Students record in the browser and can retake before submitting.',
'video_recording.edit.max_duration_placeholder': '60',
'video_recording.edit.manual_grading': 'Requires manual grading',
'video_recording.take.unsupported': 'Video recording is not supported in this browser.',
'video_recording.take.permission_prompt': 'Allow camera and microphone access to record your answer.',
'video_recording.take.permission_denied': 'Camera or microphone access was blocked. Check browser permissions.',
'video_recording.take.start_camera': 'Set up camera',
'video_recording.take.camera_label': 'Camera',
'video_recording.take.camera_placeholder': 'Select camera',
'video_recording.take.play_preview': 'Play recording preview',
'video_recording.take.pause_preview': 'Pause recording preview',
'video_recording.take.start_recording': 'Start recording',
'video_recording.take.stop_recording': 'Stop recording',
'video_recording.take.elapsed': 'Elapsed',
'video_recording.take.remaining': 'Remaining',
'video_recording.take.max_duration': 'Max',
'video_recording.take.too_short': 'Record for at least 1 second.',
'video_recording.take.use_recording': 'Use recording',
'video_recording.take.retake': 'Retake',
'video_recording.take.delete': 'Delete recording',
'video_recording.take.retry_upload': 'Retry upload',
'video_recording.take.uploading': 'Uploading recording...',
'video_recording.take.uploaded': 'Recording uploaded',
'video_recording.take.upload_failed': 'Upload failed. Try again.',
'video_recording.take.replaces_previous': 'A new accepted take replaces the previous answer after upload.',
'video_recording.preview.max_duration_label': 'Max duration',
'video_recording.preview.manual_grading': 'Learners record a short video response. Manual grading required.',
'video_recording.submission.no_video': 'No video submitted.',
'video_recording.submission.unavailable': 'Video unavailable.',
'video_recording.submission.duration': 'Duration',
'video_recording.submission.file_size': 'File size',
'video_recording.submission.recorded_at': 'Recorded',
'video_recording.submission.uploaded_at': 'Uploaded',
'video_recording.submission.retake_count': 'Retakes',
'link.edit.instructions_placeholder': 'Optional instructions for learners',
'link.edit.helper': 'Learners can submit one or more valid URLs (http/https).',
'link.take.helper': 'Add one or more links. Only valid http/https URLs are accepted.',
'link.take.placeholder': 'https://example.com/resource-{index}',
'link.take.remove_tooltip': 'Remove link',
'link.take.remove_sr': 'Remove link',
'link.take.invalid_url_error': 'Please enter a valid URL (http/https).',
'link.take.add_button': 'Add link',
'link.preview.empty': 'No links submitted.',
'link.preview.item_label': 'Link {index}',
'matching.edit.helper': 'Configure pairs in question settings via host app controls.',
'matching.take.placeholder': 'Provide matching pairs as JSON',
'matching.preview.helper': 'Matching pairs preview is provided by host app settings.',
'ordering.fallback_step_prefix': 'Step',
'ordering.edit.helper': 'Drag items to define the correct order.',
'ordering.edit.add_step': 'Add step',
'ordering.edit.empty': 'Add steps, then drag to set the expected order.',
'ordering.edit.step_placeholder': 'Step {index}',
'ordering.edit.remove_step_tooltip': 'Remove step',
'ordering.take.helper': 'Drag and drop to arrange the items in the correct order.',
'ordering.take.empty': 'No ordering items are configured for this question yet.',
'ordering.preview.submitted_label': 'Submitted order',
'ordering.preview.expected_label': 'Expected order',
'ordering.preview.matches': 'Submitted order matches expected order.',
'ordering.preview.differs': 'Submitted order differs from expected order.',
'hotspot.edit.helper': 'Configure hotspot image and zones in question settings via host app controls.',
'hotspot.take.placeholder': 'Provide hotspot coordinates as JSON',
'hotspot.preview.helper': 'Hotspot preview is provided by host app image and hotspot settings.',
'textarea.edit.placeholder': 'Learners will answer with long-form text',
'textarea.edit.min_characters_label': 'Minimum characters',
'textarea.edit.min_characters_placeholder': 'Minimum characters',
'textarea.edit.max_characters_label': 'Maximum characters',
'textarea.edit.max_characters_placeholder': 'Maximum characters',
'textarea.take.placeholder': 'Write your answer',
'submission.common.no_answer': 'No answer',
'submission.common.other': 'Other',
'submission.chart.responses': 'Responses',
'submission.chart.no_data': 'No responses yet',
'submission.list.responses': 'Responses',
'submission.list.no_responses': 'No responses yet'
} as const;