From 9f022ce7e59213284896a07a53bfef232a6e62d3 Mon Sep 17 00:00:00 2001
From: nsfisis <nsfisis@gmail.com>
Date: Mon, 12 Aug 2024 02:03:17 +0900
Subject: refactor(frontend): create SubmitStatusLabel component

---
 frontend/app/components/SubmitStatusLabel.tsx | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)
 create mode 100644 frontend/app/components/SubmitStatusLabel.tsx

(limited to 'frontend/app/components/SubmitStatusLabel.tsx')

diff --git a/frontend/app/components/SubmitStatusLabel.tsx b/frontend/app/components/SubmitStatusLabel.tsx
new file mode 100644
index 0000000..f6e19bb
--- /dev/null
+++ b/frontend/app/components/SubmitStatusLabel.tsx
@@ -0,0 +1,24 @@
+import type { SubmitResultStatus } from "../models/SubmitResult";
+
+type Props = {
+	status: SubmitResultStatus;
+};
+
+export default function SubmitStatusLabel({ status }: Props) {
+	switch (status) {
+		case "running":
+			return <span>Running...</span>;
+		case "success":
+			return <span>Accepted</span>;
+		case "wrong_answer":
+			return <span>Wrong Answer</span>;
+		case "timeout":
+			return <span>Time Limit Exceeded</span>;
+		case "compile_error":
+			return <span>Compile Error</span>;
+		case "runtime_error":
+			return <span>Runtime Error</span>;
+		case "internal_error":
+			return <span>Internal Error</span>;
+	}
+}
-- 
cgit v1.3.1


From 699c5ce665bae6bcc406a0f7de994bb218a9977e Mon Sep 17 00:00:00 2001
From: nsfisis <nsfisis@gmail.com>
Date: Mon, 12 Aug 2024 02:08:55 +0900
Subject: feat(frontend): show submission result at first rendering

---
 .../app/components/ExecStatusIndicatorIcon.tsx     |  5 ++++
 frontend/app/components/GolfWatchApp.client.tsx    | 34 ++++++++++++++--------
 .../GolfWatchApps/GolfWatchAppGaming.tsx           | 12 ++------
 frontend/app/components/SubmitStatusLabel.tsx      |  2 ++
 frontend/app/models/ExecResult.ts                  |  1 +
 frontend/app/models/PlayerInfo.ts                  |  2 +-
 frontend/app/models/SubmitResult.ts                |  1 +
 7 files changed, 34 insertions(+), 23 deletions(-)

(limited to 'frontend/app/components/SubmitStatusLabel.tsx')

