diff options
| author | nsfisis <nsfisis@gmail.com> | 2025-03-20 19:41:38 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2025-03-20 19:41:39 +0900 |
| commit | 1a08d06be929900fb8d8b61a1ac0611005c277e8 (patch) | |
| tree | b064bfb79cc0021a42c423a04359504369e1faa5 /frontend/app | |
| parent | 96081efcce7b7e1f4540cb74cb511a341f2cb4d3 (diff) | |
| download | phperkaigi-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/app')
| -rw-r--r-- | frontend/app/api/schema.d.ts | 2 | ||||
| -rw-r--r-- | frontend/app/components/Gaming/RankingTable.tsx | 66 | ||||
| -rw-r--r-- | frontend/app/components/GolfWatchApps/GolfWatchAppGamingMultiplayer.tsx | 44 |
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> |
