feat: add StatusPage
This commit is contained in:
parent
32d96207b9
commit
3f3a75f507
@ -3,4 +3,5 @@ node_modules/
|
|||||||
/pnpm-lock.yaml
|
/pnpm-lock.yaml
|
||||||
/postcss.config.cjs
|
/postcss.config.cjs
|
||||||
|
|
||||||
src/components/Languages.tsx
|
src/components/Languages.ts
|
||||||
|
src/components/Verdict.ts
|
||||||
|
17
src/components/Verdict.ts
Normal file
17
src/components/Verdict.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { Verdict } from "../app/services/status";
|
||||||
|
|
||||||
|
/* eslint-disable prettier/prettier */
|
||||||
|
export const VerdictMap: {
|
||||||
|
[key in Verdict]: { color: string; label: string; name: string };
|
||||||
|
} = {
|
||||||
|
[Verdict.Accepted]: { color: "#52C41A", label: "AC", name: "Accepted" },
|
||||||
|
[Verdict.WrongAnswer]: { color: "#E74C3C", label: "WA", name: "Wrong Answer" },
|
||||||
|
[Verdict.JuryFailed]: { color: "#0E1D69", label: "JF", name: "Jury Failed" },
|
||||||
|
[Verdict.PartialCorrect]: { color: "#E67E22", label: "PC", name: "Partial Correct" },
|
||||||
|
[Verdict.TimeLimitExceeded]: { color: "#052242", label: "TLE", name: "Time Limit Exceeded" },
|
||||||
|
[Verdict.MemoryLimitExceeded]: { color: "#052242", label: "MLE", name: "Memory Limit Exceeded" },
|
||||||
|
[Verdict.RuntimeError]: { color: "#9D3DCF", label: "RE", name: "Runtime Error" },
|
||||||
|
[Verdict.CompileError]: { color: "#FADB14", label: "CE", name: "Compile Error" },
|
||||||
|
[Verdict.SystemError]: { color: "#0E1D69", label: "UKE", name: "System Error" },
|
||||||
|
};
|
||||||
|
/* eslint-enable prettier/prettier */
|
150
src/pages/StatusPage.tsx
Normal file
150
src/pages/StatusPage.tsx
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
import type React from "react";
|
||||||
|
import { lazy } from "react";
|
||||||
|
import { useParams } from "react-router-dom";
|
||||||
|
import {
|
||||||
|
Accordion,
|
||||||
|
AccordionItem,
|
||||||
|
AccordionPanel,
|
||||||
|
Alert,
|
||||||
|
AlertIcon,
|
||||||
|
Box,
|
||||||
|
Spinner,
|
||||||
|
Stack,
|
||||||
|
Tab,
|
||||||
|
TabList,
|
||||||
|
TabPanel,
|
||||||
|
TabPanels,
|
||||||
|
Tabs,
|
||||||
|
Text,
|
||||||
|
Tooltip,
|
||||||
|
Wrap,
|
||||||
|
WrapItem,
|
||||||
|
} from "@chakra-ui/react";
|
||||||
|
|
||||||
|
import { useStatusQuery } from "../app/services/status";
|
||||||
|
import { useDetailQuery } from "../app/services/problem";
|
||||||
|
import { LanguageMap } from "../components/Languages";
|
||||||
|
import { VerdictMap } from "../components/Verdict";
|
||||||
|
|
||||||
|
const ProblemInfoMenu = lazy(() => import("../components/ProblemInfoMenu"));
|
||||||
|
const TitleItem = lazy(() => import("../components/AccordionTitleItem"));
|
||||||
|
const Highlight = lazy(() => import("../components/Highlight"));
|
||||||
|
|
||||||
|
const gridStyle: React.CSSProperties = {
|
||||||
|
width: "5.3em",
|
||||||
|
height: "5.3em",
|
||||||
|
margin: "0.2em",
|
||||||
|
fontSize: "1.5em",
|
||||||
|
padding: "0.5em",
|
||||||
|
textAlign: "center",
|
||||||
|
textShadow: "1px 1px 2px gray",
|
||||||
|
color: "white",
|
||||||
|
};
|
||||||
|
|
||||||
|
const topLeftStyle: React.CSSProperties = {
|
||||||
|
position: "absolute",
|
||||||
|
fontSize: "0.7em",
|
||||||
|
};
|
||||||
|
|
||||||
|
const labelStyle: React.CSSProperties = {
|
||||||
|
fontSize: "1.2em",
|
||||||
|
paddingTop: "0.6em",
|
||||||
|
textAlign: "center",
|
||||||
|
};
|
||||||
|
|
||||||
|
const statusStyle: React.CSSProperties = {
|
||||||
|
fontSize: "0.65em",
|
||||||
|
textAlign: "center",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function StatusPage() {
|
||||||
|
const { id } = useParams();
|
||||||
|
const sid = Number(id) || 0;
|
||||||
|
const { data: status } = useStatusQuery({ sid: sid });
|
||||||
|
const { data: problem } = useDetailQuery({ pid: status?.body.submission.problem.meta.ID || 0 });
|
||||||
|
|
||||||
|
const showSourceCode = status && status.body.submission.code !== "";
|
||||||
|
const showMessage = status && (status.body.context.message !== "" || status.body.context.compile_message !== "");
|
||||||
|
|
||||||
|
const emptyTab = (
|
||||||
|
<Alert status="warning">
|
||||||
|
<AlertIcon />
|
||||||
|
<Spinner size="sm" mr={2} />
|
||||||
|
<Text as="b" mr={2}>
|
||||||
|
Waiting for Judge...
|
||||||
|
</Text>
|
||||||
|
<Text>Please refresh the page after a few seconds.</Text>
|
||||||
|
</Alert>
|
||||||
|
);
|
||||||
|
|
||||||
|
const statusCardTab = (
|
||||||
|
<Wrap spacing="0.3em">
|
||||||
|
{status?.body.context.tasks.map((task) => (
|
||||||
|
<WrapItem key={task.id}>
|
||||||
|
<Tooltip label={task.message}>
|
||||||
|
<Box style={{ ...gridStyle, background: VerdictMap[task.verdict].color }}>
|
||||||
|
<div style={topLeftStyle}>{`#${task.id}`}</div>
|
||||||
|
<div>
|
||||||
|
<div style={labelStyle}>{VerdictMap[task.verdict].label}</div>
|
||||||
|
<div style={statusStyle}>
|
||||||
|
{`${Math.max(
|
||||||
|
task.runtime.real_time,
|
||||||
|
task.runtime.cpu_time,
|
||||||
|
)}ms/${task.runtime.memory}KB`}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Box>
|
||||||
|
</Tooltip>
|
||||||
|
</WrapItem>
|
||||||
|
))}
|
||||||
|
</Wrap>
|
||||||
|
);
|
||||||
|
|
||||||
|
const sourceCodeTab = (
|
||||||
|
<Highlight
|
||||||
|
lang={!showSourceCode ? "text" : LanguageMap[status.body.submission.language].prism}
|
||||||
|
code={status?.body.submission.code || ""}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
const messageTab = (
|
||||||
|
<Accordion defaultIndex={[0, 1]} allowMultiple>
|
||||||
|
<AccordionItem>
|
||||||
|
<TitleItem word="Message" />
|
||||||
|
<AccordionPanel pb={4}>
|
||||||
|
<Highlight lang="text" code={status?.body.context.message || "Not Available"} />
|
||||||
|
</AccordionPanel>
|
||||||
|
</AccordionItem>
|
||||||
|
<AccordionItem>
|
||||||
|
<TitleItem word="Compile Message" />
|
||||||
|
<AccordionPanel pb={4}>
|
||||||
|
<Highlight lang="text" code={status?.body.context.compile_message || "Not Available"} />
|
||||||
|
</AccordionPanel>
|
||||||
|
</AccordionItem>
|
||||||
|
</Accordion>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Stack direction={{ base: "column", md: "row" }}>
|
||||||
|
<Box w="100%">
|
||||||
|
<Tabs>
|
||||||
|
<TabList>
|
||||||
|
<Tab>Task Info</Tab>
|
||||||
|
<Tab isDisabled={!showSourceCode}>Source Code</Tab>
|
||||||
|
<Tab isDisabled={!showMessage}>Message</Tab>
|
||||||
|
</TabList>
|
||||||
|
<TabPanels>
|
||||||
|
<TabPanel>{status ? statusCardTab : emptyTab}</TabPanel>
|
||||||
|
<TabPanel>{sourceCodeTab}</TabPanel>
|
||||||
|
<TabPanel>{messageTab}</TabPanel>
|
||||||
|
</TabPanels>
|
||||||
|
</Tabs>
|
||||||
|
</Box>
|
||||||
|
<Box maxW="480px">
|
||||||
|
<ProblemInfoMenu data={problem?.body} />
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
@ -12,6 +12,7 @@ const ProblemListPage = lazy(() => import("./pages/ProblemListPage"));
|
|||||||
const ProblemDetailPage = lazy(() => import("./pages/ProblemDetailPage"));
|
const ProblemDetailPage = lazy(() => import("./pages/ProblemDetailPage"));
|
||||||
const SubmitPage = lazy(() => import("./pages/SubmitPage"));
|
const SubmitPage = lazy(() => import("./pages/SubmitPage"));
|
||||||
const SubmissionListPage = lazy(() => import("./pages/SubmissionListPage"));
|
const SubmissionListPage = lazy(() => import("./pages/SubmissionListPage"));
|
||||||
|
const StatusPage = lazy(() => import("./pages/StatusPage"));
|
||||||
|
|
||||||
export const router: RouteObject[] = [
|
export const router: RouteObject[] = [
|
||||||
{
|
{
|
||||||
@ -59,6 +60,14 @@ export const router: RouteObject[] = [
|
|||||||
</RequireAuth>
|
</RequireAuth>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "status/:id",
|
||||||
|
element: (
|
||||||
|
<RequireAuth>
|
||||||
|
<StatusPage />
|
||||||
|
</RequireAuth>
|
||||||
|
),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
Loading…
Reference in New Issue
Block a user