Skip to content

Add admin-only color override for group subquestions and MC options - #5017

Open
aseckin wants to merge 2 commits into
mainfrom
feature/subquestion-colors
Open

Add admin-only color override for group subquestions and MC options#5017
aseckin wants to merge 2 commits into
mainfrom
feature/subquestion-colors

Conversation

@aseckin

@aseckin aseckin commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Adds an admin-only (is_superuser) color picker to the Create/Edit Question forms, letting admins override the default index-based colors for question group subquestions and multiple choice options.

colorpicker.mp4

UI

  • A color swatch next to each Subquestion Label (group form) and at the end of each Choices row (MC form); clicking opens a small panel with the 18 mc-option palette colors + a reset-to-default cell.
  • Only rendered for superusers; MC colors remain editable even when options are locked by existing forecasts.

Backend

  • Two new nullable Question fields + migration: label_color (palette key, group subquestions) and options_colors (dict of option label → palette key, MC), exposed in read/write serializers with light shape validation.
  • Admin option renames (multiple_choice_rename_option) carry the color along; dict keyed by label is unaffected by reorders.

Rendering

  • Colors are stored as METAC_COLORS["mc-option"] palette keys, so light/dark theme variants keep working.
  • Override applied at the single choke point (generateChoiceItemsFromMultipleChoiceForecast / generateChoiceItemsFromGroupQuestions in choices.ts) with fallback to the existing index-based scale — all timelines, legends, and feed/consumer cards inherit it. Unset or unknown keys behave exactly as before.

Notes

  • No duplicate-color checking by design (admin discretion).
  • No server-side superuser gating: the API is already auth-gated and the field is purely cosmetic.
  • Verified locally: serializer round-trip, chart rendering of overrides + fallbacks on MC and group questions (light/dark), lint/typecheck/ruff clean.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added customizable colors for multiple-choice options and subquestion labels.
    • Color selections are preserved in drafts and applied when displaying questions.
    • Added color controls for authorized administrators, including a default/clear option.
    • Color settings remain aligned when options are added, removed, or renamed.
    • Added translations for color controls in English, Czech, Spanish, Portuguese, Simplified Chinese, and Traditional Chinese.

Admins (is_superuser) can now set an explicit color per group
subquestion and per multiple-choice option in the Create/Edit Question
forms, via a small palette swatch picker next to the Subquestion Label
field and at the end of each Choices row.

- New nullable Question fields: label_color (palette key for group
  subquestions) and options_colors (dict of option label -> palette
  key for MC), exposed in read/write serializers with light validation.
- Colors are stored as keys of METAC_COLORS["mc-option"] so light/dark
  theme variants keep working.
- Override applied at the choices.ts choke point with fallback to the
  existing index-based color scale, so all timelines, legends and cards
  inherit it; unset/unknown keys behave exactly as before.
- Option colors keyed by label survive reorders; renames via the admin
  update-options handler carry the color along.
- MC colors stay editable even when options are locked by forecasts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The change adds configurable colors for multiple-choice options and group subquestion labels. Color values use frontend palette keys, persist through question APIs and drafts, remain aligned with option edits, and override default index-based rendering colors.

Changes

Question color configuration

Layer / File(s) Summary
Persist and validate color settings
questions/models.py, questions/migrations/0038_question_label_color_question_options_colors.py, questions/serializers/common.py, questions/services/multiple_choice_handlers.py
Questions store options_colors and label_color. Serializers expose and validate these fields. Option renaming updates the corresponding color-map key.
Define palette helpers and shared picker
front_end/src/types/question.ts, front_end/src/constants/colors.ts, front_end/src/app/(main)/questions/components/color_picker.tsx
Frontend types include color fields. Palette helpers resolve keys. ColorPicker displays selectable colors and a clear action.
Edit and persist colors in forms
front_end/src/app/(main)/questions/components/question_form.tsx, front_end/src/app/(main)/questions/components/group_form.tsx
Superusers can set option and subquestion colors. Forms load, save, and draft-persist color values while keeping option colors aligned with additions and removals.
Apply colors to rendered choices
front_end/src/utils/questions/choices.ts, front_end/messages/cs.json, front_end/messages/en.json, front_end/messages/es.json, front_end/messages/pt.json, front_end/messages/zh-TW.json, front_end/messages/zh.json
Choice generation prefers configured colors over index-based defaults. Localization files add color picker labels.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Superuser
  participant QuestionForm
  participant QuestionWriteSerializer
  participant QuestionModel
  participant ChoiceGeneration
  Superuser->>QuestionForm: select option or label color
  QuestionForm->>QuestionWriteSerializer: submit color mappings
  QuestionWriteSerializer->>QuestionModel: validate and save color fields
  QuestionModel-->>QuestionForm: return question color data
  QuestionForm->>ChoiceGeneration: provide configured colors
  ChoiceGeneration-->>QuestionForm: render colored choices
Loading

Possibly related PRs

Suggested reviewers: hlbmtc, ncarazon

Poem

A rabbit chose a color bright,
Then saved it safely through the night.
Options lined up, labels glowed,
Drafts remembered what they showed.
“Hop, hop!” the picker sings,
Palette magic in tiny rings.

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding color override functionality for group subquestions and multiple-choice options restricted to admin users.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/subquestion-colors

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

🚀 Preview Environment

Your preview environment is ready!

