diff --git a/front_end/src/app/(main)/questions/[id]/components/question_view/consumer_question_view/prediction/group_of_questions_prediction/index.tsx b/front_end/src/app/(main)/questions/[id]/components/question_view/consumer_question_view/prediction/group_of_questions_prediction/index.tsx index 7b77f9d265..c4f0c4a637 100644 --- a/front_end/src/app/(main)/questions/[id]/components/question_view/consumer_question_view/prediction/group_of_questions_prediction/index.tsx +++ b/front_end/src/app/(main)/questions/[id]/components/question_view/consumer_question_view/prediction/group_of_questions_prediction/index.tsx @@ -2,6 +2,7 @@ import DateForecastCard from "@/components/consumer_post_card/group_forecast_car import NumericForecastCard from "@/components/consumer_post_card/group_forecast_card/numeric_forecast_card"; import PercentageForecastCard from "@/components/consumer_post_card/group_forecast_card/percentage_forecast_card"; import TimeSeriesChart from "@/components/consumer_post_card/time_series_chart"; +import { useHideCP } from "@/contexts/cp_context"; import { GroupOfQuestionsGraphType, PostWithForecasts } from "@/types/post"; import { QuestionType } from "@/types/question"; import { getGroupForecastAvailability } from "@/utils/questions/forecastAvailability"; @@ -16,6 +17,7 @@ type Props = { }; const GroupOfQuestionsPrediction: React.FC = ({ postData }) => { + const { hideCP } = useHideCP(); let content: React.ReactNode | null = null; if ( @@ -35,8 +37,9 @@ const GroupOfQuestionsPrediction: React.FC = ({ postData }) => { // Hide chart if no forecasts or CP not yet revealed const shouldHideChart = - forecastAvailability && - (forecastAvailability.isEmpty || !!forecastAvailability.cpRevealsOn); + hideCP || + (forecastAvailability && + (forecastAvailability.isEmpty || !!forecastAvailability.cpRevealsOn)); if (!shouldHideChart) { const sortedQuestions = sortGroupPredictionOptions( diff --git a/front_end/src/app/(main)/questions/[id]/components/question_view/consumer_question_view/prediction/single_question_prediction/continuous_question_prediction.tsx b/front_end/src/app/(main)/questions/[id]/components/question_view/consumer_question_view/prediction/single_question_prediction/continuous_question_prediction.tsx index a9c653b8c6..394c8ffcae 100644 --- a/front_end/src/app/(main)/questions/[id]/components/question_view/consumer_question_view/prediction/single_question_prediction/continuous_question_prediction.tsx +++ b/front_end/src/app/(main)/questions/[id]/components/question_view/consumer_question_view/prediction/single_question_prediction/continuous_question_prediction.tsx @@ -3,6 +3,7 @@ import { getContinuousAreaChartData } from "@/components/charts/continuous_area_chart"; import MinifiedContinuousAreaChart from "@/components/charts/minified_continuous_area_chart"; import ConsumerContinuousTile from "@/components/consumer_post_card/consumer_question_tile/consumer_continuous_tile"; +import { useHideCP } from "@/contexts/cp_context"; import { QuestionStatus } from "@/types/post"; import { QuestionWithNumericForecasts } from "@/types/question"; import { getQuestionForecastAvailability } from "@/utils/questions/forecastAvailability"; @@ -12,6 +13,7 @@ type Props = { }; const ContinuousQuestionPrediction: React.FC = ({ question }) => { + const { hideCP } = useHideCP(); const forecastAvailability = getQuestionForecastAvailability(question); // Hide chart if no forecasts or CP not yet revealed @@ -40,6 +42,7 @@ const ContinuousQuestionPrediction: React.FC = ({ question }) => { height={50} forceTickCount={2} variant="question" + hideCP={hideCP} /> diff --git a/front_end/src/components/charts/minified_continuous_area_chart.tsx b/front_end/src/components/charts/minified_continuous_area_chart.tsx index 73d4efee2b..e220f6bac5 100644 --- a/front_end/src/components/charts/minified_continuous_area_chart.tsx +++ b/front_end/src/components/charts/minified_continuous_area_chart.tsx @@ -100,7 +100,9 @@ const MinifiedContinuousAreaChart: FC = ({ const discrete = question.type === QuestionType.Discrete; const charts = useMemo(() => { - const parsedData = hideCP ? [] : data; + const parsedData = hideCP + ? [...data].filter((el) => el.type === "user") + : data; const chartData: NumericPredictionGraph[] = []; for (const datum of parsedData) { @@ -235,11 +237,14 @@ const MinifiedContinuousAreaChart: FC = ({ // However, if there's a resolution point, we need extra padding to prevent clipping const hasResolution = !isNil(question.resolution) && question.resolution !== ""; - const wantsAnyXLabels = !hideCP && (!hideLabels || minMaxLabelsOnly); + const hasVisibleData = hideCP + ? data.some((el) => el.type === "user") + : true; + const wantsAnyXLabels = hasVisibleData && (!hideLabels || minMaxLabelsOnly); if (wantsAnyXLabels) return BOTTOM_PADDING; const baseMinimalPadding = hasResolution ? 8 : 3; // Extra padding for resolution diamond return baseMinimalPadding; - }, [hideCP, hideLabels, minMaxLabelsOnly, question.resolution]); + }, [data, hideCP, hideLabels, minMaxLabelsOnly, question.resolution]); return (
@@ -391,7 +396,7 @@ const MinifiedContinuousAreaChart: FC = ({ { - if (hideCP) return ""; + if (hideCP && !data.some((el) => el.type === "user")) return ""; if (hideLabels && !minMaxLabelsOnly) return ""; diff --git a/front_end/src/components/consumer_post_card/consumer_question_tile/consumer_continuous_tile.tsx b/front_end/src/components/consumer_post_card/consumer_question_tile/consumer_continuous_tile.tsx index a7eeeafbda..3717bf2dd4 100644 --- a/front_end/src/components/consumer_post_card/consumer_question_tile/consumer_continuous_tile.tsx +++ b/front_end/src/components/consumer_post_card/consumer_question_tile/consumer_continuous_tile.tsx @@ -3,6 +3,7 @@ import { FC } from "react"; import ContinuousCPBar from "@/components/consumer_post_card/consumer_question_tile/continuous_cp_bar"; import QuestionContinuousResolutionChip from "@/components/consumer_post_card/question_continuous_resolution_chip"; +import { useHideCP } from "@/contexts/cp_context"; import { QuestionStatus } from "@/types/post"; import { ForecastAvailability, QuestionWithForecasts } from "@/types/question"; import { getPredictionDisplayValue } from "@/utils/formatters/prediction"; @@ -19,6 +20,7 @@ const ConsumerContinuousTile: FC = ({ forecastAvailability, variant = "feed", }) => { + const { hideCP } = useHideCP(); const locale = useLocale(); const latest = @@ -55,7 +57,9 @@ const ConsumerContinuousTile: FC = ({ return (
= ({ question }) => { + const { hideCP } = useHideCP(); const forecastAvailability = getQuestionForecastAvailability(question); // Hide chart if no forecasts or CP not yet revealed const shouldHideChart = - forecastAvailability.isEmpty || !!forecastAvailability.cpRevealsOn; + hideCP || + forecastAvailability.isEmpty || + !!forecastAvailability.cpRevealsOn; // Open/Closed - delegate to specific tile components based on question type switch (question.type) { diff --git a/front_end/src/components/consumer_post_card/group_forecast_card/date_forecast_card/index.tsx b/front_end/src/components/consumer_post_card/group_forecast_card/date_forecast_card/index.tsx index 9aa6cfaf73..55b9b39279 100644 --- a/front_end/src/components/consumer_post_card/group_forecast_card/date_forecast_card/index.tsx +++ b/front_end/src/components/consumer_post_card/group_forecast_card/date_forecast_card/index.tsx @@ -16,6 +16,7 @@ import { import NumericForecastCard from "@/components/consumer_post_card/group_forecast_card/numeric_forecast_card"; import { darkTheme, lightTheme } from "@/constants/chart_theme"; import { METAC_COLORS } from "@/constants/colors"; +import { useHideCP } from "@/contexts/cp_context"; import useAppTheme from "@/hooks/use_app_theme"; import useContainerSize from "@/hooks/use_container_size"; import { ChoiceItem } from "@/types/choices"; @@ -53,6 +54,7 @@ const DateForecastCard: FC = ({ questionsGroup, height = 100, }) => { + const { hideCP } = useHideCP(); const { questions } = questionsGroup; const locale = useLocale(); const t = useTranslations(); @@ -86,8 +88,8 @@ const DateForecastCard: FC = ({ }); }; - if (points.length === 0) { - // Render empty state taken from the Numeric representation + if (points.length === 0 || hideCP) { + // Render list representation (with hidden values when hideCP) return ; } diff --git a/front_end/src/components/consumer_post_card/group_forecast_card/index.tsx b/front_end/src/components/consumer_post_card/group_forecast_card/index.tsx index f799561056..e0939a97d2 100644 --- a/front_end/src/components/consumer_post_card/group_forecast_card/index.tsx +++ b/front_end/src/components/consumer_post_card/group_forecast_card/index.tsx @@ -1,5 +1,6 @@ import { FC } from "react"; +import { useHideCP } from "@/contexts/cp_context"; import { GroupOfQuestionsGraphType, PostWithForecasts } from "@/types/post"; import { QuestionType } from "@/types/question"; import { getGroupForecastAvailability } from "@/utils/questions/forecastAvailability"; @@ -19,6 +20,8 @@ type Props = { }; const GroupForecastCard: FC = ({ post }) => { + const { hideCP } = useHideCP(); + // Check forecast availability for group posts const forecastAvailability = post.group_of_questions ? getGroupForecastAvailability(post.group_of_questions.questions) @@ -26,8 +29,9 @@ const GroupForecastCard: FC = ({ post }) => { // Hide chart if no forecasts or CP not yet revealed const shouldHideChart = - forecastAvailability && - (forecastAvailability.isEmpty || !!forecastAvailability.cpRevealsOn); + hideCP || + (forecastAvailability && + (forecastAvailability.isEmpty || !!forecastAvailability.cpRevealsOn)); if ( post.group_of_questions?.graph_type === GroupOfQuestionsGraphType.FanGraph diff --git a/front_end/src/components/consumer_post_card/group_forecast_card/numeric_forecast_card.tsx b/front_end/src/components/consumer_post_card/group_forecast_card/numeric_forecast_card.tsx index 043b3de7a4..043913723b 100644 --- a/front_end/src/components/consumer_post_card/group_forecast_card/numeric_forecast_card.tsx +++ b/front_end/src/components/consumer_post_card/group_forecast_card/numeric_forecast_card.tsx @@ -4,6 +4,7 @@ import { isNil } from "lodash"; import { useLocale, useTranslations } from "next-intl"; import { FC, useState } from "react"; +import { useHideCP } from "@/contexts/cp_context"; import { PostStatus, PostWithForecasts } from "@/types/post"; import { QuestionType, Scaling } from "@/types/question"; import { getPredictionDisplayValue } from "@/utils/formatters/prediction"; @@ -26,6 +27,7 @@ const NumericForecastCard: FC = ({ post, forceColorful }) => { const visibleChoicesCount = 3; const locale = useLocale(); const t = useTranslations(); + const { hideCP } = useHideCP(); const [expanded, setExpanded] = useState(false); if (!isGroupOfQuestionsPost(post)) { @@ -104,29 +106,32 @@ const NumericForecastCard: FC = ({ post, forceColorful }) => { range_max: scaling?.range_max ?? 1, zero_point: scaling?.zero_point ?? null, }; - const formattedChoiceValue = getPredictionDisplayValue( - rawChoiceValue, - { - questionType: isDateGroup - ? QuestionType.Date - : QuestionType.Numeric, - scaling: normalizedScaling, - actual_resolve_time: actual_resolve_time ?? null, - emptyLabel: t("Upcoming"), - } - ); + const formattedChoiceValue = + hideCP && isNil(resolution) + ? "—" + : getPredictionDisplayValue(rawChoiceValue, { + questionType: isDateGroup + ? QuestionType.Date + : QuestionType.Numeric, + scaling: normalizedScaling, + actual_resolve_time: actual_resolve_time ?? null, + emptyLabel: t("Upcoming"), + }); const scaledChoiceValue = !isNil(rawChoiceValue) ? scaleInternalLocation(rawChoiceValue, normalizedScaling) : NaN; - const relativeWidth = !isNil(resolution) - ? 100 - : calculateRelativeWidth({ - scaledChoiceValue, - maxScaledValue, - minScaledValue, - }); + const relativeWidth = + hideCP && isNil(resolution) + ? 0 + : !isNil(resolution) + ? 100 + : calculateRelativeWidth({ + scaledChoiceValue, + maxScaledValue, + minScaledValue, + }); return ( = ({ post, forceColorful }) => { const visibleChoicesCount = 3; const locale = useLocale(); const t = useTranslations(); + const { hideCP } = useHideCP(); const [expanded, setExpanded] = useState(false); const isMC = isMultipleChoicePost(post); @@ -44,19 +46,21 @@ const PercentageForecastCard: FC = ({ post, forceColorful }) => { const allChoices = useMemo(() => { const raw = generateChoiceItems(post, visibleChoicesCount, locale, t); return raw.map((choice) => { - const valueStr = getPredictionDisplayValue( - choice.aggregationValues.at(-1), - { - questionType: QuestionType.Binary, - scaling: choice.scaling, - actual_resolve_time: choice.actual_resolve_time ?? null, - emptyLabel, - } - ); + const valueStr = + hideCP && !choice.resolution + ? "—" + : getPredictionDisplayValue(choice.aggregationValues.at(-1), { + questionType: QuestionType.Binary, + scaling: choice.scaling, + actual_resolve_time: choice.actual_resolve_time ?? null, + emptyLabel, + }); const percent = - typeof valueStr === "string" - ? Number(valueStr.replace("%", "")) || 0 - : 0; + hideCP && !choice.resolution + ? 0 + : typeof valueStr === "string" + ? Number(valueStr.replace("%", "")) || 0 + : 0; const isChoiceClosed = choice.closeTime ? choice.closeTime < Date.now() @@ -69,7 +73,7 @@ const PercentageForecastCard: FC = ({ post, forceColorful }) => { isChoiceClosed, }; }); - }, [post, locale, t, emptyLabel]); + }, [post, locale, t, emptyLabel, hideCP]); if (!isMC && !isGroupOfQuestionsPost(post)) return null;