chore: refactor problem page

This commit is contained in:
Paul Pan 2023-12-21 15:09:37 +08:00
parent 2dfdec85ed
commit 4ee1b98699
5 changed files with 87 additions and 75 deletions

9
src/api/loader.ts Normal file
View File

@ -0,0 +1,9 @@
import { ProblemApi } from "./problem.ts";
export async function ProblemLoader({ params }: { params: { id: string } }) {
const id = parseInt(params.id);
if (isNaN(id)) {
throw new Error("invalid problem id");
}
return await ProblemApi.Details({ pid: id });
}

View File

@ -0,0 +1,71 @@
import React from "react";
import { Link, useLoaderData } from "react-router-dom";
import { Collapse, CollapseProps, Descriptions, Space, Tag } from "antd";
import { DetailsResp } from "../api/problem.ts";
interface ProblemDetailsProps {
action: React.ReactNode;
}
export default function ProblemDetails(props: ProblemDetailsProps) {
const details = useLoaderData() as DetailsResp;
const problemInfo = (
<Descriptions bordered column={1} size="small">
<Descriptions.Item label="Provider">
<Link to={`/user/${details.problem.provider.meta.ID}`}>
{details.problem.provider.nick_name}
</Link>
</Descriptions.Item>
<Descriptions.Item label="Supported Languages">
<Space size={[0, 8]} wrap>
{details.context.Languages.map((l) => (
<Tag key={l.Lang}>{l.Lang}</Tag>
))}
</Space>
</Descriptions.Item>
<Descriptions.Item label="Task Nums">
{details.context.Tasks.length}
</Descriptions.Item>
</Descriptions>
);
const runtimeLimit = (
<Descriptions bordered column={1} size="small">
<Descriptions.Item label="Time Limit">
{details.context.Runtime.TimeLimit} ms
</Descriptions.Item>
<Descriptions.Item label="Memory Limit">
{details.context.Runtime.MemoryLimit} MB
</Descriptions.Item>
<Descriptions.Item label="Process Limit">
{details.context.Runtime.NProcLimit}
</Descriptions.Item>
</Descriptions>
);
const miscItems: CollapseProps["items"] = [
{
key: "1",
label: "Problem Info",
children: problemInfo,
},
{
key: "2",
label: "Runtime Limit",
children: runtimeLimit,
},
{
key: "3",
label: "Action",
children: props.action,
},
];
return (
<>
<Collapse items={miscItems} defaultActiveKey={["1", "2", "3"]} />
</>
);
}

View File

@ -11,7 +11,7 @@ const ErrorPage = () => {
const convertError = (error: unknown): string => { const convertError = (error: unknown): string => {
if (isRouteErrorResponse(error)) { if (isRouteErrorResponse(error)) {
return error.error?.message || error.statusText; return `${error.status} ${error.statusText}`;
} else if (error instanceof Error) { } else if (error instanceof Error) {
return error.message; return error.message;
} else if (typeof error === "string") { } else if (typeof error === "string") {

View File

@ -2,5 +2,5 @@ export { Root } from "./root";
export { ErrorPage } from "./error-page"; export { ErrorPage } from "./error-page";
export { HomePage } from "./home"; export { HomePage } from "./home";
export { ProblemLoader, ProblemPage } from "./problem"; export { ProblemPage } from "./problem";
export { SearchPage } from "./search"; export { SearchPage } from "./search";

View File

@ -1,63 +1,15 @@
import { import { Row, Col, Space, Button } from "antd";
Row,
Col,
Collapse,
CollapseProps,
Descriptions,
Tag,
Space,
Button,
} from "antd";
import { PlayCircleOutlined, SearchOutlined } from "@ant-design/icons"; import { PlayCircleOutlined, SearchOutlined } from "@ant-design/icons";
import { Link, useLoaderData, useNavigate } from "react-router-dom"; import { useLoaderData, useNavigate } from "react-router-dom";
import Markdown from "../components/markdown.tsx"; import Markdown from "../components/markdown.tsx";
import { DetailsResp, ProblemApi } from "../api/problem.ts"; import { DetailsResp } from "../api/problem.ts";
import ProblemDetails from "../components/problem-details.tsx";
export async function ProblemLoader({ params }: { params: { id: string } }) {
const id = parseInt(params.id);
if (isNaN(id)) {
throw new Error("invalid problem id");
}
return await ProblemApi.Details({ pid: id });
}
export function ProblemPage() { export function ProblemPage() {
const details = useLoaderData() as DetailsResp; const details = useLoaderData() as DetailsResp;
const navigate = useNavigate(); const navigate = useNavigate();
const problemInfo = (
<Descriptions bordered column={1} size="small">
<Descriptions.Item label="Provider">
<Link to={`/user/${details.problem.provider.meta.ID}`}>
{details.problem.provider.nick_name}
</Link>
</Descriptions.Item>
<Descriptions.Item label="Supported Languages">
<Space size={[0, 8]} wrap>
{details.context.Languages.map((l) => (
<Tag key={l.Lang}>{l.Lang}</Tag>
))}
</Space>
</Descriptions.Item>
<Descriptions.Item label="Task Nums">
{details.context.Tasks.length}
</Descriptions.Item>
</Descriptions>
);
const runtimeLimit = (
<Descriptions bordered column={1} size="small">
<Descriptions.Item label="Time Limit">
{details.context.Runtime.TimeLimit} ms
</Descriptions.Item>
<Descriptions.Item label="Memory Limit">
{details.context.Runtime.MemoryLimit} MB
</Descriptions.Item>
<Descriptions.Item label="Process Limit">
{details.context.Runtime.NProcLimit}
</Descriptions.Item>
</Descriptions>
);
const actionBtn = ( const actionBtn = (
<Space wrap> <Space wrap>
<Button <Button
@ -79,28 +31,8 @@ export function ProblemPage() {
</Space> </Space>
); );
const miscItems: CollapseProps["items"] = [
{
key: "1",
label: "Problem Info",
children: problemInfo,
},
{
key: "2",
label: "Runtime Limit",
children: runtimeLimit,
},
{
key: "3",
label: "Action",
children: actionBtn,
},
];
const ProblemStatement = <Markdown markdown={details.problem.statement} />; const ProblemStatement = <Markdown markdown={details.problem.statement} />;
const MiscPanel = ( const MiscPanel = <ProblemDetails action={actionBtn} />;
<Collapse items={miscItems} defaultActiveKey={["1", "2", "3"]} />
);
return ( return (
<> <>