aboutsummaryrefslogtreecommitdiffhomepage
path: root/frontend
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2025-03-20 22:18:14 +0900
committernsfisis <nsfisis@gmail.com>2025-03-20 22:18:14 +0900
commitcca0f63e50684d6806697589b620ee4b4c1b21b5 (patch)
tree592bf93ac15a325b9b86e9455c2007f857907efc /frontend
parentf1701822ed069d70841e8b40392e55bb28bf3eb6 (diff)
downloadphperkaigi-2025-albatross-cca0f63e50684d6806697589b620ee4b4c1b21b5.tar.gz
phperkaigi-2025-albatross-cca0f63e50684d6806697589b620ee4b4c1b21b5.tar.zst
phperkaigi-2025-albatross-cca0f63e50684d6806697589b620ee4b4c1b21b5.zip
feat(frontend): improve watch page layout
Diffstat (limited to 'frontend')
-rw-r--r--frontend/app/components/Gaming/ProblemColumn.tsx (renamed from frontend/app/components/Gaming/Problem.tsx)12
-rw-r--r--frontend/app/components/Gaming/SubmitResult.tsx18
-rw-r--r--frontend/app/components/GolfPlayApps/GolfPlayAppGaming.tsx26
-rw-r--r--frontend/app/components/GolfWatchApps/GolfWatchAppGaming1v1.tsx48
-rw-r--r--frontend/app/components/GolfWatchApps/GolfWatchAppGamingMultiplayer.tsx12
-rw-r--r--frontend/app/components/TitledColumn.tsx15
-rw-r--r--frontend/app/states/play.ts10
-rw-r--r--frontend/app/states/watch.ts10
8 files changed, 88 insertions, 63 deletions
diff --git a/frontend/app/components/Gaming/Problem.tsx b/frontend/app/components/Gaming/ProblemColumn.tsx
index e2f1487..2a57afd 100644
--- a/frontend/app/components/Gaming/Problem.tsx
+++ b/frontend/app/components/Gaming/ProblemColumn.tsx
@@ -1,4 +1,5 @@
import BorderedContainerWithCaption from "../BorderedContainerWithCaption";
+import TitledColumn from "../TitledColumn";
import CodeBlock from "./CodeBlock";
import InlineCode from "./InlineCode";
@@ -8,10 +9,13 @@ type Props = {
sampleCode: string;
};
-export default function Problem({ title, description, sampleCode }: Props) {
+export default function ProblemColumn({
+ title,
+ description,
+ sampleCode,
+}: Props) {
return (
- <div className="p-4 flex flex-col gap-4">
- <div className="text-center text-xl font-bold">{title}</div>
+ <TitledColumn title={title}>
<BorderedContainerWithCaption caption="問題">
<pre className="text-gray-700 whitespace-pre-wrap break-words">
{description}
@@ -51,6 +55,6 @@ export default function Problem({ title, description, sampleCode }: Props) {
</p>
</div>
</BorderedContainerWithCaption>
- </div>
+ </TitledColumn>
);
}
diff --git a/frontend/app/components/Gaming/SubmitResult.tsx b/frontend/app/components/Gaming/SubmitResult.tsx
deleted file mode 100644
index ea75b6b..0000000
--- a/frontend/app/components/Gaming/SubmitResult.tsx
+++ /dev/null
@@ -1,18 +0,0 @@
-import type { components } from "../../api/schema";
-import SubmitStatusLabel from "../SubmitStatusLabel";
-
-type Props = {
- status: components["schemas"]["ExecutionStatus"];
-};
-
-export default function SubmitResult({ status }: Props) {
- return (
- <div className="flex flex-col gap-2">
- <div className="flex">
- <div className="grow font-bold text-xl text-center">
- <SubmitStatusLabel status={status} />
- </div>
- </div>
- </div>
- );
-}
diff --git a/frontend/app/components/GolfPlayApps/GolfPlayAppGaming.tsx b/frontend/app/components/GolfPlayApps/GolfPlayAppGaming.tsx
index 0931f73..c4bd772 100644
--- a/frontend/app/components/GolfPlayApps/GolfPlayAppGaming.tsx
+++ b/frontend/app/components/GolfPlayApps/GolfPlayAppGaming.tsx
@@ -5,26 +5,18 @@ import {
gamingLeftTimeSecondsAtom,
scoreAtom,
statusAtom,
+ calcCodeSize,
} from "../../states/play";
import type { PlayerProfile } from "../../types/PlayerProfile";
import BorderedContainer from "../BorderedContainer";
import LeftTime from "../Gaming/LeftTime";
-import Problem from "../Gaming/Problem";
+import ProblemColumn from "../Gaming/ProblemColumn";
import SubmitButton from "../SubmitButton";
import SubmitStatusLabel from "../SubmitStatusLabel";
import ThreeColumnLayout from "../ThreeColumnLayout";
+import TitledColumn from "../TitledColumn";
import UserIcon from "../UserIcon";
-function calcCodeSize(code: string): number {
- const trimmed = code
- .replace(/\s+/g, "")
- .replace(/^<\?php/, "")
- .replace(/^<\?/, "")
- .replace(/\?>$/, "");
- const utf8Encoded = new TextEncoder().encode(trimmed);
- return utf8Encoded.length;
-}
-
type Props = {
gameDisplayName: string;
playerProfile: PlayerProfile;
@@ -86,13 +78,12 @@ export default function GolfPlayAppGaming({
</Link>
</div>
<ThreeColumnLayout>
- <Problem
+ <ProblemColumn
title={problemTitle}
description={problemDescription}
sampleCode={sampleCode}
/>
- <div className="p-4 flex flex-col gap-4">
- <div className="text-center text-xl font-bold">ソースコード</div>
+ <TitledColumn title="ソースコード">
<BorderedContainer className="grow flex flex-col gap-4">
<div className="flex flex-row gap-2 items-center">
<div className="grow font-semibold text-lg">
@@ -109,9 +100,8 @@ export default function GolfPlayAppGaming({
className="grow resize-none h-full w-full p-2 bg-gray-50 rounded-lg border border-gray-300 focus:outline-hidden focus:ring-2 focus:ring-gray-400 transition duration-300"
/>
</BorderedContainer>
- </div>
- <div className="p-4 flex flex-col gap-4">
- <div className="text-center text-xl font-bold">提出結果</div>
+ </TitledColumn>
+ <TitledColumn title="提出結果">
<div className="overflow-hidden border-2 border-blue-600 rounded-xl">
<table className="min-w-full divide-y divide-gray-400 border-collapse">
<thead className="bg-gray-50">
@@ -139,7 +129,7 @@ export default function GolfPlayAppGaming({
NOTE:
過去の提出結果を閲覧する機能は現在実装中です。それまでは提出コードをお手元に保管しておいてください。
</p>
- </div>
+ </TitledColumn>
</ThreeColumnLayout>
</div>
);
diff --git a/frontend/app/components/GolfWatchApps/GolfWatchAppGaming1v1.tsx b/frontend/app/components/GolfWatchApps/GolfWatchAppGaming1v1.tsx
index f72397d..4a7fe8a 100644
--- a/frontend/app/components/GolfWatchApps/GolfWatchAppGaming1v1.tsx
+++ b/frontend/app/components/GolfWatchApps/GolfWatchAppGaming1v1.tsx
@@ -2,15 +2,18 @@ import { useAtomValue } from "jotai";
import {
gamingLeftTimeSecondsAtom,
latestGameStatesAtom,
+ calcCodeSize,
} from "../../states/watch";
import type { PlayerProfile } from "../../types/PlayerProfile";
+import BorderedContainer from "../BorderedContainer";
+import SubmitStatusLabel from "../SubmitStatusLabel";
+import ThreeColumnLayout from "../ThreeColumnLayout";
+import TitledColumn from "../TitledColumn";
+import UserIcon from "../UserIcon";
import CodeBlock from "../Gaming/CodeBlock";
import LeftTime from "../Gaming/LeftTime";
-import Problem from "../Gaming/Problem";
+import ProblemColumn from "../Gaming/ProblemColumn";
import ScoreBar from "../Gaming/ScoreBar";
-import SubmitResult from "../Gaming/SubmitResult";
-import ThreeColumnLayout from "../ThreeColumnLayout";
-import UserIcon from "../UserIcon";
type Props = {
gameDisplayName: string;
@@ -43,6 +46,9 @@ export default function GolfWatchAppGaming1v1({
const scoreB = stateB?.score ?? null;
const statusB = stateB?.status ?? "none";
+ const codeSizeA = calcCodeSize(codeA);
+ const codeSizeB = calcCodeSize(codeB);
+
const topBg = gameResult
? gameResult === "winA"
? "bg-orange-400"
@@ -102,19 +108,27 @@ export default function GolfWatchAppGaming1v1({
bgB="bg-purple-400"
/>
<ThreeColumnLayout>
- <CodeBlock code={codeA} language="php" />
- <div className="flex flex-col gap-4">
- <div className="grid grid-cols-2 gap-4">
- <SubmitResult status={statusA} />
- <SubmitResult status={statusB} />
- </div>
- <Problem
- title={problemTitle}
- description={problemDescription}
- sampleCode={sampleCode}
- />
- </div>
- <CodeBlock code={codeB} language="php" />
+ <TitledColumn title={<SubmitStatusLabel status={statusA} />}>
+ <BorderedContainer className="grow flex flex-col gap-4">
+ <div className="text-center font-semibold text-lg">
+ コードサイズ: {codeSizeA}
+ </div>
+ <CodeBlock code={codeA} language="php" />
+ </BorderedContainer>
+ </TitledColumn>
+ <ProblemColumn
+ title={problemTitle}
+ description={problemDescription}
+ sampleCode={sampleCode}
+ />
+ <TitledColumn title={<SubmitStatusLabel status={statusB} />}>
+ <BorderedContainer className="grow flex flex-col gap-4">
+ <div className="text-center font-semibold text-lg">
+ コードサイズ: {codeSizeB}
+ </div>
+ <CodeBlock code={codeB} language="php" />
+ </BorderedContainer>
+ </TitledColumn>
</ThreeColumnLayout>
</div>
);
diff --git a/frontend/app/components/GolfWatchApps/GolfWatchAppGamingMultiplayer.tsx b/frontend/app/components/GolfWatchApps/GolfWatchAppGamingMultiplayer.tsx
index 06a2376..a6b9464 100644
--- a/frontend/app/components/GolfWatchApps/GolfWatchAppGamingMultiplayer.tsx
+++ b/frontend/app/components/GolfWatchApps/GolfWatchAppGamingMultiplayer.tsx
@@ -2,8 +2,9 @@ import { useAtomValue } from "jotai";
import type { components } from "../../api/schema";
import { gamingLeftTimeSecondsAtom } from "../../states/watch";
import LeftTime from "../Gaming/LeftTime";
-import Problem from "../Gaming/Problem";
+import ProblemColumn from "../Gaming/ProblemColumn";
import RankingTable from "../Gaming/RankingTable";
+import TitledColumn from "../TitledColumn";
import TwoColumnLayout from "../TwoColumnLayout";
type RankingEntry = components["schemas"]["RankingEntry"];
@@ -27,7 +28,7 @@ export default function GolfWatchAppGamingMultiplayer({
return (
<div className="min-h-screen bg-gray-100 flex flex-col">
- <div className={`text-white bg-sky-600 grid grid-cols-3 px-4 py-2`}>
+ <div className="text-white bg-sky-600 grid grid-cols-3 px-4 py-2">
<div className="font-bold flex justify-between my-auto"></div>
<div className="font-bold text-center">
<div className="text-gray-100">{gameDisplayName}</div>
@@ -36,15 +37,14 @@ export default function GolfWatchAppGamingMultiplayer({
<div className="font-bold flex justify-between my-auto"></div>
</div>
<TwoColumnLayout>
- <Problem
+ <ProblemColumn
title={problemTitle}
description={problemDescription}
sampleCode={sampleCode}
/>
- <div className="p-4 flex flex-col gap-4">
- <div className="text-center text-xl font-bold">順位表</div>
+ <TitledColumn title="順位表">
<RankingTable ranking={ranking} />
- </div>
+ </TitledColumn>
</TwoColumnLayout>
</div>
);
diff --git a/frontend/app/components/TitledColumn.tsx b/frontend/app/components/TitledColumn.tsx
new file mode 100644
index 0000000..4272ad4
--- /dev/null
+++ b/frontend/app/components/TitledColumn.tsx
@@ -0,0 +1,15 @@
+import React from "react";
+
+type Props = {
+ children: React.ReactNode;
+ title: React.ReactNode;
+};
+
+export default function TitledColumn({ children, title }: Props) {
+ return (
+ <div className="p-4 flex flex-col gap-4">
+ <div className="text-center text-xl font-bold">{title}</div>
+ {children}
+ </div>
+ );
+}
diff --git a/frontend/app/states/play.ts b/frontend/app/states/play.ts
index 7bf4b4e..79b25c7 100644
--- a/frontend/app/states/play.ts
+++ b/frontend/app/states/play.ts
@@ -100,3 +100,13 @@ export const setLatestGameStateAtom = atom(
set(rawScoreAtom, value.score);
},
);
+
+export function calcCodeSize(code: string): number {
+ const trimmed = code
+ .replace(/\s+/g, "")
+ .replace(/^<\?php/, "")
+ .replace(/^<\?/, "")
+ .replace(/\?>$/, "");
+ const utf8Encoded = new TextEncoder().encode(trimmed);
+ return utf8Encoded.length;
+}
diff --git a/frontend/app/states/watch.ts b/frontend/app/states/watch.ts
index 14a70b4..8c7faa7 100644
--- a/frontend/app/states/watch.ts
+++ b/frontend/app/states/watch.ts
@@ -83,3 +83,13 @@ export const setLatestGameStatesAtom = atom(
set(rawLatestGameStatesAtom, value);
},
);
+
+export function calcCodeSize(code: string): number {
+ const trimmed = code
+ .replace(/\s+/g, "")
+ .replace(/^<\?php/, "")
+ .replace(/^<\?/, "")
+ .replace(/\?>$/, "");
+ const utf8Encoded = new TextEncoder().encode(trimmed);
+ return utf8Encoded.length;
+}