diff options
| author | nsfisis <nsfisis@gmail.com> | 2024-08-18 00:38:07 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2024-08-18 01:46:02 +0900 |
| commit | ad42f43d1c3c8f0da0ac31b8016e2f20f1765720 (patch) | |
| tree | 09567462299eed859bb0f2170ee2602825c23ca0 /frontend/app/components/Gaming | |
| parent | 7653eb2b28911a0479b3b673c9b63fd490aedb6b (diff) | |
| download | iosdc-japan-2024-albatross-ad42f43d1c3c8f0da0ac31b8016e2f20f1765720.tar.gz iosdc-japan-2024-albatross-ad42f43d1c3c8f0da0ac31b8016e2f20f1765720.tar.zst iosdc-japan-2024-albatross-ad42f43d1c3c8f0da0ac31b8016e2f20f1765720.zip | |
refactor(frontend): extract components for gaming page
Diffstat (limited to 'frontend/app/components/Gaming')
| -rw-r--r-- | frontend/app/components/Gaming/CodeBlock.tsx | 11 | ||||
| -rw-r--r-- | frontend/app/components/Gaming/ExecStatusIndicatorIcon.tsx | 51 | ||||
| -rw-r--r-- | frontend/app/components/Gaming/ScoreBar.tsx | 25 | ||||
| -rw-r--r-- | frontend/app/components/Gaming/SubmitResult.tsx | 56 |
4 files changed, 143 insertions, 0 deletions
diff --git a/frontend/app/components/Gaming/CodeBlock.tsx b/frontend/app/components/Gaming/CodeBlock.tsx new file mode 100644 index 0000000..20cd425 --- /dev/null +++ b/frontend/app/components/Gaming/CodeBlock.tsx @@ -0,0 +1,11 @@ +type Props = { + code: string; +}; + +export default function CodeBlock({ code }: Props) { + return ( + <pre className="bg-white resize-none h-full w-full rounded-lg border border-gray-300 p-2"> + <code>{code}</code> + </pre> + ); +} diff --git a/frontend/app/components/Gaming/ExecStatusIndicatorIcon.tsx b/frontend/app/components/Gaming/ExecStatusIndicatorIcon.tsx new file mode 100644 index 0000000..8daf48c --- /dev/null +++ b/frontend/app/components/Gaming/ExecStatusIndicatorIcon.tsx @@ -0,0 +1,51 @@ +import { + faBan, + faCircle, + faCircleCheck, + faCircleExclamation, + faRotate, +} from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import type { ExecResultStatus } from "../../models/ExecResult"; + +type Props = { + status: ExecResultStatus; +}; + +export default function ExecStatusIndicatorIcon({ status }: Props) { + switch (status) { + case "waiting_submission": + return ( + <FontAwesomeIcon icon={faCircle} fixedWidth className="text-gray-400" /> + ); + case "running": + return ( + <FontAwesomeIcon + icon={faRotate} + spin + fixedWidth + className="text-gray-700" + /> + ); + case "success": + return ( + <FontAwesomeIcon + icon={faCircleCheck} + fixedWidth + className="text-green-500" + /> + ); + case "canceled": + return ( + <FontAwesomeIcon icon={faBan} fixedWidth className="text-gray-400" /> + ); + default: + return ( + <FontAwesomeIcon + icon={faCircleExclamation} + fixedWidth + className="text-red-500" + /> + ); + } +} diff --git a/frontend/app/components/Gaming/ScoreBar.tsx b/frontend/app/components/Gaming/ScoreBar.tsx new file mode 100644 index 0000000..4eac3ad --- /dev/null +++ b/frontend/app/components/Gaming/ScoreBar.tsx @@ -0,0 +1,25 @@ +type Props = { + scoreA: number | null; + scoreB: number | null; + bgA: string; + bgB: string; +}; + +export default function ScoreBar({ scoreA, scoreB, bgA, bgB }: Props) { + let scoreRatio; + if (scoreA === null && scoreB === null) { + scoreRatio = 50; + } else if (scoreA === null) { + scoreRatio = 0; + } else if (scoreB === null) { + scoreRatio = 100; + } else { + scoreRatio = (scoreB / (scoreA + scoreB)) * 100; + } + + return ( + <div className={`w-full ${bgB}`}> + <div className={`h-6 ${bgA}`} style={{ width: `${scoreRatio}%` }}></div> + </div> + ); +} diff --git a/frontend/app/components/Gaming/SubmitResult.tsx b/frontend/app/components/Gaming/SubmitResult.tsx new file mode 100644 index 0000000..ae83e92 --- /dev/null +++ b/frontend/app/components/Gaming/SubmitResult.tsx @@ -0,0 +1,56 @@ +import { faArrowDown } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import React from "react"; +import type { SubmitResult } from "../../models/SubmitResult"; +import BorderedContainer from "../BorderedContainer"; +import SubmitStatusLabel from "../SubmitStatusLabel"; +import ExecStatusIndicatorIcon from "./ExecStatusIndicatorIcon"; + +type Props = { + result: SubmitResult; + submitButton?: React.ReactNode; +}; + +export default function SubmitResult({ result, submitButton }: Props) { + return ( + <div className="flex flex-col gap-2"> + <div className="flex"> + {submitButton} + <div className="grow font-bold text-xl text-center"> + <SubmitStatusLabel status={result.status} /> + </div> + </div> + <ul className="flex flex-col gap-2"> + {result.execResults.map((r, idx) => ( + <li key={r.testcase_id ?? -1} className="flex gap-2"> + <div className="flex flex-col gap-2 p-2"> + <div className="w-6"> + <ExecStatusIndicatorIcon status={r.status} /> + </div> + {idx !== result.execResults.length - 1 && ( + <div> + <FontAwesomeIcon + icon={faArrowDown} + fixedWidth + className="text-gray-500" + /> + </div> + )} + </div> + <div className="grow p-2"> + <BorderedContainer> + <div className="font-semibold">{r.label}</div> + <div> + <code> + {r.stdout} + {r.stderr} + </code> + </div> + </BorderedContainer> + </div> + </li> + ))} + </ul> + </div> + ); +} |