diff --git a/frontend/app/components/ExecStatusIndicatorIcon.tsx b/frontend/app/components/ExecStatusIndicatorIcon.tsx
index 13343b8..5277bfa 100644
--- a/frontend/app/components/ExecStatusIndicatorIcon.tsx
+++ b/frontend/app/components/ExecStatusIndicatorIcon.tsx
@@ -1,5 +1,6 @@
 import {
 	faBan,
+	faCircle,
 	faCircleCheck,
 	faCircleExclamation,
 	faRotate,
@@ -13,6 +14,10 @@ type Props = {
 
 export default function ExecStatusIndicatorIcon({ status }: Props) {
 	switch (status) {
+		case "waiting_submission":
+			return (
+				<FontAwesomeIcon icon={faCircle} fixedWidth className="text-gray-400" />
+			);
 		case "running":
 			return (
 				<FontAwesomeIcon
diff --git a/frontend/app/components/GolfWatchApp.client.tsx b/frontend/app/components/GolfWatchApp.client.tsx
index 481b997..7f582c9 100644
--- a/frontend/app/components/GolfWatchApp.client.tsx
+++ b/frontend/app/components/GolfWatchApp.client.tsx
@@ -78,14 +78,32 @@ export default function GolfWatchApp({
 		iconPath: playerA?.icon_path ?? null,
 		score: null,
 		code: "",
-		submitResult: undefined,
+		submitResult: {
+			status: "waiting_submission",
+			execResults: game.exec_steps.map((r) => ({
+				testcase_id: r.testcase_id,
+				status: "waiting_submission",
+				label: r.label,
+				stdout: "",
+				stderr: "",
+			})),
+		},
 	});
 	const [playerInfoB, setPlayerInfoB] = useState<PlayerInfo>({
 		displayName: playerB?.display_name ?? null,
 		iconPath: playerB?.icon_path ?? null,
 		score: null,
 		code: "",
-		submitResult: undefined,
+		submitResult: {
+			status: "waiting_submission",
+			execResults: game.exec_steps.map((r) => ({
+				testcase_id: r.testcase_id,
+				status: "waiting_submission",
+				label: r.label,
+				stdout: "",
+				stderr: "",
+			})),
+		},
 	});
 
 	if (readyState === ReadyState.UNINSTANTIATED) {
@@ -127,10 +145,9 @@ export default function GolfWatchApp({
 						...prev,
 						submitResult: {
 							status: "running",
-							execResults: game.exec_steps.map((r) => ({
-								testcase_id: r.testcase_id,
+							execResults: prev.submitResult.execResults.map((r) => ({
+								...r,
 								status: "running",
-								label: r.label,
 								stdout: "",
 								stderr: "",
 							})),
@@ -143,9 +160,6 @@ export default function GolfWatchApp({
 						player_id === playerA?.user_id ? setPlayerInfoA : setPlayerInfoB;
 					setter((prev) => {
 						const ret = { ...prev };
-						if (ret.submitResult === undefined) {
-							return ret;
-						}
 						ret.submitResult = {
 							...ret.submitResult,
 							execResults: ret.submitResult.execResults.map((r) =>
@@ -167,9 +181,6 @@ export default function GolfWatchApp({
 						player_id === playerA?.user_id ? setPlayerInfoA : setPlayerInfoB;
 					setter((prev) => {
 						const ret = { ...prev };
-						if (ret.submitResult === undefined) {
-							return ret;
-						}
 						ret.submitResult = {
 							...ret.submitResult,
 							status,
@@ -217,7 +228,6 @@ export default function GolfWatchApp({
 			}
 		}
 	}, [
-		game.exec_steps,
 		game.started_at,
 		lastJsonMessage,
 		readyState,
diff --git a/frontend/app/components/GolfWatchApps/GolfWatchAppGaming.tsx b/frontend/app/components/GolfWatchApps/GolfWatchAppGaming.tsx
index b436842..2a852e0 100644
--- a/frontend/app/components/GolfWatchApps/GolfWatchAppGaming.tsx
+++ b/frontend/app/components/GolfWatchApps/GolfWatchAppGaming.tsx
@@ -60,11 +60,7 @@ export default function GolfWatchAppGaming({
 				</div>
 				<div>
 					<div>
-						{playerInfoA.submitResult?.status ? (
-							<SubmitStatusLabel status={playerInfoA.submitResult?.status} />
-						) : (
-							<span>-</span>
-						)}
+						<SubmitStatusLabel status={playerInfoA.submitResult.status} />
 					</div>
 					<div>
 						<ol>
@@ -92,11 +88,7 @@ export default function GolfWatchAppGaming({
 				</div>
 				<div>
 					<div>
-						{playerInfoB.submitResult?.status ? (
-							<SubmitStatusLabel status={playerInfoB.submitResult?.status} />
-						) : (
-							<span>-</span>
-						)}
+						<SubmitStatusLabel status={playerInfoB.submitResult.status} />
 					</div>
 					<div>
 						<ol>
diff --git a/frontend/app/components/SubmitStatusLabel.tsx b/frontend/app/components/SubmitStatusLabel.tsx
index f6e19bb..2d4890d 100644
--- a/frontend/app/components/SubmitStatusLabel.tsx
+++ b/frontend/app/components/SubmitStatusLabel.tsx
@@ -6,6 +6,8 @@ type Props = {
 
 export default function SubmitStatusLabel({ status }: Props) {
 	switch (status) {
+		case "waiting_submission":
+			return <span>-</span>;
 		case "running":
 			return <span>Running...</span>;
 		case "success":
diff --git a/frontend/app/models/ExecResult.ts b/frontend/app/models/ExecResult.ts
index 59b8ee8..e0b6bb4 100644
--- a/frontend/app/models/ExecResult.ts
+++ b/frontend/app/models/ExecResult.ts
@@ -1,4 +1,5 @@
 export type ExecResultStatus =
+	| "waiting_submission"
 	| "running"
 	| "success"
 	| "wrong_answer"
diff --git a/frontend/app/models/PlayerInfo.ts b/frontend/app/models/PlayerInfo.ts
index 30cccea..8092ab3 100644
--- a/frontend/app/models/PlayerInfo.ts
+++ b/frontend/app/models/PlayerInfo.ts
@@ -5,5 +5,5 @@ export type PlayerInfo = {
 	iconPath: string | null;
 	score: number | null;
 	code: string | null;
-	submitResult?: SubmitResult;
+	submitResult: SubmitResult;
 };
diff --git a/frontend/app/models/SubmitResult.ts b/frontend/app/models/SubmitResult.ts
index 0cb7f9b..6df00b6 100644
--- a/frontend/app/models/SubmitResult.ts
+++ b/frontend/app/models/SubmitResult.ts
@@ -1,6 +1,7 @@
 import type { ExecResult } from "./ExecResult";
 
 export type SubmitResultStatus =
+	| "waiting_submission"
 	| "running"
 	| "success"
 	| "wrong_answer"
-- 
cgit v1.3.1


From c6629900f3a4965ceca0f42e90648937bf51dfb5 Mon Sep 17 00:00:00 2001
From: nsfisis <nsfisis@gmail.com>
Date: Mon, 12 Aug 2024 03:56:13 +0900
Subject: feat(frontend): improve play page styling

---
 .../components/GolfPlayApps/GolfPlayAppGaming.tsx  | 50 +++++++++++++++++++---
 frontend/app/components/SubmitStatusLabel.tsx      | 16 +++----
 worker/exec.go                                     | 11 +++--
 worker/models.go                                   |  1 +
 4 files changed, 59 insertions(+), 19 deletions(-)

(limited to 'frontend/app/components/SubmitStatusLabel.tsx')

diff --git a/frontend/app/components/GolfPlayApps/GolfPlayAppGaming.tsx b/frontend/app/components/GolfPlayApps/GolfPlayAppGaming.tsx
index 08490a6..667ada2 100644
--- a/frontend/app/components/GolfPlayApps/GolfPlayAppGaming.tsx
+++ b/frontend/app/components/GolfPlayApps/GolfPlayAppGaming.tsx
@@ -3,6 +3,10 @@ import React, { useRef } from "react";
 import SubmitButton from "../../components/SubmitButton";
 import type { PlayerInfo } from "../../models/PlayerInfo";
 import BorderedContainer from "../BorderedContainer";
+import SubmitStatusLabel from "../SubmitStatusLabel";
+import ExecStatusIndicatorIcon from "../ExecStatusIndicatorIcon";
+import { faArrowDown } from "@fortawesome/free-solid-svg-icons";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 
 type Props = {
 	gameDisplayName: string;
@@ -40,10 +44,11 @@ export default function GolfPlayAppGaming({
 					<div className="text-gray-100">{gameDisplayName}</div>
 					<div className="text-2xl">03:21</div>
 				</div>
-				<div>
-					<Link to={"/dashboard"} className="font-bold text-xl">
+				<div className="font-bold text-end">
+					<Link to={"/dashboard"} className="text-gray-100">
 						{playerInfo.displayName}
 					</Link>
+					<div className="text-2xl">{playerInfo.score}</div>
 				</div>
 			</div>
 			<div className="grow grid grid-cols-3 divide-x divide-gray-300">
@@ -62,13 +67,44 @@ export default function GolfPlayAppGaming({
 						className="resize-none h-full w-full rounded-lg border border-gray-300 p-2 focus:outline-none focus:ring-2 focus:ring-gray-400 transition duration-300"
 					></textarea>
 				</div>
-				<div className="p-4">
-					<SubmitButton onClick={handleSubmitButtonClick}>提出</SubmitButton>
-					<div className="mb-2 mt-auto">
-						<div className="font-semibold text-green-500">
-							Score: {playerInfo.score ?? "-"}
+				<div className="p-4 flex flex-col gap-4">
+					<div className="flex">
+						<SubmitButton onClick={handleSubmitButtonClick}>提出</SubmitButton>
+						<div className="grow font-bold text-xl text-center m-1">
+							<SubmitStatusLabel status={playerInfo.submitResult.status} />
 						</div>
 					</div>
+					<ul className="flex flex-col gap-2">
+						{playerInfo.submitResult.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 !== playerInfo.submitResult.execResults.length - 1 && (
+										<div>
+											<FontAwesomeIcon
+												icon={faArrowDown}
+												fixedWidth
+												className="text-gray-500"
+											/>
+										</div>
+									)}
+								</div>
+								<div className="grow p-2 overflow-x-scroll">
+									<BorderedContainer>
+										<div className="font-semibold">{r.label}</div>
+										<div>
+											<code>
+												{r.stdout}
+												{r.stderr}
+											</code>
+										</div>
+									</BorderedContainer>
+								</div>
+							</li>
+						))}
+					</ul>
 				</div>
 			</div>
 		</div>
diff --git a/frontend/app/components/SubmitStatusLabel.tsx b/frontend/app/components/SubmitStatusLabel.tsx
index 2d4890d..e0ecc27 100644
--- a/frontend/app/components/SubmitStatusLabel.tsx
+++ b/frontend/app/components/SubmitStatusLabel.tsx
@@ -7,20 +7,20 @@ type Props = {
 export default function SubmitStatusLabel({ status }: Props) {
 	switch (status) {
 		case "waiting_submission":
-			return <span>-</span>;
+			return null;
 		case "running":
-			return <span>Running...</span>;
+			return "実行中...";
 		case "success":
-			return <span>Accepted</span>;
+			return "成功";
 		case "wrong_answer":
-			return <span>Wrong Answer</span>;
+			return "テスト失敗";
 		case "timeout":
-			return <span>Time Limit Exceeded</span>;
+			return "時間切れ";
 		case "compile_error":
-			return <span>Compile Error</span>;
+			return "コンパイルエラー";
 		case "runtime_error":
-			return <span>Runtime Error</span>;
+			return "実行時エラー";
 		case "internal_error":
-			return <span>Internal Error</span>;
+			return "！内部エラー！";
 	}
 }
diff --git a/worker/exec.go b/worker/exec.go
index 9e937f7..37f542b 100644
--- a/worker/exec.go
+++ b/worker/exec.go
@@ -67,11 +67,14 @@ func execCommandWithTimeout(
 	}
 }
 
-func convertCommandErrorToResultType(err error) string {
+func convertCommandErrorToResultType(err error, isCompile bool) string {
 	if err != nil {
 		if err == context.DeadlineExceeded {
 			return resultTimeout
 		}
+		if isCompile {
+			return resultCompileError
+		}
 		return resultRuntimeError
 	}
 	return resultSuccess
@@ -109,7 +112,7 @@ func execSwiftCompile(
 	)
 
 	return swiftCompileResponseData{
-		Status: convertCommandErrorToResultType(err),
+		Status: convertCommandErrorToResultType(err, true),
 		Stdout: stdout,
 		Stderr: stderr,
 	}
@@ -140,7 +143,7 @@ func execWasmCompile(
 	)
 
 	return wasmCompileResponseData{
-		Status: convertCommandErrorToResultType(err),
+		Status: convertCommandErrorToResultType(err, true),
 		Stdout: stdout,
 		Stderr: stderr,
 	}
@@ -170,7 +173,7 @@ func execTestRun(
 	)
 
 	return testRunResponseData{
-		Status: convertCommandErrorToResultType(err),
+		Status: convertCommandErrorToResultType(err, false),
 		Stdout: stdout,
 		Stderr: stderr,
 	}
diff --git a/worker/models.go b/worker/models.go
index 9f60eb0..4a318d0 100644
--- a/worker/models.go
+++ b/worker/models.go
@@ -7,6 +7,7 @@ import (
 
 const (
 	resultSuccess       = "success"
+	resultCompileError  = "compile_error"
 	resultRuntimeError  = "runtime_error"
 	resultTimeout       = "timeout"
 	resultInternalError = "internal_error"
-- 
cgit v1.3.1

