feat: add ProblemDetailPage

This commit is contained in:
Paul Pan 2024-03-16 16:46:50 +08:00
parent df7f87d6bf
commit afbf796208
3 changed files with 214 additions and 0 deletions

View File

@ -0,0 +1,157 @@
import type React from "react";
import { useEffect, useState } from "react";
import {
Accordion,
AccordionButton,
AccordionIcon,
AccordionItem,
AccordionPanel,
Box,
Link,
Select,
Table,
Tag,
Tbody,
Td,
Tr,
Wrap,
WrapItem,
} from "@chakra-ui/react";
import { Link as ReactRouterLink } from "react-router-dom";
import type { DetailResponse, RuntimeInfo } from "../app/services/problem";
const TitleItem = ({ word }: { word: string }) => (
<h2>
<AccordionButton>
<Box as="span" flex="1" textAlign="left">
{word}
</Box>
<AccordionIcon />
</AccordionButton>
</h2>
);
interface ProblemInfoMenuProps {
data?: DetailResponse;
onLanguageSelect?: (lang: string) => void;
action?: React.ReactNode;
}
const EmptyRuntime: RuntimeInfo = {
TimeLimit: 0,
MemoryLimit: 0,
NProcLimit: 0,
};
export default function ProblemInfoMenu(props: ProblemInfoMenuProps) {
const [lang, setLang] = useState(props.data?.context.Languages[0]?.Lang || "c");
const runtime = props.data?.context.Languages.find((x) => x.Lang === lang)?.Runtime.Run || EmptyRuntime;
useEffect(() => {
props.onLanguageSelect && props.onLanguageSelect(lang);
}, [lang, props]);
const problemInfo = (
<Table variant="striped" size="sm">
<Tbody>
<Tr>
<Td>Title</Td>
<Td>{props.data?.problem.title || "..."}</Td>
</Tr>
<Tr>
<Td>Author</Td>
<Td>
<Link
as={ReactRouterLink}
color="blue.500"
to={`/profile/${props.data?.problem.provider.meta.ID || 0}`}
>
{props.data?.problem.provider.nick_name || "..."}
</Link>
</Td>
</Tr>
<Tr>
<Td>Languages</Td>
<Td>
<Wrap>
{props.data?.context.Languages.map((l) => (
<WrapItem key={l.Lang}>
<Tag variant="outline" size="sm" colorScheme="pink">
{l.Lang}
</Tag>
</WrapItem>
))}
</Wrap>
</Td>
</Tr>
<Tr>
<Td>Tags</Td>
<Td>
<Wrap>
{props.data?.problem.tags.Elements.map((t) => (
<WrapItem key={t}>
<Tag variant="outline" size="sm" colorScheme="orange">
{t}
</Tag>
</WrapItem>
))}
</Wrap>
</Td>
</Tr>
</Tbody>
</Table>
);
const taskInfo = (
<Table variant="striped" size="sm">
<Tbody>
<Tr>
<Td>Time Limit</Td>
<Td>{runtime.TimeLimit} ms</Td>
</Tr>
<Tr>
<Td>Memory Limit</Td>
<Td>{runtime.MemoryLimit} MB</Td>
</Tr>
<Tr>
<Td>Process Limit</Td>
<Td>{runtime.NProcLimit}</Td>
</Tr>
<Tr>
<Td>Select</Td>
<Td>
<Select size="sm" onChange={(e) => setLang(e.target.value)} value={lang}>
{props.data?.context.Languages.map((l) => (
<option key={l.Lang} value={l.Lang}>
{l.Lang}
</option>
))}
</Select>
</Td>
</Tr>
</Tbody>
</Table>
);
return (
<>
<Accordion defaultIndex={[0, 1, ...(props.action ? [2] : [])]} allowMultiple>
<AccordionItem>
<TitleItem word="Problem Info" />
<AccordionPanel pb={4}>{problemInfo}</AccordionPanel>
</AccordionItem>
<AccordionItem>
<TitleItem word="Task Info" />
<AccordionPanel pb={4}>{taskInfo}</AccordionPanel>
</AccordionItem>
{props.action && (
<AccordionItem>
<TitleItem word="Action" />
<AccordionPanel pb={4}>{props.action}</AccordionPanel>
</AccordionItem>
)}
</Accordion>
</>
);
}

View File

@ -0,0 +1,52 @@
import { useNavigate, useParams } from "react-router-dom";
import { Box, Button, Stack, Wrap, WrapItem } from "@chakra-ui/react";
import { MdPlayCircleOutline, MdSearch } from "react-icons/md";
import { useDetailQuery } from "../app/services/problem";
import Markdown from "../components/Markdown";
import ProblemInfoMenu from "../components/ProblemInfoMenu";
export default function ProblemDetailPage() {
const { id } = useParams();
const navigate = useNavigate();
const { data: details } = useDetailQuery({ pid: Number(id) || 0 });
const actionBtn = (
<Wrap>
<WrapItem>
<Button
variant="outline"
size="sm"
leftIcon={<MdPlayCircleOutline />}
onClick={() => navigate(`/problem/${id}/submit`)}
>
Submit
</Button>
</WrapItem>
<WrapItem>
<Button
variant="outline"
size="sm"
leftIcon={<MdSearch />}
onClick={() => navigate(`/problem/${id}/status`)}
>
Status
</Button>
</WrapItem>
</Wrap>
);
return (
<>
<Stack direction={{ base: "column", md: "row" }}>
<Box w="100%">
<Markdown markdown={details?.body.problem.statement || ""} />
</Box>
<Box w="480px">
<ProblemInfoMenu data={details?.body} action={actionBtn} />
</Box>
</Stack>
</>
);
}

View File

@ -9,6 +9,7 @@ const HomePage = lazy(() => import("./pages/HomePage"));
const LoginPage = lazy(() => import("./pages/LoginPage"));
const LogoutPage = lazy(() => import("./pages/LogoutPage"));
const ProblemListPage = lazy(() => import("./pages/ProblemListPage"));
const ProblemDetailPage = lazy(() => import("./pages/ProblemDetailPage"));
export const router: RouteObject[] = [
{
@ -36,6 +37,10 @@ export const router: RouteObject[] = [
path: "problem",
element: <ProblemListPage />,
},
{
path: "problem/:id",
element: <ProblemDetailPage />,
},
],
},
];