Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ const ForecastMakerGroupContinuous: FC<Props> = ({
value={activeTableOption}
options={groupOptions}
onChange={setActiveTableOption}
questions={questions}
/>
{groupOptions.map((option) => {
const dataset = getNumericForecastDataset(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,16 @@ import {
import ResolutionIcon from "@/components/icons/resolution";
import { MultiSliderValue } from "@/components/sliders/multi_slider";
import RadioButton from "@/components/ui/radio_button";
import { Resolution } from "@/types/post";
import {
Bounds,
Resolution,
} from "@/types/post";
import {
Quartiles,
Question,
QuestionWithNumericForecasts,
} from "@/types/question";
import { formatResolution } from "@/utils/questions";
import { getDisplayValue } from "@/utils/charts";

export type ConditionalTableOption = {
id: number;
Expand All @@ -38,9 +41,15 @@ type Props = {
value: number | null;
options: ConditionalTableOption[];
onChange: (id: number) => void;
questions: QuestionWithNumericForecasts[];
};

const GroupForecastTable: FC<Props> = ({ options, value, onChange }) => {
const GroupForecastTable: FC<Props> = ({
options,
value,
onChange,
questions,
}) => {
const t = useTranslations();
const locale = useLocale();

Expand Down Expand Up @@ -155,8 +164,18 @@ const GroupForecastTable: FC<Props> = ({ options, value, onChange }) => {
})}
>
<PredictionCell
communityValue={option.communityQuartiles.lower25}
userValue={option.userQuartiles?.lower25}
communityValue={getDisplayValue(
option.communityQuartiles.lower25,
questions.find(
(question) => question.id === option.id
) as Question
)}
userValue={getDisplayValue(
option.userQuartiles?.lower25,
questions.find(
(question) => question.id === option.id
) as Question
)}
isDirty={option.isDirty}
/>
</Td>
Expand All @@ -166,8 +185,18 @@ const GroupForecastTable: FC<Props> = ({ options, value, onChange }) => {
})}
>
<PredictionCell
communityValue={option.communityQuartiles.median}
userValue={option.userQuartiles?.median}
communityValue={getDisplayValue(
option.communityQuartiles.median,
questions.find(
(question) => question.id === option.id
) as Question
)}
userValue={getDisplayValue(
option.userQuartiles?.median,
questions.find(
(question) => question.id === option.id
) as Question
)}
isDirty={option.isDirty}
/>
</Td>
Expand All @@ -179,8 +208,18 @@ const GroupForecastTable: FC<Props> = ({ options, value, onChange }) => {
<div className="flex">
<div className="w-full">
<PredictionCell
communityValue={option.communityQuartiles.upper75}
userValue={option.userQuartiles?.upper75}
communityValue={getDisplayValue(
option.communityQuartiles.upper75,
questions.find(
(question) => question.id === option.id
) as Question
)}
userValue={getDisplayValue(
option.userQuartiles?.upper75,
questions.find(
(question) => question.id === option.id
) as Question
)}
isDirty={option.isDirty}
/>
</div>
Expand All @@ -197,9 +236,9 @@ const GroupForecastTable: FC<Props> = ({ options, value, onChange }) => {
};

const PredictionCell: FC<{
communityValue: number;
communityValue: number | string;
isDirty: boolean;
userValue?: number;
userValue?: number | string;
}> = ({ communityValue, isDirty, userValue }) => (
<div className="grid grid-rows-2">
<div className="flex justify-center whitespace-nowrap text-olive-700 dark:text-olive-700-dark">
Expand Down
14 changes: 11 additions & 3 deletions front_end/src/components/charts/fan_chart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { METAC_COLORS } from "@/constants/colors";
import useAppTheme from "@/hooks/use_app_theme";
import useContainerSize from "@/hooks/use_container_size";
import { Area, FanOption, Line } from "@/types/charts";
import { Quartiles } from "@/types/question";
import { Quartiles, QuestionWithNumericForecasts } from "@/types/question";

const TOOLTIP_WIDTH = 150;

Expand Down Expand Up @@ -58,8 +58,16 @@ const FanChart: FC<Props> = ({

const tooltipItems = useMemo(
() =>
options.reduce<Record<string, Quartiles>>(
(acc, el) => ({ ...acc, [el.name]: el.quartiles }),
options.reduce<
Record<
string,
{ quartiles: Quartiles; question: QuestionWithNumericForecasts }
>
>(
(acc, el) => ({
...acc,
[el.name]: { quartiles: el.quartiles, question: el.question },
}),
{}
),
[options]
Expand Down
23 changes: 15 additions & 8 deletions front_end/src/components/charts/primitives/chart_fan_tooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@ import { useTranslations } from "next-intl";
import { ComponentProps, FC } from "react";
import { VictoryLabel } from "victory";

import { Quartiles } from "@/types/question";
import { Quartiles, QuestionWithNumericForecasts } from "@/types/question";
import { getDisplayValue } from "@/utils/charts";

const HEIGHT = 70;

type Props = ComponentProps<typeof VictoryLabel> & {
items: Record<string, Quartiles>;
items: Record<
string,
{ quartiles: Quartiles; question: QuestionWithNumericForecasts }
>;
width: number;
chartHeight: number;
};
Expand All @@ -27,11 +31,14 @@ const ChartFanTooltip: FC<Props> = ({
return null;
}

const quartiles = items[option];
if (!quartiles) {
const activeItem = items[option];
if (!activeItem) {
return null;
}

const quartiles = items[option].quartiles;
const question = items[option].question;

const padding = 10;
const position = y + padding + HEIGHT > chartHeight ? "top" : "bottom";

Expand All @@ -46,23 +53,23 @@ const ChartFanTooltip: FC<Props> = ({
<div className="flex flex-col rounded-sm border border-olive-700 bg-gray-0 p-1 dark:border-olive-700-dark dark:bg-gray-0-dark">
<TooltipItem
label={t("fanGraphThirdQuartileLabel")}
value={quartiles.upper75}
value={getDisplayValue(quartiles.upper75, question)}
/>
<TooltipItem
label={t("fanGraphSecondQuartileLabel")}
value={quartiles.median}
value={getDisplayValue(quartiles.median, question)}
/>
<TooltipItem
label={t("fanGraphFirstQuartileLabel")}
value={quartiles.lower25}
value={getDisplayValue(quartiles.lower25, question)}
/>
</div>
</foreignObject>
</g>
);
};

const TooltipItem: FC<{ label: string; value: number }> = ({
const TooltipItem: FC<{ label: string; value: string }> = ({
label,
value,
}) => (
Expand Down
3 changes: 2 additions & 1 deletion front_end/src/types/charts.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Quartiles } from "@/types/question";
import { Quartiles, QuestionWithNumericForecasts } from "@/types/question";

export type TickFormat = (
value: number,
Expand Down Expand Up @@ -31,6 +31,7 @@ export type FanOption = {
name: string;
quartiles: Quartiles;
resolved: boolean;
question: QuestionWithNumericForecasts;
};

export enum TimelineChartZoomOption {
Expand Down
4 changes: 3 additions & 1 deletion front_end/src/utils/charts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -435,12 +435,14 @@ export function getFanOptionsFromNumericGroup(
cdf: q.aggregations.recency_weighted.latest?.forecast_values ?? [],
resolvedAt: new Date(q.scheduled_resolve_time),
resolved: q.resolution !== null,
question: q,
}))
.sort((a, b) => differenceInMilliseconds(a.resolvedAt, b.resolvedAt))
.map(({ name, cdf, resolved }) => ({
.map(({ name, cdf, resolved, question }) => ({
name,
quartiles: computeQuartilesFromCDF(cdf),
resolved,
question,
}));
}

Expand Down