Resource Details
🌐 Preview URL https://metaculus-pr-5017-feature-subquestion-colors-preview.mtcl.cc
📦 Docker Image ghcr.io/metaculus/metaculus:feature-subquestion-colors-0887952
🗄️ PostgreSQL NeonDB branch preview/pr-5017-feature-subquestion-colors
Redis Fly Redis mtc-redis-pr-5017-feature-subquestion-colors

Details

  • Commit: 0887952ddf0f49d12c029a20aa691fdf7c26c401
  • Branch: feature/subquestion-colors
  • Fly App: metaculus-pr-5017-feature-subquestion-colors

ℹ️ Preview Environment Info

Isolation:

  • PostgreSQL and Redis are fully isolated from production
  • Each PR gets its own database branch and Redis instance
  • Changes pushed to this PR will trigger a new deployment

Limitations:

  • Background workers and cron jobs are not deployed in preview environments
  • If you need to test background jobs, use Heroku staging environments

Cleanup:

  • This preview will be automatically destroyed when the PR is closed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
front_end/src/app/(main)/questions/components/group_form.tsx (1)

821-826: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Avoid directly mutating the state object.

React state should be treated as immutable. While mutating the object directly and returning it in a new array will trigger a re-render in this component, it can lead to subtle bugs in memoized child components or hooks that rely on referential equality.

Consider returning a cloned object instead.

♻️ Proposed refactor
                             subQuestions.map((subQuestion, iter_index) => {
                               if (index === iter_index) {
-                                subQuestion["label_color"] = key;
+                                return { ...subQuestion, label_color: key };
                               }
                               return subQuestion;
                             })
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@front_end/src/app/`(main)/questions/components/group_form.tsx around lines
821 - 826, Update the subQuestions mapping in the group form so the item
matching iter_index is replaced with a cloned object containing the new
label_color, rather than mutating subQuestion directly. Return unchanged
subQuestion objects for all other entries and preserve the existing array update
behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@front_end/src/app/`(main)/questions/components/color_picker.tsx:
- Around line 51-66: Add an accessible name to each color swatch button in the
color-picker rendering by deriving a descriptive label from the color/key, and
apply that value to both aria-label and title. Keep the existing selection,
styling, and click behavior unchanged.

---

Nitpick comments:
In `@front_end/src/app/`(main)/questions/components/group_form.tsx:
- Around line 821-826: Update the subQuestions mapping in the group form so the
item matching iter_index is replaced with a cloned object containing the new
label_color, rather than mutating subQuestion directly. Return unchanged
subQuestion objects for all other entries and preserve the existing array update
behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ae2a469d-ac8c-4840-a350-6d5bf50f4d94

📥 Commits

Reviewing files that changed from the base of the PR and between ff66637 and 97e46c7.

📒 Files selected for processing (16)
  • front_end/messages/cs.json
  • front_end/messages/en.json
  • front_end/messages/es.json
  • front_end/messages/pt.json
  • front_end/messages/zh-TW.json
  • front_end/messages/zh.json
  • front_end/src/app/(main)/questions/components/color_picker.tsx
  • front_end/src/app/(main)/questions/components/group_form.tsx
  • front_end/src/app/(main)/questions/components/question_form.tsx
  • front_end/src/constants/colors.ts
  • front_end/src/types/question.ts
  • front_end/src/utils/questions/choices.ts
  • questions/migrations/0038_question_label_color_question_options_colors.py
  • questions/models.py
  • questions/serializers/common.py
  • questions/services/multiple_choice_handlers.py

Comment thread front_end/src/app/(main)/questions/components/color_picker.tsx
@aseckin
aseckin requested review from hlbmtc and ncarazon July 15, 2026 15:15
@aseckin
aseckin marked this pull request as ready for review July 15, 2026 15:15
…olors

# Conflicts:
#	front_end/src/app/(main)/questions/components/group_form.tsx
#	front_end/src/app/(main)/questions/components/question_form.tsx

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
front_end/messages/zh.json (1)

616-616: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Use Simplified Chinese in zh.json.

This locale uses Simplified Chinese elsewhere, but this value contains Traditional characters (, 評論). Replace it with the approved Simplified Chinese translation, such as 链接的评论, to avoid mixed scripts.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@front_end/messages/zh.json` at line 616, Update the linkedComment translation
in zh.json to use Simplified Chinese characters, replacing the Traditional
translation with the approved equivalent “链接的评论” while leaving other locale
entries unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@front_end/messages/zh.json`:
- Line 616: Update the linkedComment translation in zh.json to use Simplified
Chinese characters, replacing the Traditional translation with the approved
equivalent “链接的评论” while leaving other locale entries unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 58281728-ef25-4c0b-9bfe-85954683d62d

📥 Commits

Reviewing files that changed from the base of the PR and between 97e46c7 and 0887952.

📒 Files selected for processing (8)
  • front_end/messages/cs.json
  • front_end/messages/en.json
  • front_end/messages/es.json
  • front_end/messages/pt.json
  • front_end/messages/zh-TW.json
  • front_end/messages/zh.json
  • front_end/src/app/(main)/questions/components/group_form.tsx
  • front_end/src/app/(main)/questions/components/question_form.tsx
🚧 Files skipped from review as they are similar to previous changes (4)
  • front_end/messages/es.json
  • front_end/messages/zh-TW.json
  • front_end/messages/pt.json
  • front_end/src/app/(main)/questions/components/question_form.tsx

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant