feat: embed question pages

This commit is contained in:
Vyacheslav Matyukhin 2022-05-26 17:11:19 +04:00
parent 19779bca02
commit addf87b22a
No known key found for this signature in database
GPG Key ID: 3D2A774C5489F96C
7 changed files with 168 additions and 47 deletions

View File

@ -0,0 +1,4 @@
export {
default,
getServerSideProps,
} from "../../../web/questions/pages/EmbedQuestionPage";

View File

@ -0,0 +1,16 @@
import { FaExternalLinkAlt } from "react-icons/fa";
import { QuestionFragment } from "../../fragments.generated";
export const PlatformLink: React.FC<{ question: QuestionFragment }> = ({
question,
}) => (
<a
className="px-2 py-1 border-2 border-gray-400 rounded-lg text-black no-underline text-normal hover:bg-gray-100 flex flex-nowrap space-x-1 items-center"
href={question.url}
target="_blank"
>
<span>{question.platform.label}</span>
<FaExternalLinkAlt className="text-gray-400 inline sm:text-md text-md" />
</a>
);

View File

@ -0,0 +1,19 @@
import { QuestionWithHistoryFragment } from "../../fragments.generated";
import { HistoryChart } from "./HistoryChart";
type Props = {
question: QuestionWithHistoryFragment;
};
export const QuestionChartOrVisualization: React.FC<Props> = ({ question }) =>
question.platform.id === "guesstimate" && question.visualization ? (
<a className="no-underline" href={question.url} target="_blank">
<img
className="rounded-sm"
src={question.visualization}
alt="Guesstimate Screenshot"
/>
</a>
) : question.options.length > 0 ? (
<HistoryChart question={question} />
) : null; /* Don't display chart if there are no options, for now. */

View File

@ -0,0 +1,20 @@
import { QuestionFragment } from "../../fragments.generated";
import { PlatformLink } from "./PlatformLink";
import { QuestionOptions } from "./QuestionOptions";
import { Stars } from "./Stars";
type Props = {
question: QuestionFragment;
};
export const QuestionInfoRow: React.FC<Props> = ({ question }) => (
<div className="flex items-center gap-2">
<PlatformLink question={question} />
<Stars num={question.qualityIndicators.stars} />
<QuestionOptions
question={{ ...question }}
maxNumOptions={1}
forcePrimaryMode={true}
/>
</div>
);

View File

@ -0,0 +1,26 @@
import { QuestionFragment } from "../../fragments.generated";
import { getBasePath } from "../../utils";
type Props = {
question: QuestionFragment;
linkToMetaforecast?: boolean;
};
export const QuestionTitle: React.FC<Props> = ({
question,
linkToMetaforecast,
}) => (
<h1 className="sm:text-3xl text-xl">
<a
className="text-black no-underline hover:text-gray-700"
href={
linkToMetaforecast
? getBasePath() + `/questions/${question.id}`
: question.url
}
target="_blank"
>
{question.title}
</a>
</h1>
);

View File

@ -0,0 +1,63 @@
import { GetServerSideProps, NextPage } from "next";
import NextError from "next/error";
import { Query } from "../../common/Query";
import { ssrUrql } from "../../urql";
import { QuestionChartOrVisualization } from "../components/QuestionChartOrVisualization";
import { QuestionInfoRow } from "../components/QuestionInfoRow";
import { QuestionTitle } from "../components/QuestionTitle";
import { QuestionPageDocument } from "../queries.generated";
interface Props {
id: string;
}
export const getServerSideProps: GetServerSideProps<Props> = async (
context
) => {
const [ssrCache, client] = ssrUrql();
const id = context.query.id as string;
const question =
(await client.query(QuestionPageDocument, { id }).toPromise()).data
?.result || null;
if (!question) {
context.res.statusCode = 404;
}
return {
props: {
urqlState: ssrCache.extractData(),
id,
},
};
};
const EmbedQuestionPage: NextPage<Props> = ({ id }) => {
return (
<div className="bg-white min-h-screen">
<Query document={QuestionPageDocument} variables={{ id }}>
{({ data: { result: question } }) =>
question ? (
<div className="p-4">
<QuestionTitle question={question} linkToMetaforecast={true} />
<div className="mb-5 mt-5">
<QuestionInfoRow question={question} />
</div>
<div className="mb-10">
<QuestionChartOrVisualization question={question} />
</div>
</div>
) : (
<NextError statusCode={404} />
)
}
</Query>
</div>
);
};
export default EmbedQuestionPage;

View File

