chore: refactor problem page
This commit is contained in:
parent
2dfdec85ed
commit
4ee1b98699
9
src/api/loader.ts
Normal file
9
src/api/loader.ts
Normal 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 });
|
||||||
|
}
|
71
src/components/problem-details.tsx
Normal file
71
src/components/problem-details.tsx
Normal 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"]} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
@ -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") {
|
||||||
|
@ -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";
|
||||||
|
@ -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 (
|
||||||
<>
|
<>
|
||||||
|
Reference in New Issue
Block a user