aboutsummaryrefslogtreecommitdiffhomepage
path: root/frontend
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2025-03-20 19:41:38 +0900
committernsfisis <nsfisis@gmail.com>2025-03-20 19:41:39 +0900
commit1a08d06be929900fb8d8b61a1ac0611005c277e8 (patch)
treeb064bfb79cc0021a42c423a04359504369e1faa5 /frontend
parent96081efcce7b7e1f4540cb74cb511a341f2cb4d3 (diff)
downloadphperkaigi-2025-albatross-1a08d06be929900fb8d8b61a1ac0611005c277e8.tar.gz
phperkaigi-2025-albatross-1a08d06be929900fb8d8b61a1ac0611005c277e8.tar.zst
phperkaigi-2025-albatross-1a08d06be929900fb8d8b61a1ac0611005c277e8.zip
feat: show submission date on ranking
Diffstat (limited to 'frontend')
-rw-r--r--frontend/app/api/schema.d.ts2
-rw-r--r--frontend/app/components/Gaming/RankingTable.tsx66
-rw-r--r--frontend/app/components/GolfWatchApps/GolfWatchAppGamingMultiplayer.tsx44
3 files changed, 70 insertions, 42 deletions
diff --git a/frontend/app/api/schema.d.ts b/frontend/app/api/schema.d.ts
index b5ec26b..0cace21 100644
--- a/frontend/app/api/schema.d.ts
+++ b/frontend/app/api/schema.d.ts
@@ -207,6 +207,8 @@ export interface components {
player: components["schemas"]["User"];
/** @example 100 */
score: number;
+ /** @example 946684800 */
+ submitted_at: number;
};
};
responses: {
diff --git a/frontend/app/components/Gaming/RankingTable.tsx b/frontend/app/components/Gaming/RankingTable.tsx
new file mode 100644
index 0000000..e712ed9
--- /dev/null
+++ b/frontend/app/components/Gaming/RankingTable.tsx
@@ -0,0 +1,66 @@
+import React from "react";
+import type { components } from "../../api/schema";
+
+type RankingEntry = components["schemas"]["RankingEntry"];
+
+type Props = {
+ ranking: RankingEntry[];
+};
+
+function TableHeaderCell({ children }: { children: React.ReactNode }) {
+ return (
+ <th scope="col" className="px-6 py-3 text-left font-medium text-gray-800">
+ {children}
+ </th>
+ );
+}
+
+function TableBodyCell({ children }: { children: React.ReactNode }) {
+ return (
+ <td className="px-6 py-4 whitespace-nowrap text-gray-900">{children}</td>
+ );
+}
+
+function formatUnixTimestamp(timestamp: number) {
+ const date = new Date(timestamp * 1000);
+
+ const year = date.getFullYear();
+ const month = (date.getMonth() + 1).toString().padStart(2, "0");
+ const day = date.getDate().toString().padStart(2, "0");
+ const hours = date.getHours().toString().padStart(2, "0");
+ const minutes = date.getMinutes().toString().padStart(2, "0");
+
+ return `${year}-${month}-${day} ${hours}:${minutes}`;
+}
+
+export default function RankingTable({ ranking }: Props) {
+ return (
+ <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">
+ <tr>
+ <TableHeaderCell>順位</TableHeaderCell>
+ <TableHeaderCell>プレイヤー</TableHeaderCell>
+ <TableHeaderCell>スコア</TableHeaderCell>
+ <TableHeaderCell>提出時刻</TableHeaderCell>
+ </tr>
+ </thead>
+ <tbody className="bg-white divide-y divide-gray-300">
+ {ranking.map((entry, index) => (
+ <tr key={entry.player.user_id}>
+ <TableBodyCell>{index + 1}</TableBodyCell>
+ <TableBodyCell>
+ {entry.player.display_name}
+ {entry.player.label && ` (${entry.player.label})`}
+ </TableBodyCell>
+ <TableBodyCell>{entry.score}</TableBodyCell>
+ <TableBodyCell>
+ {formatUnixTimestamp(entry.submitted_at)}
+ </TableBodyCell>
+ </tr>
+ ))}
+ </tbody>
+ </table>
+ </div>
+ );
+}
diff --git a/frontend/app/components/GolfWatchApps/GolfWatchAppGamingMultiplayer.tsx b/frontend/app/components/GolfWatchApps/GolfWatchAppGamingMultiplayer.tsx
index a2b2d21..f3a377b 100644
--- a/frontend/app/components/GolfWatchApps/GolfWatchAppGamingMultiplayer.tsx
+++ b/frontend/app/components/GolfWatchApps/GolfWatchAppGamingMultiplayer.tsx
@@ -3,6 +3,7 @@ import type { components } from "../../api/schema";
import { gamingLeftTimeSecondsAtom } from "../../states/watch";
import LeftTime from "../Gaming/LeftTime";
import Problem from "../Gaming/Problem";
+import RankingTable from "../Gaming/RankingTable";
type RankingEntry = components["schemas"]["RankingEntry"];
@@ -41,48 +42,7 @@ export default function GolfWatchAppGamingMultiplayer({
/>
<div className="p-4 flex flex-col gap-4">
<div className="text-center text-xl font-bold">順位表</div>
- <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">
- <tr>
- <th
- scope="col"
- className="px-6 py-3 text-left font-medium text-gray-800"
- >
- 順位
- </th>
- <th
- scope="col"
- className="px-6 py-3 text-left font-medium text-gray-800"
- >
- プレイヤー
- </th>
- <th
- scope="col"
- className="px-6 py-3 text-left font-medium text-gray-800"
- >
- スコア
- </th>
- </tr>
- </thead>
- <tbody className="bg-white divide-y divide-gray-300">
- {ranking.map((entry, index) => (
- <tr key={entry.player.user_id}>
- <td className="px-6 py-4 whitespace-nowrap text-gray-900">
- {index + 1}
- </td>
- <td className="px-6 py-4 whitespace-nowrap text-gray-900">
- {entry.player.display_name}
- {entry.player.label && ` (${entry.player.label})`}
- </td>
- <td className="px-6 py-4 whitespace-nowrap text-gray-900">
- {entry.score}
- </td>
- </tr>
- ))}
- </tbody>
- </table>
- </div>
+ <RankingTable ranking={ranking} />
</div>
</div>
</div>