@ -1,19 +1,20 @@
import { GetServerSideProps, NextPage } from "next"; import { GetServerSideProps, NextPage } from "next";
import NextError from "next/error"; import NextError from "next/error";
import { FaExternalLinkAlt } from "react-icons/fa";
import ReactMarkdown from "react-markdown"; import ReactMarkdown from "react-markdown";
import { Card } from "../../common/Card"; import { Card } from "../../common/Card";
import { CopyParagraph } from "../../common/CopyParagraph";
import { Layout } from "../../common/Layout"; import { Layout } from "../../common/Layout";
import { LineHeader } from "../../common/LineHeader"; import { LineHeader } from "../../common/LineHeader";
import { Query } from "../../common/Query"; import { Query } from "../../common/Query";
import { QuestionWithHistoryFragment } from "../../fragments.generated"; import { QuestionWithHistoryFragment } from "../../fragments.generated";
import { ssrUrql } from "../../urql"; import { ssrUrql } from "../../urql";
import { getBasePath } from "../../utils";
import { CaptureQuestion } from "../components/CaptureQuestion"; import { CaptureQuestion } from "../components/CaptureQuestion";
import { HistoryChart } from "../components/HistoryChart";
import { IndicatorsTable } from "../components/IndicatorsTable"; import { IndicatorsTable } from "../components/IndicatorsTable";
import { QuestionOptions } from "../components/QuestionOptions"; import { QuestionChartOrVisualization } from "../components/QuestionChartOrVisualization";
import { Stars } from "../components/Stars"; import { QuestionInfoRow } from "../components/QuestionInfoRow";
import { QuestionTitle } from "../components/QuestionTitle";
import { QuestionPageDocument } from "../queries.generated"; import { QuestionPageDocument } from "../queries.generated";
interface Props { interface Props {
@ -49,58 +50,19 @@ const Section: React.FC<{ title: string }> = ({ title, children }) => (
</div> </div>
); );
const PlatformLink: React.FC<{ question: QuestionWithHistoryFragment }> = ({
question,
}) => (
<a
className="px-2 py-1 border-2 border-gray-400 rounded-lg text-black no-underline text-normal hover:bg-gray-100 flex flex-nowrap space-x-1 items-center"
href={question.url}
target="_blank"
>
<span>{question.platform.label}</span>
<FaExternalLinkAlt className="text-gray-400 inline sm:text-md text-md" />
</a>
);
const LargeQuestionCard: React.FC<{ const LargeQuestionCard: React.FC<{
question: QuestionWithHistoryFragment; question: QuestionWithHistoryFragment;
}> = ({ question }) => { }> = ({ question }) => {
return ( return (
<Card highlightOnHover={false} large={true}> <Card highlightOnHover={false} large={true}>
<h1 className="sm:text-3xl text-xl"> <QuestionTitle question={question} />
<a
className="text-black no-underline hover:text-gray-700"
href={question.url}
target="_blank"
>
{question.title}
</a>
</h1>
<div className="flex items-center gap-2 mb-5 mt-5"> <div className="mb-5 mt-5">
<PlatformLink question={question} /> <QuestionInfoRow question={question} />
<Stars num={question.qualityIndicators.stars} />
<QuestionOptions
question={{ ...question }}
maxNumOptions={1}
forcePrimaryMode={true}
/>
</div> </div>
<div className="mb-10"> <div className="mb-10">
{ <QuestionChartOrVisualization question={question} />
question.platform.id === "guesstimate" && question.visualization ? (
<a className="no-underline" href={question.url} target="_blank">
<img
className="rounded-sm"
src={question.visualization}
alt="Guesstimate Screenshot"
/>
</a>
) : question.options.length > 0 ? (
<HistoryChart question={question} />
) : null /* Don't display chart if there are no options, for now. */
}
</div> </div>
<div className="mx-auto max-w-prose"> <div className="mx-auto max-w-prose">
@ -131,6 +93,17 @@ const QuestionScreen: React.FC<{ question: QuestionWithHistoryFragment }> = ({
<h1>Capture</h1> <h1>Capture</h1>
</LineHeader> </LineHeader>
<CaptureQuestion question={question} /> <CaptureQuestion question={question} />
<LineHeader>
<h1>Embed</h1>
</LineHeader>
<div className="max-w-md mx-auto">
<CopyParagraph
text={`<iframe src="${
getBasePath() + `/questions/embed/${question.id}`
}" height="600" width="600" frameborder="0" />`}
buttonText="Copy HTML"
/>
</div>
</div> </div>
</div> </div>
); );