Conversation
closes #3887 adds max attainable coverage helper text to score and performance sections on question page todo: quivalent text on group info is cramped and hover doesn't work quite correclty
|
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughAdds a computed max-attainable peer-coverage value and displays it inline as “(max. X%)” with an info-tooltip and help link; introduces i18n keys for the explanation and updates participation/additional-scores UI to render the suffix when applicable. Changes
Sequence Diagram(s)sequenceDiagram
participant Q as Question data
participant UI as ParticipationSummary / ScoresTable
participant Tooltip as Tooltip (info icon + link)
Q->>UI: provide open_time, scheduled_close_time, actual_close_time
UI->>UI: compute getMaxCoverage(question)
alt maxCoverage present
UI->>UI: format "(max. X%)"
UI->>Tooltip: render info icon with explanation + help link
Tooltip-->>UI: show on hover/tap
else no maxCoverage
UI-->>UI: render nothing
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 7
🧹 Nitpick comments (2)
front_end/src/app/(main)/questions/[id]/components/post_score_data/additional_scores_table.tsx (1)
139-139: Use one shared max-coverage formatting rule across score surfaces.This table uses one decimal place, while
participation_summary.tsxrounds to an integer. Same metric, two displays.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@front_end/src/app/`(main)/questions/[id]/components/post_score_data/additional_scores_table.tsx at line 139, The max-coverage percentage is formatted differently here ((maxCoverage * 100).toFixed(1) + "%") than in participation_summary.tsx; introduce or use a single shared formatter (e.g., formatMaxCoverage or formatPercentage) and replace the local (maxCoverage * 100).toFixed(1) usage in additional_scores_table.tsx (and update participation_summary.tsx to the same helper) so both score surfaces use the same rounding/display rule; ensure the helper accepts the raw maxCoverage number and returns the final string with percent sign for consistent rendering across components.front_end/src/app/(main)/questions/[id]/components/post_score_data/participation_summary.tsx (1)
42-55: ExtractgetMaxCoverageto a shared util.The same helper now exists in two score components; centralizing it will prevent behavior drift.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@front_end/src/app/`(main)/questions/[id]/components/post_score_data/participation_summary.tsx around lines 42 - 55, Extract the getMaxCoverage function (signature: const getMaxCoverage = (question: QuestionWithForecasts): number | null) into a shared utility module and replace the duplicate implementations with an import from that module; specifically, create a shared util exporting getMaxCoverage, remove the local getMaxCoverage definitions in both score components, update their imports to import { getMaxCoverage } from the new util, and ensure the util accepts the same QuestionWithForecasts shape and preserves the exact return semantics (null when inputs missing or totalDuration <= 0).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@front_end/messages/cs.json`:
- Line 2084: Update the Czech locale strings to include the
<maxCoverageDisplay></maxCoverageDisplay> injection where the participation
summary sentence is defined so the max attainable coverage value is rendered;
specifically, edit the JSON entries that correspond to the participation summary
main sentence (the entries referenced in the review around the same region as
"maxAttainableCoverageExplanation") and insert the
<maxCoverageDisplay></maxCoverageDisplay> tag in the appropriate place in those
strings to mirror other locales.
In `@front_end/messages/pt.json`:
- Line 2082: The Portuguese locale is missing the {maxCoverageDisplay} insertion
in its participation summary strings, so add the placeholder where the
English/other locales show the “(max. X%)” text; update the relevant keys in
pt.json (e.g., the participation summary string(s) that accompany
"maxAttainableCoverageExplanation") to include the {maxCoverageDisplay} token in
the same position as other locales and ensure "maxCoverageDisplay" key
exists/has the correct translation alongside "maxAttainableCoverageExplanation".
In `@front_end/messages/zh-TW.json`:
- Line 2081: The Traditional Chinese locale is missing the {maxCoverageDisplay}
placeholder in the participation/coverage summary sentence templates (you
already have maxAttainableCoverageExplanation present); find the
participation/coverage summary keys in zh-TW (e.g., any keys like
participationSummary, coverageSummary, participationSummaryWithCount) and insert
the {maxCoverageDisplay} token where the inline max-coverage should appear so
the translated string mirrors the source template; keep surrounding punctuation
and spacing consistent with the EN source and do not alter
maxAttainableCoverageExplanation.
In
`@front_end/src/app/`(main)/questions/[id]/components/post_score_data/additional_scores_table.tsx:
- Around line 137-142: Replace the hardcoded English fragment "(max. ...)" in
the AdditionalScoresTable TSX with a localized string using the translations
hook (useTranslations) so the entire label is translatable; locate the JSX
around maxCoverage and Tooltip in additional_scores_table.tsx and wrap the whole
phrase in t or t.rich (passing the formatted value {(maxCoverage *
100).toFixed(1)}% as an interpolation) instead of the literal " (max. " and
closing ")". Ensure you remove the hardcoded punctuation and pass any
link/Tooltip content to t.rich to preserve existing behavior.
- Around line 90-99: getMaxCoverage can return non-finite or out-of-range
values; update the function (getMaxCoverage in additional_scores_table.tsx) to
validate that new Date(open_time), new Date(actual_close_time) and new
Date(scheduled_close_time) produce finite timestamps (guard with
isFinite/isNaN), ensure totalDuration > 0, compute rawCoverage = (actualClose -
open) / totalDuration and then clamp the returned value to the [0, 1] range
(return null for invalid/non-finite inputs). Keep the function signature and
null return behavior when inputs are invalid.
In
`@front_end/src/app/`(main)/questions/[id]/components/post_score_data/participation_summary.tsx:
- Around line 46-55: getMaxCoverage currently computes (actualClose -
open)/totalDuration without validating timestamps or bounding the ratio; update
the getMaxCoverage function to first validate parsed dates (use isNaN or
Number.isFinite on new Date(...).getTime()) and return null for invalid
timestamps or non-positive totalDuration, then compute the ratio and clamp it to
the [0, 1] range before returning; reference getMaxCoverage and the variables
open_time, actual_close_time, scheduled_close_time so you update that helper and
ensure callers that render percentage text also treat null or out-of-range
values safely.
- Around line 120-121: The hardcoded "(max. " and "%" in
participation_summary.tsx prevent localization; update the component to use
useTranslations() (e.g., const t = useTranslations('ParticipationSummary')) and
replace the literal bits around Math.round(maxCoverage * 100) with a single
localized string key that accepts the percentage as an interpolated variable
(for example t('max', { pct: Math.round(maxCoverage * 100) })), or use separate
keys if your i18n prefers prefix/suffix; ensure you remove the hardcoded " (max.
" and "%" and add the corresponding i18n key(s) in your locale files.
---
Nitpick comments:
In
`@front_end/src/app/`(main)/questions/[id]/components/post_score_data/additional_scores_table.tsx:
- Line 139: The max-coverage percentage is formatted differently here
((maxCoverage * 100).toFixed(1) + "%") than in participation_summary.tsx;
introduce or use a single shared formatter (e.g., formatMaxCoverage or
formatPercentage) and replace the local (maxCoverage * 100).toFixed(1) usage in
additional_scores_table.tsx (and update participation_summary.tsx to the same
helper) so both score surfaces use the same rounding/display rule; ensure the
helper accepts the raw maxCoverage number and returns the final string with
percent sign for consistent rendering across components.
In
`@front_end/src/app/`(main)/questions/[id]/components/post_score_data/participation_summary.tsx:
- Around line 42-55: Extract the getMaxCoverage function (signature: const
getMaxCoverage = (question: QuestionWithForecasts): number | null) into a shared
utility module and replace the duplicate implementations with an import from
that module; specifically, create a shared util exporting getMaxCoverage, remove
the local getMaxCoverage definitions in both score components, update their
imports to import { getMaxCoverage } from the new util, and ensure the util
accepts the same QuestionWithForecasts shape and preserves the exact return
semantics (null when inputs missing or totalDuration <= 0).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 84f009e2-8b93-45ce-9895-72ad32824d1c
📒 Files selected for processing (9)
front_end/messages/cs.jsonfront_end/messages/en.jsonfront_end/messages/es.jsonfront_end/messages/pt.jsonfront_end/messages/zh-TW.jsonfront_end/messages/zh.jsonfront_end/src/app/(main)/questions/[id]/components/post_score_data/additional_scores_table.tsxfront_end/src/app/(main)/questions/[id]/components/post_score_data/participation_summary.tsxfront_end/src/components/question/score_card.tsx
💤 Files with no reviewable changes (1)
- front_end/src/components/question/score_card.tsx
front_end/src/app/(main)/questions/[id]/components/post_score_data/additional_scores_table.tsx
Show resolved
Hide resolved
| <span className="text-sm text-gray-600 dark:text-gray-600-dark"> | ||
| {" (max. "} | ||
| {(maxCoverage * 100).toFixed(1)}% | ||
| <Tooltip | ||
| tooltipContent={t.rich("maxAttainableCoverageExplanation", { | ||
| link: (chunks) => ( |
There was a problem hiding this comment.
Avoid hardcoded English in max coverage label.
"(max. ...)" is hardcoded in TSX, so this fragment won’t localize correctly.
Based on learnings: Do not hardcode English strings in TSX components; prefer useTranslations() and i18n strings across components, avoiding hardcoded literals that stall localization.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@front_end/src/app/`(main)/questions/[id]/components/post_score_data/additional_scores_table.tsx
around lines 137 - 142, Replace the hardcoded English fragment "(max. ...)" in
the AdditionalScoresTable TSX with a localized string using the translations
hook (useTranslations) so the entire label is translatable; locate the JSX
around maxCoverage and Tooltip in additional_scores_table.tsx and wrap the whole
phrase in t or t.rich (passing the formatted value {(maxCoverage *
100).toFixed(1)}% as an interpolation) instead of the literal " (max. " and
closing ")". Ensure you remove the hardcoded punctuation and pass any
link/Tooltip content to t.rich to preserve existing behavior.
front_end/src/app/(main)/questions/[id]/components/post_score_data/participation_summary.tsx
Show resolved
Hide resolved
| {" (max. "} | ||
| {Math.round(maxCoverage * 100)}% |
There was a problem hiding this comment.
(max. …) label is still hardcoded English.
This prevents full localization of the participation summary string.
Based on learnings: Do not hardcode English strings in TSX components; prefer useTranslations() and i18n strings across components, avoiding hardcoded literals that stall localization.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@front_end/src/app/`(main)/questions/[id]/components/post_score_data/participation_summary.tsx
around lines 120 - 121, The hardcoded "(max. " and "%" in
participation_summary.tsx prevent localization; update the component to use
useTranslations() (e.g., const t = useTranslations('ParticipationSummary')) and
replace the literal bits around Math.round(maxCoverage * 100) with a single
localized string key that accepts the percentage as an interpolated variable
(for example t('max', { pct: Math.round(maxCoverage * 100) })), or use separate
keys if your i18n prefers prefix/suffix; ensure you remove the hardcoded " (max.
" and "%" and add the corresponding i18n key(s) in your locale files.
🚀 Preview EnvironmentYour preview environment is ready!
Details
ℹ️ Preview Environment InfoIsolation:
Limitations:
Cleanup:
|
…hub.com/metaculus/metaculus into issue/3887/feat/max-attainable-coverage
ncarazon
left a comment
There was a problem hiding this comment.
LGTM with minor suggestions.
| const getMaxCoverage = (question: QuestionWithForecasts): number | null => { | ||
| const { open_time, actual_close_time, scheduled_close_time } = question; | ||
| if (!open_time || !actual_close_time || !scheduled_close_time) return null; | ||
| const open = new Date(open_time).getTime(); | ||
| const actualClose = new Date(actual_close_time).getTime(); | ||
| const scheduledClose = new Date(scheduled_close_time).getTime(); | ||
| const totalDuration = scheduledClose - open; | ||
| if (totalDuration <= 0) return null; | ||
| return (actualClose - open) / totalDuration; | ||
| }; |
There was a problem hiding this comment.
getMaxCoverage is defined identically in both additional_scores_table.tsx and participation_summary.tsx. There's already a utils.ts in the same post_score_data/ directory – consider extracting getMaxCoverage there and importing it in both files.
| <span className="cursor-help text-sm text-gray-600 dark:text-gray-600-dark"> | ||
| (max. {(maxCoverage * 100).toFixed(1)}% |
There was a problem hiding this comment.
The max coverage display uses (maxCoverage * 100).toFixed(1) here (one decimal), but participation_summary.tsx uses Math.round(maxCoverage * 100) (integer). The same value shows differently in two places on the same page – e.g., "max. 73.2%" in the scores table vs "max. 73%" in the participation summary. Should these be consistent?
| className?: string; | ||
| variant?: Variant; | ||
| }> = ({ rows, className, variant = "auto" }) => ( | ||
| }> = ({ rows, className }) => ( |
There was a problem hiding this comment.
You removed "variant" usage but its still used by group_resolution_score_data/index.tsx:48. Confirm that does not cause any regression and remove variant prop completely instead of silently ignoring it.
closes #3887
adds max attainable coverage helper text to score and performance sections on question page
Summary by CodeRabbit
New Features
Localization
Bug Fixes