From 8e73d12a703e90ad908962143951178c13d0d6fe Mon Sep 17 00:00:00 2001 From: nsfisis Date: Fri, 20 Feb 2026 23:32:22 +0900 Subject: feat: add user submission history page Allow users to view their own past submissions (code, size, status, timestamp) for each game. Adds API endpoint, backend handler, SQL query, and frontend page with expandable code display. Co-Authored-By: Claude Opus 4.6 --- frontend/app/pages/SubmissionsPage.tsx | 126 +++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 frontend/app/pages/SubmissionsPage.tsx (limited to 'frontend/app/pages/SubmissionsPage.tsx') diff --git a/frontend/app/pages/SubmissionsPage.tsx b/frontend/app/pages/SubmissionsPage.tsx new file mode 100644 index 0000000..2c3329d --- /dev/null +++ b/frontend/app/pages/SubmissionsPage.tsx @@ -0,0 +1,126 @@ +import { useEffect, useState } from "react"; +import { createApiClient } from "../api/client"; +import type { components } from "../api/schema"; +import BorderedContainerWithCaption from "../components/BorderedContainerWithCaption"; +import NavigateLink from "../components/NavigateLink"; +import SubmitStatusLabel from "../components/SubmitStatusLabel"; +import { APP_NAME } from "../config"; +import { usePageTitle } from "../hooks/usePageTitle"; + +type Submission = components["schemas"]["Submission"]; + +export default function SubmissionsPage({ gameId }: { gameId: string }) { + usePageTitle(`Submissions | ${APP_NAME}`); + + const [submissions, setSubmissions] = useState([]); + const [loading, setLoading] = useState(true); + const [expandedId, setExpandedId] = useState(null); + + const numericGameId = Number(gameId); + + useEffect(() => { + const apiClient = createApiClient(); + apiClient + .getGamePlaySubmissions(numericGameId) + .then(({ submissions }) => setSubmissions(submissions)) + .catch(() => {}) + .finally(() => setLoading(false)); + }, [numericGameId]); + + if (loading) { + return ( +
+

Loading...

+
+ ); + } + + return ( +
+ +
+ {submissions.length === 0 ? ( +

提出履歴はありません

+ ) : ( +
    + {submissions.map((s) => ( +
  • +
    +
    + + + {s.code_size} + + bytes + + +
    +
    + + {formatDate(s.created_at)} + + +
    +
    + {expandedId === s.submission_id && ( +
    +											{s.code}
    +										
    + )} +
  • + ))} +
+ )} +
+
+ 対戦に戻る + ダッシュボードに戻る +
+ ); +} + +function StatusBadge({ + status, +}: { + status: components["schemas"]["ExecutionStatus"]; +}) { + const colorClass = + status === "success" + ? "bg-green-100 text-green-800" + : status === "running" + ? "bg-yellow-100 text-yellow-800" + : status === "none" + ? "bg-gray-100 text-gray-800" + : "bg-red-100 text-red-800"; + + return ( + + + + ); +} + +function formatDate(unixTimestamp: number): string { + const date = new Date(unixTimestamp * 1000); + return date.toLocaleString("ja-JP", { + month: "2-digit", + day: "2-digit", + hour: "2-digit", + minute: "2-digit", + second: "2-digit", + }); +} -- cgit v1.3.1