aboutsummaryrefslogtreecommitdiffhomepage
path: root/frontend/app/components/Gaming
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2024-08-18 00:38:07 +0900
committernsfisis <nsfisis@gmail.com>2024-08-18 01:46:02 +0900
commitad42f43d1c3c8f0da0ac31b8016e2f20f1765720 (patch)
tree09567462299eed859bb0f2170ee2602825c23ca0 /frontend/app/components/Gaming
parent7653eb2b28911a0479b3b673c9b63fd490aedb6b (diff)
downloadiosdc-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.tsx11
-rw-r--r--frontend/app/components/Gaming/ExecStatusIndicatorIcon.tsx51
-rw-r--r--frontend/app/components/Gaming/ScoreBar.tsx25
-rw-r--r--frontend/app/components/Gaming/SubmitResult.tsx56
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>
+ );
+}