refactor: question components
This commit is contained in:
parent
ab6f17ffe0
commit
73a47d94c3
|
@ -3,57 +3,28 @@ import { QuestionFragment } from "../../search/queries.generated";
|
|||
type QualityIndicator = QuestionFragment["qualityIndicators"];
|
||||
type IndicatorName = keyof QualityIndicator;
|
||||
|
||||
const formatQualityIndicator = (indicator: IndicatorName) => {
|
||||
let result: string | null = null;
|
||||
switch (indicator) {
|
||||
case "numForecasts":
|
||||
result = null;
|
||||
break;
|
||||
// this duplication can probably be simplified with typescript magic, but this is good enough for now
|
||||
type UsedIndicatorName =
|
||||
| "volume"
|
||||
| "numForecasters"
|
||||
| "spread"
|
||||
| "sharesVolume"
|
||||
| "liquidity"
|
||||
| "tradeVolume"
|
||||
| "openInterest";
|
||||
|
||||
case "stars":
|
||||
result = null;
|
||||
break;
|
||||
|
||||
case "volume":
|
||||
result = "Volume";
|
||||
break;
|
||||
|
||||
case "numForecasters":
|
||||
result = "Forecasters";
|
||||
break;
|
||||
|
||||
// case "yesBid":
|
||||
// result = null; // "Yes bid"
|
||||
// break;
|
||||
|
||||
// case "yesAsk":
|
||||
// result = null; // "Yes ask"
|
||||
// break;
|
||||
|
||||
case "spread":
|
||||
result = "Spread";
|
||||
break;
|
||||
case "sharesVolume":
|
||||
result = "Shares vol.";
|
||||
break;
|
||||
|
||||
case "openInterest":
|
||||
result = "Interest";
|
||||
break;
|
||||
|
||||
// case "resolution_data":
|
||||
// result = null;
|
||||
// break;
|
||||
|
||||
case "liquidity":
|
||||
result = "Liquidity";
|
||||
break;
|
||||
|
||||
case "tradeVolume":
|
||||
result = "Volume";
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
const qualityIndicatorLabels: { [k in UsedIndicatorName]: string } = {
|
||||
// numForecasts: null,
|
||||
// stars: null,
|
||||
// yesBid: "Yes bid",
|
||||
// yesAsk: "Yes ask",
|
||||
volume: "Volume",
|
||||
numForecasters: "Forecasters",
|
||||
spread: "Spread",
|
||||
sharesVolume: "Shares vol.",
|
||||
liquidity: "Liquidity",
|
||||
tradeVolume: "Volume",
|
||||
openInterest: "Interest",
|
||||
};
|
||||
|
||||
const formatNumber = (num) => {
|
||||
|
@ -66,27 +37,16 @@ const formatNumber = (num) => {
|
|||
}
|
||||
};
|
||||
|
||||
const formatQualityIndicators = (qualityIndicators: QualityIndicator) => {
|
||||
let newQualityIndicators: { [k: string]: string | number } = {};
|
||||
for (const key of Object.keys(qualityIndicators)) {
|
||||
const newKey = formatQualityIndicator(key as IndicatorName);
|
||||
if (newKey && qualityIndicators[key] !== null) {
|
||||
newQualityIndicators[newKey] = qualityIndicators[key];
|
||||
}
|
||||
}
|
||||
return newQualityIndicators;
|
||||
};
|
||||
|
||||
/* Display functions*/
|
||||
|
||||
const getPercentageSymbolIfNeeded = ({
|
||||
indicator,
|
||||
platform,
|
||||
}: {
|
||||
indicator: string;
|
||||
indicator: UsedIndicatorName;
|
||||
platform: string;
|
||||
}) => {
|
||||
let indicatorsWhichNeedPercentageSymbol = ["Spread"];
|
||||
let indicatorsWhichNeedPercentageSymbol: IndicatorName[] = ["spread"];
|
||||
if (indicatorsWhichNeedPercentageSymbol.includes(indicator)) {
|
||||
return "%";
|
||||
} else {
|
||||
|
@ -98,10 +58,15 @@ const getCurrencySymbolIfNeeded = ({
|
|||
indicator,
|
||||
platform,
|
||||
}: {
|
||||
indicator: string;
|
||||
indicator: UsedIndicatorName;
|
||||
platform: string;
|
||||
}) => {
|
||||
let indicatorsWhichNeedCurrencySymbol = ["Volume", "Interest", "Liquidity"];
|
||||
const indicatorsWhichNeedCurrencySymbol: IndicatorName[] = [
|
||||
"volume",
|
||||
"tradeVolume",
|
||||
"openInterest",
|
||||
"liquidity",
|
||||
];
|
||||
let dollarPlatforms = ["predictit", "kalshi", "polymarket"];
|
||||
if (indicatorsWhichNeedCurrencySymbol.includes(indicator)) {
|
||||
if (dollarPlatforms.includes(platform)) {
|
||||
|
@ -114,66 +79,50 @@ const getCurrencySymbolIfNeeded = ({
|
|||
}
|
||||
};
|
||||
|
||||
const showFirstQualityIndicator: React.FC<{
|
||||
const FirstQualityIndicator: React.FC<{
|
||||
question: QuestionFragment;
|
||||
showTimeStamp: boolean;
|
||||
}> = ({ question, showTimeStamp }) => {
|
||||
const lastUpdated = new Date(question.timestamp * 1000);
|
||||
if (!!question.qualityIndicators.numForecasts) {
|
||||
}> = ({ question }) => {
|
||||
if (question.qualityIndicators.numForecasts) {
|
||||
return (
|
||||
<div className="flex col-span-1 row-span-1">
|
||||
{/*<span>{` ${numforecasts == 1 ? "Forecast" : "Forecasts:"}`}</span> */}
|
||||
<div className="flex">
|
||||
<span>Forecasts:</span>
|
||||
<span className="font-bold">
|
||||
{Number(question.qualityIndicators.numForecasts).toFixed(0)}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
} else if (showTimeStamp) {
|
||||
return (
|
||||
<span className="hidden sm:flex items-center justify-center text-gray-600 mt-2">
|
||||
<svg className="ml-4 mr-1 mt-1" height="10" width="16">
|
||||
<circle cx="4" cy="4" r="4" fill="rgb(29, 78, 216)" />
|
||||
</svg>
|
||||
{`Last updated: ${
|
||||
lastUpdated ? lastUpdated.toISOString().slice(0, 10) : "unknown"
|
||||
}`}
|
||||
</span>
|
||||
);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const displayQualityIndicators: React.FC<{
|
||||
const QualityIndicatorsList: React.FC<{
|
||||
question: QuestionFragment;
|
||||
showTimeStamp: boolean;
|
||||
}> = ({ question, showTimeStamp }) => {
|
||||
const { qualityIndicators } = question;
|
||||
}> = ({ question }) => {
|
||||
return (
|
||||
<div className="text-sm">
|
||||
{showFirstQualityIndicator({
|
||||
question,
|
||||
showTimeStamp,
|
||||
})}
|
||||
{Object.entries(formatQualityIndicators(question.qualityIndicators)).map(
|
||||
(entry, i) => {
|
||||
<FirstQualityIndicator question={question} />
|
||||
{Object.entries(question.qualityIndicators).map((entry, i) => {
|
||||
const indicatorLabel = qualityIndicatorLabels[entry[0]];
|
||||
if (!indicatorLabel || entry[1] === null) return;
|
||||
const indicator = entry[0] as UsedIndicatorName; // guaranteed by the previous line
|
||||
const value = entry[1];
|
||||
|
||||
return (
|
||||
<div className="col-span-1 row-span-1">
|
||||
<span>${entry[0]}:</span>
|
||||
<div key={indicator}>
|
||||
<span>{indicatorLabel}:</span>
|
||||
<span className="font-bold">
|
||||
{`${getCurrencySymbolIfNeeded({
|
||||
indicator: entry[0],
|
||||
indicator,
|
||||
platform: question.platform.id,
|
||||
})}${formatNumber(entry[1])}${getPercentageSymbolIfNeeded({
|
||||
indicator: entry[0],
|
||||
})}${formatNumber(value)}${getPercentageSymbolIfNeeded({
|
||||
indicator,
|
||||
platform: question.platform.id,
|
||||
})}`}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
)}
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -235,16 +184,13 @@ function getStarsColor(numstars: number) {
|
|||
|
||||
interface Props {
|
||||
question: QuestionFragment;
|
||||
showTimeStamp: boolean;
|
||||
expandFooterToFullWidth: boolean;
|
||||
}
|
||||
|
||||
export const QuestionFooter: React.FC<Props> = ({
|
||||
question,
|
||||
showTimeStamp,
|
||||
expandFooterToFullWidth,
|
||||
}) => {
|
||||
let debuggingWithBackground = false;
|
||||
return (
|
||||
<div
|
||||
className={`grid grid-cols-3 ${
|
||||
|
@ -254,14 +200,14 @@ export const QuestionFooter: React.FC<Props> = ({
|
|||
<div
|
||||
className={`self-center col-span-1 ${getStarsColor(
|
||||
question.qualityIndicators.stars
|
||||
)} ${debuggingWithBackground ? "bg-red-200" : ""}`}
|
||||
)}`}
|
||||
>
|
||||
{getstars(question.qualityIndicators.stars)}
|
||||
</div>
|
||||
<div
|
||||
className={`${
|
||||
expandFooterToFullWidth ? "place-self-center" : "self-center"
|
||||
} col-span-1 font-bold ${debuggingWithBackground ? "bg-red-100" : ""}`}
|
||||
} col-span-1 font-bold`}
|
||||
>
|
||||
{question.platform.label
|
||||
.replace("Good Judgment Open", "GJOpen")
|
||||
|
@ -272,12 +218,9 @@ export const QuestionFooter: React.FC<Props> = ({
|
|||
expandFooterToFullWidth
|
||||
? "justify-self-end mr-4"
|
||||
: "justify-self-center"
|
||||
} col-span-1 ${debuggingWithBackground ? "bg-red-100" : ""}`}
|
||||
} col-span-1`}
|
||||
>
|
||||
{displayQualityIndicators({
|
||||
question,
|
||||
showTimeStamp,
|
||||
})}
|
||||
<QualityIndicatorsList question={question} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -4,7 +4,6 @@ import ReactMarkdown from "react-markdown";
|
|||
|
||||
import { CopyText } from "../../common/CopyText";
|
||||
import { QuestionOptions } from "../../questions/components/QuestionOptions";
|
||||
import { formatProbability } from "../../questions/utils";
|
||||
import { QuestionFragment } from "../../search/queries.generated";
|
||||
import { Card } from "../Card";
|
||||
import { QuestionFooter } from "./QuestionFooter";
|
||||
|
@ -87,99 +86,12 @@ const cleanText = (text: string): string => {
|
|||
return textString;
|
||||
};
|
||||
|
||||
const primaryForecastColor = (probability: number) => {
|
||||
if (probability < 0.03) {
|
||||
return "bg-red-600";
|
||||
} else if (probability < 0.1) {
|
||||
return "bg-red-600 opacity-80";
|
||||
} else if (probability < 0.2) {
|
||||
return "bg-red-600 opacity-70";
|
||||
} else if (probability < 0.3) {
|
||||
return "bg-red-600 opacity-60";
|
||||
} else if (probability < 0.4) {
|
||||
return "bg-red-600 opacity-50";
|
||||
} else if (probability < 0.5) {
|
||||
return "bg-gray-500";
|
||||
} else if (probability < 0.6) {
|
||||
return "bg-gray-500";
|
||||
} else if (probability < 0.7) {
|
||||
return "bg-green-600 opacity-50";
|
||||
} else if (probability < 0.8) {
|
||||
return "bg-green-600 opacity-60";
|
||||
} else if (probability < 0.9) {
|
||||
return "bg-green-600 opacity-70";
|
||||
} else if (probability < 0.97) {
|
||||
return "bg-green-600 opacity-80";
|
||||
} else {
|
||||
return "bg-green-600";
|
||||
}
|
||||
};
|
||||
|
||||
const textColor = (probability: number) => {
|
||||
if (probability < 0.03) {
|
||||
return "text-red-600";
|
||||
} else if (probability < 0.1) {
|
||||
return "text-red-600 opacity-80";
|
||||
} else if (probability < 0.2) {
|
||||
return "text-red-600 opacity-80";
|
||||
} else if (probability < 0.3) {
|
||||
return "text-red-600 opacity-70";
|
||||
} else if (probability < 0.4) {
|
||||
return "text-red-600 opacity-70";
|
||||
} else if (probability < 0.5) {
|
||||
return "text-gray-500";
|
||||
} else if (probability < 0.6) {
|
||||
return "text-gray-500";
|
||||
} else if (probability < 0.7) {
|
||||
return "text-green-600 opacity-70";
|
||||
} else if (probability < 0.8) {
|
||||
return "text-green-600 opacity-70";
|
||||
} else if (probability < 0.9) {
|
||||
return "text-green-600 opacity-80";
|
||||
} else if (probability < 0.97) {
|
||||
return "text-green-600 opacity-80";
|
||||
} else {
|
||||
return "text-green-600";
|
||||
}
|
||||
};
|
||||
|
||||
const primaryEstimateAsText = (probability: number) => {
|
||||
if (probability < 0.03) {
|
||||
return "Exceptionally unlikely";
|
||||
} else if (probability < 0.1) {
|
||||
return "Very unlikely";
|
||||
} else if (probability < 0.4) {
|
||||
return "Unlikely";
|
||||
} else if (probability < 0.6) {
|
||||
return "About Even";
|
||||
} else if (probability < 0.9) {
|
||||
return "Likely";
|
||||
} else if (probability < 0.97) {
|
||||
return "Very likely";
|
||||
} else {
|
||||
return "Virtually certain";
|
||||
}
|
||||
};
|
||||
|
||||
// Logical checks
|
||||
|
||||
const checkIfDisplayTimeStampAtBottom = (qualityIndicators: {
|
||||
[k: string]: any;
|
||||
}) => {
|
||||
let indicators = Object.keys(qualityIndicators);
|
||||
if (indicators.length == 1 && indicators[0] == "stars") {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Auxiliary components
|
||||
|
||||
const DisplayMarkdown: React.FC<{ description: string }> = ({
|
||||
description,
|
||||
}) => {
|
||||
let formatted = truncateText(250, cleanText(description));
|
||||
const formatted = truncateText(250, cleanText(description));
|
||||
// overflow-hidden overflow-ellipsis h-24
|
||||
return formatted === "" ? null : (
|
||||
<div className="overflow-clip">
|
||||
|
@ -217,19 +129,10 @@ export const DisplayQuestion: React.FC<Props> = ({
|
|||
expandFooterToFullWidth,
|
||||
showIdToggle,
|
||||
}) => {
|
||||
const {
|
||||
platform,
|
||||
description,
|
||||
options,
|
||||
qualityIndicators,
|
||||
timestamp,
|
||||
visualization,
|
||||
} = question;
|
||||
const lastUpdated = new Date(timestamp * 1000);
|
||||
const displayTimestampAtBottom =
|
||||
checkIfDisplayTimeStampAtBottom(qualityIndicators);
|
||||
const { options } = question;
|
||||
const lastUpdated = new Date(question.timestamp * 1000);
|
||||
|
||||
const yesNoOptions =
|
||||
const isBinary =
|
||||
options.length === 2 &&
|
||||
(options[0].name === "Yes" || options[0].name === "No");
|
||||
|
||||
|
@ -243,7 +146,7 @@ export const DisplayQuestion: React.FC<Props> = ({
|
|||
</div>
|
||||
) : null}
|
||||
<div>
|
||||
<Link href={`/questions/${question.id}`}>
|
||||
<Link href={`/questions/${question.id}`} passHref>
|
||||
<a className="float-right block ml-2 mt-1.5">
|
||||
<FaExpand
|
||||
size="18"
|
||||
|
@ -261,41 +164,17 @@ export const DisplayQuestion: React.FC<Props> = ({
|
|||
</a>
|
||||
</Card.Title>
|
||||
</div>
|
||||
{yesNoOptions && (
|
||||
{isBinary ? (
|
||||
<div className="flex justify-between">
|
||||
<div className="space-x-2">
|
||||
<span
|
||||
className={`${primaryForecastColor(
|
||||
options[0].probability
|
||||
)} text-white w-16 rounded-md px-1.5 py-0.5 font-bold`}
|
||||
>
|
||||
{formatProbability(options[0].probability)}
|
||||
</span>
|
||||
<span
|
||||
className={`${textColor(
|
||||
options[0].probability
|
||||
)} text-gray-500 inline-block`}
|
||||
>
|
||||
{primaryEstimateAsText(options[0].probability)}
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className={`hidden ${
|
||||
showTimeStamp && !displayTimestampAtBottom ? "sm:block" : ""
|
||||
}`}
|
||||
>
|
||||
<QuestionOptions options={options} />
|
||||
<div className={`hidden ${showTimeStamp ? "sm:block" : ""}`}>
|
||||
<LastUpdated timestamp={lastUpdated} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{!yesNoOptions && (
|
||||
) : (
|
||||
<div className="space-y-2">
|
||||
<QuestionOptions options={options} />
|
||||
<div
|
||||
className={`hidden ${
|
||||
showTimeStamp && !displayTimestampAtBottom ? "sm:block" : ""
|
||||
} ml-6`}
|
||||
>
|
||||
<div className={`hidden ${showTimeStamp ? "sm:block" : ""} ml-6`}>
|
||||
<LastUpdated timestamp={lastUpdated} />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -303,14 +182,14 @@ export const DisplayQuestion: React.FC<Props> = ({
|
|||
|
||||
{question.platform.id !== "guesstimate" && options.length < 3 && (
|
||||
<div className="text-gray-500">
|
||||
<DisplayMarkdown description={description} />
|
||||
<DisplayMarkdown description={question.description} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{question.platform.id === "guesstimate" && (
|
||||
<img
|
||||
className="rounded-sm"
|
||||
src={visualization}
|
||||
src={question.visualization}
|
||||
alt="Guesstimate Screenshot"
|
||||
/>
|
||||
)}
|
||||
|
@ -324,7 +203,6 @@ export const DisplayQuestion: React.FC<Props> = ({
|
|||
<div className="w-full">
|
||||
<QuestionFooter
|
||||
question={question}
|
||||
showTimeStamp={showTimeStamp && displayTimestampAtBottom}
|
||||
expandFooterToFullWidth={expandFooterToFullWidth}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -1,7 +1,83 @@
|
|||
import { QuestionFragment } from "../../search/queries.generated";
|
||||
import { formatProbability } from "../utils";
|
||||
|
||||
const OptionRow: React.FC<{ option: any }> = ({ option }) => {
|
||||
const chooseColor = (probability: number) => {
|
||||
type Option = QuestionFragment["options"][0];
|
||||
|
||||
const textColor = (probability: number) => {
|
||||
if (probability < 0.03) {
|
||||
return "text-red-600";
|
||||
} else if (probability < 0.1) {
|
||||
return "text-red-600 opacity-80";
|
||||
} else if (probability < 0.2) {
|
||||
return "text-red-600 opacity-80";
|
||||
} else if (probability < 0.3) {
|
||||
return "text-red-600 opacity-70";
|
||||
} else if (probability < 0.4) {
|
||||
return "text-red-600 opacity-70";
|
||||
} else if (probability < 0.5) {
|
||||
return "text-gray-500";
|
||||
} else if (probability < 0.6) {
|
||||
return "text-gray-500";
|
||||
} else if (probability < 0.7) {
|
||||
return "text-green-600 opacity-70";
|
||||
} else if (probability < 0.8) {
|
||||
return "text-green-600 opacity-70";
|
||||
} else if (probability < 0.9) {
|
||||
return "text-green-600 opacity-80";
|
||||
} else if (probability < 0.97) {
|
||||
return "text-green-600 opacity-80";
|
||||
} else {
|
||||
return "text-green-600";
|
||||
}
|
||||
};
|
||||
|
||||
const primaryForecastColor = (probability: number) => {
|
||||
if (probability < 0.03) {
|
||||
return "bg-red-600";
|
||||
} else if (probability < 0.1) {
|
||||
return "bg-red-600 opacity-80";
|
||||
} else if (probability < 0.2) {
|
||||
return "bg-red-600 opacity-70";
|
||||
} else if (probability < 0.3) {
|
||||
return "bg-red-600 opacity-60";
|
||||
} else if (probability < 0.4) {
|
||||
return "bg-red-600 opacity-50";
|
||||
} else if (probability < 0.5) {
|
||||
return "bg-gray-500";
|
||||
} else if (probability < 0.6) {
|
||||
return "bg-gray-500";
|
||||
} else if (probability < 0.7) {
|
||||
return "bg-green-600 opacity-50";
|
||||
} else if (probability < 0.8) {
|
||||
return "bg-green-600 opacity-60";
|
||||
} else if (probability < 0.9) {
|
||||
return "bg-green-600 opacity-70";
|
||||
} else if (probability < 0.97) {
|
||||
return "bg-green-600 opacity-80";
|
||||
} else {
|
||||
return "bg-green-600";
|
||||
}
|
||||
};
|
||||
|
||||
const primaryEstimateAsText = (probability: number) => {
|
||||
if (probability < 0.03) {
|
||||
return "Exceptionally unlikely";
|
||||
} else if (probability < 0.1) {
|
||||
return "Very unlikely";
|
||||
} else if (probability < 0.4) {
|
||||
return "Unlikely";
|
||||
} else if (probability < 0.6) {
|
||||
return "About Even";
|
||||
} else if (probability < 0.9) {
|
||||
return "Likely";
|
||||
} else if (probability < 0.97) {
|
||||
return "Very likely";
|
||||
} else {
|
||||
return "Virtually certain";
|
||||
}
|
||||
};
|
||||
|
||||
const chooseColor = (probability: number) => {
|
||||
if (probability < 0.1) {
|
||||
return "bg-blue-50 text-blue-500";
|
||||
} else if (probability < 0.3) {
|
||||
|
@ -11,8 +87,9 @@ const OptionRow: React.FC<{ option: any }> = ({ option }) => {
|
|||
} else {
|
||||
return "bg-blue-300 text-blue-800";
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const OptionRow: React.FC<{ option: Option }> = ({ option }) => {
|
||||
return (
|
||||
<div className="flex items-center">
|
||||
<div
|
||||
|
@ -29,9 +106,36 @@ const OptionRow: React.FC<{ option: any }> = ({ option }) => {
|
|||
);
|
||||
};
|
||||
|
||||
export const QuestionOptions: React.FC<{ options: any[] }> = ({ options }) => {
|
||||
export const QuestionOptions: React.FC<{ options: Option[] }> = ({
|
||||
options,
|
||||
}) => {
|
||||
const isBinary =
|
||||
options.length === 2 &&
|
||||
(options[0].name === "Yes" || options[0].name === "No");
|
||||
|
||||
const optionsSorted = options.sort((a, b) => b.probability - a.probability);
|
||||
const optionsMax5 = !!optionsSorted.slice ? optionsSorted.slice(0, 5) : []; // display max 5 options.
|
||||
|
||||
if (isBinary) {
|
||||
return (
|
||||
<div className="space-x-2">
|
||||
<span
|
||||
className={`${primaryForecastColor(
|
||||
options[0].probability
|
||||
)} text-white w-16 rounded-md px-1.5 py-0.5 font-bold`}
|
||||
>
|
||||
{formatProbability(options[0].probability)}
|
||||
</span>
|
||||
<span
|
||||
className={`${textColor(
|
||||
options[0].probability
|
||||
)} text-gray-500 inline-block`}
|
||||
>
|
||||
{primaryEstimateAsText(options[0].probability)}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
{optionsMax5.map((option, i) => (
|
||||
|
@ -39,4 +143,5 @@ export const QuestionOptions: React.FC<{ options: any[] }> = ({ options }) => {
|
|||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -3,6 +3,7 @@ import ReactMarkdown from "react-markdown";
|
|||
|
||||
import { Query } from "../../common/Query";
|
||||
import { Card } from "../../display/Card";
|
||||
import { QuestionFooter } from "../../display/DisplayQuestion/QuestionFooter";
|
||||
import { Layout } from "../../display/Layout";
|
||||
import { QuestionFragment } from "../../search/queries.generated";
|
||||
import { ssrUrql } from "../../urql";
|
||||
|
@ -39,8 +40,18 @@ const QuestionCardContents: React.FC<{ question: QuestionFragment }> = ({
|
|||
question,
|
||||
}) => (
|
||||
<div className="space-y-4">
|
||||
<h1>{question.title}</h1>
|
||||
<h1>
|
||||
<a
|
||||
className="text-black no-underline"
|
||||
href={question.url}
|
||||
target="_blank"
|
||||
>
|
||||
{question.title}
|
||||
</a>
|
||||
</h1>
|
||||
<QuestionFooter question={question} expandFooterToFullWidth={true} />
|
||||
<QuestionOptions options={question.options} />
|
||||
|
||||
<ReactMarkdown linkTarget="_blank" className="font-normal">
|
||||
{question.description}
|
||||
</ReactMarkdown>
|
||||
|
|
Loading…
Reference in New Issue
Block a user