Skip to content
Draft
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 @@ -75,7 +75,7 @@ const IndividualQuestionPage: FC<{
)}
>
<div className="flex gap-4">
<div className="relative w-full">
<div className="relative w-full min-w-0">
{isCommunityQuestion && defaultProject && (
<div className="absolute z-0 -mt-[34px] hidden w-full sm:block">
<CommunityDisclaimer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,9 +224,7 @@ const KeyFactorsFeed: FC<Props> = ({ post, truncateText, hideOverlay }) => {
<KeyFactorsGridPlaceholder
key={`placeholder-${i}`}
className={i < 2 - totalItemCount ? "" : "hidden md:flex"}
onClick={
i === 0 && canAddKeyFactor ? handleAddClick : undefined
}
onClick={i === 0 && !isClosed ? handleAddClick : undefined}
/>
))}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,26 +15,30 @@ type Props = {
const KeyFactorsGridPlaceholder: FC<Props> = ({ className, onClick }) => {
const t = useTranslations();

return (
<div
className={cn(
"group flex min-h-[100px] items-center justify-center overflow-hidden rounded-xl bg-[#e8eeee] dark:bg-blue-300-dark",
onClick &&
"cursor-pointer border border-transparent p-5 hover:border-blue-500 dark:hover:border-blue-500-dark",
className
)}
onClick={onClick}
>
{onClick && (
<div className="flex flex-col items-center gap-3 text-blue-700 opacity-0 transition-opacity group-hover:opacity-100 dark:text-blue-700-dark">
<FontAwesomeIcon icon={faPlus} className="size-[22px]" />
<span className="whitespace-nowrap text-base capitalize leading-5">
{t("addKeyFactor")}
</span>
</div>
)}
const sharedClassName = cn(
"flex min-h-[100px] items-center justify-center overflow-hidden rounded-xl bg-[#e8eeee] dark:bg-blue-300-dark",
"border border-transparent p-5 opacity-50 transition-opacity hover:opacity-100",
onClick &&
"cursor-pointer hover:border-blue-500 dark:hover:border-blue-500-dark",
className
);

const content = (
<div className="flex flex-col items-center gap-3 text-blue-700 dark:text-blue-700-dark">
<FontAwesomeIcon icon={faPlus} className="size-[22px]" />
<span className="whitespace-nowrap text-base capitalize leading-5">
{t("addKeyFactor")}
</span>
</div>
);

return onClick ? (
<button type="button" className={sharedClassName} onClick={onClick}>
{content}
</button>
) : (
<div className={sharedClassName}>{content}</div>
);
};

export default KeyFactorsGridPlaceholder;
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@ function isResolvedNo(item: ChoiceItem, questionType: QuestionType): boolean {
);
}

function isResolvedYes(item: ChoiceItem, questionType: QuestionType): boolean {
if (questionType === QuestionType.Binary) return item.resolution === "yes";
return (
item.resolution !== null &&
!isUnsuccessfullyResolved(item.resolution) &&
item.choice.toLowerCase() === String(item.resolution).toLowerCase()
);
}

function getLastAggregationValue(item: ChoiceItem): number | null {
const values = item.aggregationValues;
for (let i = values.length - 1; i >= 0; i--) {
Expand Down Expand Up @@ -55,7 +64,10 @@ const CompactLegendBar: FC<Props> = ({
let hiddenCount: number;

if (showAll) {
visibleItems = items;
const resolvedNoItems = items.filter((item) =>
isResolvedNo(item, questionType)
);
visibleItems = [...normalItems, ...resolvedNoItems];
hiddenCount = 0;
} else if (!isMd) {
// Mobile: show up to MOBILE_MAX_ITEMS normal items only; resolved-NO always hidden
Expand All @@ -72,6 +84,7 @@ const CompactLegendBar: FC<Props> = ({
<div className="flex flex-wrap gap-x-3 gap-y-1.5">
{visibleItems.map((item) => {
const resolvedNo = isResolvedNo(item, questionType);
const resolvedYes = isResolvedYes(item, questionType);
const pct = getForecastPctDisplayValue(getLastAggregationValue(item));

return (
Expand All @@ -81,9 +94,11 @@ const CompactLegendBar: FC<Props> = ({
"flex items-center gap-1 rounded px-1.5 py-0.5",
resolvedNo
? "cursor-default"
: "cursor-pointer hover:bg-gray-200 dark:hover:bg-gray-200-dark",
item.highlighted && "bg-gray-200 dark:bg-gray-200-dark"
: "cursor-pointer hover:bg-gray-200 dark:hover:bg-gray-200-dark"
)}
onClick={() =>
!resolvedNo && onChoiceChange(item.choice, !item.active)
}
onMouseEnter={() =>
!resolvedNo && onChoiceHighlight(item.choice, true)
}
Expand All @@ -94,7 +109,7 @@ const CompactLegendBar: FC<Props> = ({
{resolvedNo ? (
<>
<span className="size-3 shrink-0 rounded-full bg-gray-300 dark:bg-gray-300-dark" />
<span className="max-w-[120px] truncate text-sm font-medium leading-4 text-gray-400 line-through dark:text-gray-400-dark md:text-base md:leading-5">
<span className="max-w-[300px] truncate text-sm font-medium leading-4 text-gray-400 line-through dark:text-gray-400-dark md:text-base md:leading-5">
{item.label || item.choice}
</span>
</>
Expand All @@ -114,7 +129,6 @@ const CompactLegendBar: FC<Props> = ({
}
: undefined
}
onClick={() => onChoiceChange(item.choice, !item.active)}
onKeyDown={(e) => {
if (e.key === " " || e.key === "Enter") {
e.preventDefault();
Expand All @@ -135,11 +149,11 @@ const CompactLegendBar: FC<Props> = ({
</svg>
)}
</span>
<span className="max-w-[120px] truncate text-sm font-medium leading-4 text-gray-800 dark:text-gray-800-dark md:text-base md:leading-5">
<span className="max-w-[300px] truncate text-sm font-medium leading-4 text-gray-800 dark:text-gray-800-dark md:text-base md:leading-5">
{item.label || item.choice}
</span>
<span className="shrink-0 text-sm tabular-nums leading-4 text-gray-600 dark:text-gray-600-dark md:text-base md:leading-6">
{pct}
{resolvedYes ? `(${t("Yes")})` : pct}
</span>
</>
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import React, { FC } from "react";

import ForecastMaker from "@/components/forecast_maker";
import BackgroundInfo from "@/components/question/background_info";
import ResolutionCriteria from "@/components/question/resolution_criteria";
import { PostWithForecasts } from "@/types/post";
import { QuestionType } from "@/types/question";
import { isContinuousQuestion } from "@/utils/questions/helpers";
Expand Down Expand Up @@ -57,8 +55,6 @@ const SingleQuestionScoreData: FC<Props> = ({
)}
<ResolutionScoreCards post={post} />
{isContinuousQuestion(question) && <ForecastMaker post={post} />}
<ResolutionCriteria post={post} defaultOpen />
<BackgroundInfo post={post} defaultOpen />
</div>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import DetailedQuestionCard from "@/components/detailed_question_card/detailed_q
import ForecastMaker from "@/components/forecast_maker";
import MarkdownEditor from "@/components/markdown_editor";
import CommunityDisclaimer from "@/components/post_card/community_disclaimer";
import ResolutionCriteria from "@/components/question/resolution_criteria";
import SectionToggle from "@/components/ui/section_toggle";
import { ContinuousChartCursorProvider } from "@/contexts/continuous_chart_cursor_context";
import { useHideCP } from "@/contexts/cp_context";
Expand All @@ -32,6 +33,7 @@ import { getQuestionForecastAvailability } from "@/utils/questions/forecastAvail
import { sortGroupPredictionOptions } from "@/utils/questions/groupOrdering";
import {
checkGroupOfQuestionsPostType,
isConditionalPost,
isContinuousQuestion,
isGroupOfQuestionsPost,
isMultipleChoicePost,
Expand Down Expand Up @@ -83,7 +85,10 @@ export const ForecasterShell: FC<
};
}, [postData.is_current_content_translated, locale, setBannerIsVisible]);

const sections = usePostTextSections(postData, { excludeFinePrint: true });
const sections = usePostTextSections(postData, {
excludeFinePrint: true,
excludeResolutionCriteria: true,
});
const isResolved = postData.status === PostStatus.RESOLVED;
const isGroup = isGroupOfQuestionsPost(postData);
const showChartDivider =
Expand Down Expand Up @@ -145,6 +150,7 @@ export const ForecasterShell: FC<
))}
</div>
{(!isResolved || isGroup) && <ForecastMaker post={postData} />}
<ResolutionCriteria post={postData} defaultOpen />
{sections.length > 0 && (
<div className="flex flex-col gap-2">
{sections.map((section) => (
Expand Down Expand Up @@ -270,7 +276,13 @@ export const ConsumerShell: FC<{
{t("predictionClosedMessage")}
</p>
)}
{isBinarySingleQuestion && isQuestionPost(postData) ? (
{isConditionalPost(postData) ? (
<QuestionTimeline
postData={postData}
isConsumerView
className="mt-0 hidden sm:block"
/>
) : isBinarySingleQuestion && isQuestionPost(postData) ? (
<div className="flex flex-col sm:flex-row sm:items-center sm:gap-0 md:gap-8">
<div className="order-1 flex w-64 flex-col items-center justify-center gap-[18px] self-center sm:self-stretch">
{hideCP ? (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,23 +35,28 @@ const MetaRow: FC<Props> = ({ post, className, variant }) => {
const { ref, width } = useContainerSize<HTMLDivElement>();

const projectsData = post.projects;
const defaultProject = projectsData?.default_project ?? null;
// default_project is shown first; the rest are deduped and go into "N more"
const allProjects: Project[] = projectsData
? [
...(defaultProject ? [defaultProject] : []),
...(projectsData.index ?? []),
...(projectsData.tournament ?? []),
...(projectsData.question_series ?? []),
...(projectsData.community ?? []),
...(projectsData.category ?? []),
...(projectsData.leaderboard_tag ?? []),
]
].filter(
(project, index, arr) =>
arr.findIndex((candidate) => candidate.id === project.id) === index
)
: [];

// Drop "forecasters" label first, then reduce visible chips
const compactCounters = width > 0 && width < 600;
const maxVisibleChips = width > 0 && width < 500 ? 1 : 2;

const visibleChips = allProjects.slice(0, maxVisibleChips);
const hiddenChips = allProjects.slice(maxVisibleChips);
const visibleChips = allProjects.slice(0, 1);
const hiddenChips = allProjects.slice(1);

return (
<div className={cn("px-4 lg:px-8", className)}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
import { useTranslations } from "next-intl";
import { FC, useEffect } from "react";

import useCoherenceLinksContext from "@/app/(main)/components/coherence_links_provider";
import { Tabs, TabsList, TabsTab } from "@/components/ui/tabs";
import { useBreakpoint } from "@/hooks/tailwind";
import { PostStatus, PostWithForecasts } from "@/types/post";
import cn from "@/utils/core/cn";

import { isDisplayableQuestionLink } from "../key_factors/utils";
import { shouldPostShowUserScores } from "../post_score_data/utils";
import { useQuestionLayout } from "../question_layout/question_layout_context";
import { hasTimeline } from "../question_view/consumer_question_view/timeline";
Expand Down Expand Up @@ -49,7 +51,11 @@ const QuestionPageShellTabBar: FC<Props> = ({ post, variant, className }) => {

const isSm = useBreakpoint("sm");
const commentCount = post.comment_count ?? 0;
const keyFactorsCount = post.key_factors?.length ?? 0;
const { aggregateCoherenceLinks } = useCoherenceLinksContext();
const questionLinksCount = aggregateCoherenceLinks.data.filter(
isDisplayableQuestionLink
).length;
const keyFactorsCount = (post.key_factors?.length ?? 0) + questionLinksCount;
const hasScores = shouldPostShowUserScores(post);
const hasSimilarQuestionsTab =
!isSm && post.curation_status === PostStatus.APPROVED;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,23 @@ const ConsumerGroupChart: FC<Props> = ({
chartHeight,
visibleQuestions,
}) => {
const { hoveredChoiceName, setHoveredChoiceName } = useListChartExpanded();
const { hoveredChoiceName, setHoveredChoiceName, chartAreaHeight } =
useListChartExpanded();
const { open_time, actual_close_time, scheduled_close_time, status } = post;
const refCloseTime = actual_close_time ?? scheduled_close_time;

const groupTimelineProps = visibleQuestions
? { questions: visibleQuestions }
: { group: post.group_of_questions };

// GroupChart renders a ~44px header (title + zoom picker) above the SVG div.
// Subtract it so header + SVG together fit within chartAreaHeight.
const CHART_HEADER_HEIGHT = 44;
const effectiveChartHeight =
chartAreaHeight > 0
? Math.max(80, chartAreaHeight - CHART_HEADER_HEIGHT)
: chartHeight;

return (
<div onMouseLeave={() => setHoveredChoiceName(null)}>
<GroupTimeline
Expand All @@ -41,7 +50,7 @@ const ConsumerGroupChart: FC<Props> = ({
withLegend={false}
withHighlightArea={false}
externalHighlightedChoice={hoveredChoiceName}
chartHeight={chartHeight}
chartHeight={effectiveChartHeight}
/>
</div>
);
Expand Down
Loading
Loading