From 46f9ba5d8c295454381655e6ec02ad3cf8bd79db Mon Sep 17 00:00:00 2001 From: nsfisis Date: Fri, 6 Mar 2026 02:18:40 +0900 Subject: style: switch from tab to space indentation in frontend and worker/php Update biome.json indentStyle from "tab" to "space" and reformat all files in both workspaces. Co-Authored-By: Claude Opus 4.6 --- frontend/app/pages/DashboardPage.tsx | 202 +++++----- frontend/app/pages/GolfPlayPage.tsx | 94 ++--- frontend/app/pages/GolfProblemPreviewPage.tsx | 90 ++--- frontend/app/pages/GolfWatchPage.tsx | 116 +++--- frontend/app/pages/IndexPage.tsx | 62 ++-- frontend/app/pages/LoginPage.tsx | 166 ++++----- frontend/app/pages/TournamentPage.test.tsx | 90 ++--- frontend/app/pages/TournamentPage.tsx | 510 +++++++++++++------------- 8 files changed, 665 insertions(+), 665 deletions(-) (limited to 'frontend/app/pages') diff --git a/frontend/app/pages/DashboardPage.tsx b/frontend/app/pages/DashboardPage.tsx index 54bfdd6..74be96e 100644 --- a/frontend/app/pages/DashboardPage.tsx +++ b/frontend/app/pages/DashboardPage.tsx @@ -12,111 +12,111 @@ import { usePageTitle } from "../hooks/usePageTitle"; type Game = components["schemas"]["Game"]; export default function DashboardPage() { - usePageTitle(`Dashboard | ${APP_NAME}`); + usePageTitle(`Dashboard | ${APP_NAME}`); - const { user, isLoggedIn, isLoading: authLoading, logout } = useAuth(); - const [, navigate] = useLocation(); + const { user, isLoggedIn, isLoading: authLoading, logout } = useAuth(); + const [, navigate] = useLocation(); - const [games, setGames] = useState([]); - const [loading, setLoading] = useState(true); + const [games, setGames] = useState([]); + const [loading, setLoading] = useState(true); - useEffect(() => { - const apiClient = createApiClient(); - apiClient - .getGames() - .then(({ games }) => setGames(games)) - .finally(() => setLoading(false)); - }, []); + useEffect(() => { + const apiClient = createApiClient(); + apiClient + .getGames() + .then(({ games }) => setGames(games)) + .finally(() => setLoading(false)); + }, []); - async function handleLogout() { - await logout(); - navigate("/"); - } + async function handleLogout() { + await logout(); + navigate("/"); + } - if (loading || authLoading) { - return ( -
-

Loading...

-
- ); - } + if (loading || authLoading) { + return ( +
+

Loading...

+
+ ); + } - return ( -
- {isLoggedIn && user?.icon_path && ( - - )} - {isLoggedIn ? ( -

- {user?.display_name} -

- ) : ( -

試合一覧

- )} - -
- {games.length === 0 ? ( -

試合はありません

- ) : ( -
    - {games.map((game) => ( -
  • -
    - - {game.display_name} - -
    -
    - {isLoggedIn && game.started_at == null && ( - - 問題を見る - - )} - {isLoggedIn && ( - - 対戦 - - )} - - 観戦 - -
    -
  • - ))} -
- )} -
-
- {isLoggedIn ? ( - - ) : ( - ログイン - )} - {isLoggedIn && user?.is_admin && ( - - Admin Dashboard - - )} -
- ); + return ( +
+ {isLoggedIn && user?.icon_path && ( + + )} + {isLoggedIn ? ( +

+ {user?.display_name} +

+ ) : ( +

試合一覧

+ )} + +
+ {games.length === 0 ? ( +

試合はありません

+ ) : ( +
    + {games.map((game) => ( +
  • +
    + + {game.display_name} + +
    +
    + {isLoggedIn && game.started_at == null && ( + + 問題を見る + + )} + {isLoggedIn && ( + + 対戦 + + )} + + 観戦 + +
    +
  • + ))} +
+ )} +
+
+ {isLoggedIn ? ( + + ) : ( + ログイン + )} + {isLoggedIn && user?.is_admin && ( + + Admin Dashboard + + )} +
+ ); } diff --git a/frontend/app/pages/GolfPlayPage.tsx b/frontend/app/pages/GolfPlayPage.tsx index 49f47f6..ff6273d 100644 --- a/frontend/app/pages/GolfPlayPage.tsx +++ b/frontend/app/pages/GolfPlayPage.tsx @@ -12,58 +12,58 @@ type Game = components["schemas"]["Game"]; type LatestGameState = components["schemas"]["LatestGameState"]; export default function GolfPlayPage({ gameId }: { gameId: string }) { - const { user } = useAuth(); - const [, navigate] = useLocation(); + const { user } = useAuth(); + const [, navigate] = useLocation(); - const [game, setGame] = useState(null); - const [gameState, setGameState] = useState(null); - const [loading, setLoading] = useState(true); + const [game, setGame] = useState(null); + const [gameState, setGameState] = useState(null); + const [loading, setLoading] = useState(true); - const gameIdNum = Number(gameId); + const gameIdNum = Number(gameId); - usePageTitle( - game - ? `Golf Playing ${game.display_name} | ${APP_NAME}` - : `Golf Playing | ${APP_NAME}`, - ); + usePageTitle( + game + ? `Golf Playing ${game.display_name} | ${APP_NAME}` + : `Golf Playing | ${APP_NAME}`, + ); - useEffect(() => { - const apiClient = createApiClient(); - Promise.all([ - apiClient.getGame(gameIdNum), - apiClient.getGamePlayLatestState(gameIdNum), - ]) - .then(([{ game }, { state }]) => { - setGame(game); - setGameState(state); - }) - .catch(() => navigate("/dashboard")) - .finally(() => setLoading(false)); - }, [gameIdNum, navigate]); + useEffect(() => { + const apiClient = createApiClient(); + Promise.all([ + apiClient.getGame(gameIdNum), + apiClient.getGamePlayLatestState(gameIdNum), + ]) + .then(([{ game }, { state }]) => { + setGame(game); + setGameState(state); + }) + .catch(() => navigate("/dashboard")) + .finally(() => setLoading(false)); + }, [gameIdNum, navigate]); - const store = useMemo(() => { - if (!game || !user) return null; - return createStore(); - }, [game, user]); + const store = useMemo(() => { + if (!game || !user) return null; + return createStore(); + }, [game, user]); - if (loading || !game || !gameState || !user || !store) { - return ( -
-

Loading...

-
- ); - } + if (loading || !game || !gameState || !user || !store) { + return ( +
+

Loading...

+
+ ); + } - return ( - - - - - - ); + return ( + + + + + + ); } diff --git a/frontend/app/pages/GolfProblemPreviewPage.tsx b/frontend/app/pages/GolfProblemPreviewPage.tsx index 4a84809..9eee691 100644 --- a/frontend/app/pages/GolfProblemPreviewPage.tsx +++ b/frontend/app/pages/GolfProblemPreviewPage.tsx @@ -10,54 +10,54 @@ import { usePageTitle } from "../hooks/usePageTitle"; type Game = components["schemas"]["Game"]; export default function GolfProblemPreviewPage({ gameId }: { gameId: string }) { - const [, navigate] = useLocation(); - const [game, setGame] = useState(null); - const [loading, setLoading] = useState(true); + const [, navigate] = useLocation(); + const [game, setGame] = useState(null); + const [loading, setLoading] = useState(true); - const gameIdNum = Number(gameId); + const gameIdNum = Number(gameId); - usePageTitle( - game - ? `${game.display_name} - 問題プレビュー | ${APP_NAME}` - : `問題プレビュー | ${APP_NAME}`, - ); + usePageTitle( + game + ? `${game.display_name} - 問題プレビュー | ${APP_NAME}` + : `問題プレビュー | ${APP_NAME}`, + ); - useEffect(() => { - const apiClient = createApiClient(); - apiClient - .getGame(gameIdNum) - .then(({ game }) => setGame(game)) - .catch(() => navigate("/dashboard")) - .finally(() => setLoading(false)); - }, [gameIdNum, navigate]); + useEffect(() => { + const apiClient = createApiClient(); + apiClient + .getGame(gameIdNum) + .then(({ game }) => setGame(game)) + .catch(() => navigate("/dashboard")) + .finally(() => setLoading(false)); + }, [gameIdNum, navigate]); - if (loading || !game) { - return ( -
-

Loading...

-
- ); - } + if (loading || !game) { + return ( +
+

Loading...

+
+ ); + } - return ( -
-

{game.display_name}

-
- -
-
- - 対戦ページへ - - - 観戦ページへ - -
- ダッシュボードへ戻る -
- ); + return ( +
+

{game.display_name}

+
+ +
+
+ + 対戦ページへ + + + 観戦ページへ + +
+ ダッシュボードへ戻る +
+ ); } diff --git a/frontend/app/pages/GolfWatchPage.tsx b/frontend/app/pages/GolfWatchPage.tsx index 168bd6f..013e1a0 100644 --- a/frontend/app/pages/GolfWatchPage.tsx +++ b/frontend/app/pages/GolfWatchPage.tsx @@ -11,69 +11,69 @@ type LatestGameState = components["schemas"]["LatestGameState"]; type RankingEntry = components["schemas"]["RankingEntry"]; export default function GolfWatchPage({ gameId }: { gameId: string }) { - const [game, setGame] = useState(null); - const [ranking, setRanking] = useState([]); - const [gameStates, setGameStates] = useState<{ - [key: string]: LatestGameState; - }>({}); - const [loading, setLoading] = useState(true); - const [error, setError] = useState(false); + const [game, setGame] = useState(null); + const [ranking, setRanking] = useState([]); + const [gameStates, setGameStates] = useState<{ + [key: string]: LatestGameState; + }>({}); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(false); - const gameIdNum = Number(gameId); + const gameIdNum = Number(gameId); - usePageTitle( - game - ? `Golf Watching ${game.display_name} | ${APP_NAME}` - : `Golf Watching | ${APP_NAME}`, - ); + usePageTitle( + game + ? `Golf Watching ${game.display_name} | ${APP_NAME}` + : `Golf Watching | ${APP_NAME}`, + ); - useEffect(() => { - const apiClient = createApiClient(); - Promise.all([ - apiClient.getGame(gameIdNum), - apiClient.getGameWatchRanking(gameIdNum), - apiClient.getGameWatchLatestStates(gameIdNum), - ]) - .then(([{ game }, { ranking }, { states }]) => { - setGame(game); - setRanking(ranking); - setGameStates(states); - }) - .catch(() => setError(true)) - .finally(() => setLoading(false)); - }, [gameIdNum]); + useEffect(() => { + const apiClient = createApiClient(); + Promise.all([ + apiClient.getGame(gameIdNum), + apiClient.getGameWatchRanking(gameIdNum), + apiClient.getGameWatchLatestStates(gameIdNum), + ]) + .then(([{ game }, { ranking }, { states }]) => { + setGame(game); + setRanking(ranking); + setGameStates(states); + }) + .catch(() => setError(true)) + .finally(() => setLoading(false)); + }, [gameIdNum]); - const store = useMemo(() => { - if (!game) return null; - return createStore(); - }, [game]); + const store = useMemo(() => { + if (!game) return null; + return createStore(); + }, [game]); - if (loading) { - return ( -
-

Loading...

-
- ); - } + if (loading) { + return ( +
+

Loading...

+
+ ); + } - if (error || !game || !store) { - return ( -
-

試合が見つかりませんでした

-
- ); - } + if (error || !game || !store) { + return ( +
+

試合が見つかりませんでした

+
+ ); + } - return ( - - - - - - ); + return ( + + + + + + ); } diff --git a/frontend/app/pages/IndexPage.tsx b/frontend/app/pages/IndexPage.tsx index 8dfbefe..a3aa9f1 100644 --- a/frontend/app/pages/IndexPage.tsx +++ b/frontend/app/pages/IndexPage.tsx @@ -4,36 +4,36 @@ import { APP_NAME, BASE_PATH } from "../config"; import { usePageTitle } from "../hooks/usePageTitle"; export default function IndexPage() { - usePageTitle(APP_NAME); + usePageTitle(APP_NAME); - return ( -
- PHPerKaigi 2026 -
-
-
PHPER CODE BATTLE
-
-
-
- -

- PHPer コードバトルは指示された動作をする PHP - コードをより短く書けた方が勝ち、という 1 対 1 の対戦コンテンツです。 - 3/20 の day 0 と 3/21 の day 1 では、2/28 - に実施されたオフライン予選と、当日まで開催しているオンライン予選を勝ち抜いたプレイヤーによるトーナメント形式での - PHPer コードバトルを実施します。ここでは短いコードが正義です! - 可読性も保守性も放り投げた、イベントならではのコードをお楽しみください! -

-
-
-
- 観戦する - ログイン -
-
- ); + return ( +
+ PHPerKaigi 2026 +
+
+
PHPER CODE BATTLE
+
+
+
+ +

+ PHPer コードバトルは指示された動作をする PHP + コードをより短く書けた方が勝ち、という 1 対 1 の対戦コンテンツです。 + 3/20 の day 0 と 3/21 の day 1 では、2/28 + に実施されたオフライン予選と、当日まで開催しているオンライン予選を勝ち抜いたプレイヤーによるトーナメント形式での + PHPer コードバトルを実施します。ここでは短いコードが正義です! + 可読性も保守性も放り投げた、イベントならではのコードをお楽しみください! +

+
+
+
+ 観戦する + ログイン +
+
+ ); } diff --git a/frontend/app/pages/LoginPage.tsx b/frontend/app/pages/LoginPage.tsx index 139b1f0..4130518 100644 --- a/frontend/app/pages/LoginPage.tsx +++ b/frontend/app/pages/LoginPage.tsx @@ -8,94 +8,94 @@ import { useAuth } from "../hooks/useAuth"; import { usePageTitle } from "../hooks/usePageTitle"; export default function LoginPage() { - usePageTitle(`Login | ${APP_NAME}`); + usePageTitle(`Login | ${APP_NAME}`); - const { login } = useAuth(); - const [, navigate] = useLocation(); + const { login } = useAuth(); + const [, navigate] = useLocation(); - const [error, setError] = useState(null); - const [fieldErrors, setFieldErrors] = useState<{ - username?: string; - password?: string; - }>({}); - const [submitting, setSubmitting] = useState(false); + const [error, setError] = useState(null); + const [fieldErrors, setFieldErrors] = useState<{ + username?: string; + password?: string; + }>({}); + const [submitting, setSubmitting] = useState(false); - async function handleSubmit(e: FormEvent) { - e.preventDefault(); - const formData = new FormData(e.currentTarget); - const username = String(formData.get("username")); - const password = String(formData.get("password")); + async function handleSubmit(e: FormEvent) { + e.preventDefault(); + const formData = new FormData(e.currentTarget); + const username = String(formData.get("username")); + const password = String(formData.get("password")); - const errors: { username?: string; password?: string } = {}; - if (username === "") errors.username = "ユーザー名を入力してください"; - if (password === "") errors.password = "パスワードを入力してください"; - if (Object.keys(errors).length > 0) { - setFieldErrors(errors); - setError("ユーザー名またはパスワードが誤っています"); - return; - } + const errors: { username?: string; password?: string } = {}; + if (username === "") errors.username = "ユーザー名を入力してください"; + if (password === "") errors.password = "パスワードを入力してください"; + if (Object.keys(errors).length > 0) { + setFieldErrors(errors); + setError("ユーザー名またはパスワードが誤っています"); + return; + } - setSubmitting(true); - setError(null); - setFieldErrors({}); + setSubmitting(true); + setError(null); + setFieldErrors({}); - try { - await login(username, password); - navigate("/dashboard"); - } catch (err) { - setError(err instanceof Error ? err.message : "ログインに失敗しました"); - } finally { - setSubmitting(false); - } - } + try { + await login(username, password); + navigate("/dashboard"); + } catch (err) { + setError(err instanceof Error ? err.message : "ログインに失敗しました"); + } finally { + setSubmitting(false); + } + } - return ( -
-
- -
-

- fortee アカウントでログイン -

- {error &&

{error}

} -
- - - {fieldErrors.username && ( -

{fieldErrors.username}

- )} -
-
- - - {fieldErrors.password && ( -

{fieldErrors.password}

- )} -
-
- - {submitting ? "ログイン中..." : "ログイン"} - -
-
-
-
-
- ); + return ( +
+
+ +
+

+ fortee アカウントでログイン +

+ {error &&

{error}

} +
+ + + {fieldErrors.username && ( +

{fieldErrors.username}

+ )} +
+
+ + + {fieldErrors.password && ( +

{fieldErrors.password}

+ )} +
+
+ + {submitting ? "ログイン中..." : "ログイン"} + +
+
+
+
+
+ ); } diff --git a/frontend/app/pages/TournamentPage.test.tsx b/frontend/app/pages/TournamentPage.test.tsx index 3c6f116..e9e4987 100644 --- a/frontend/app/pages/TournamentPage.test.tsx +++ b/frontend/app/pages/TournamentPage.test.tsx @@ -6,55 +6,55 @@ import { afterEach, describe, expect, test } from "vitest"; import TournamentPage, { standardBracketSeedsForTest } from "./TournamentPage"; afterEach(() => { - cleanup(); + cleanup(); }); describe("standardBracketSeeds", () => { - test("bracket_size=2 returns [1, 2]", () => { - const seeds = standardBracketSeedsForTest(2); - expect(seeds).toEqual([1, 2]); - }); - - test("bracket_size=4 returns [1, 4, 2, 3]", () => { - const seeds = standardBracketSeedsForTest(4); - expect(seeds).toEqual([1, 4, 2, 3]); - }); - - test("bracket_size=8 returns [1, 8, 4, 5, 2, 7, 3, 6]", () => { - const seeds = standardBracketSeedsForTest(8); - expect(seeds).toEqual([1, 8, 4, 5, 2, 7, 3, 6]); - }); - - test("all seeds present for size 16", () => { - const seeds = standardBracketSeedsForTest(16); - expect(seeds).toHaveLength(16); - const sorted = [...seeds].sort((a, b) => a - b); - expect(sorted).toEqual(Array.from({ length: 16 }, (_, i) => i + 1)); - }); - - test("seed 1 and seed 2 on opposite sides for size 8", () => { - const seeds = standardBracketSeedsForTest(8); - const pos1 = seeds.indexOf(1); - const pos2 = seeds.indexOf(2); - // Seed 1 in first half (0-3), Seed 2 in second half (4-7) - expect(pos1).toBeLessThan(4); - expect(pos2).toBeGreaterThanOrEqual(4); - }); + test("bracket_size=2 returns [1, 2]", () => { + const seeds = standardBracketSeedsForTest(2); + expect(seeds).toEqual([1, 2]); + }); + + test("bracket_size=4 returns [1, 4, 2, 3]", () => { + const seeds = standardBracketSeedsForTest(4); + expect(seeds).toEqual([1, 4, 2, 3]); + }); + + test("bracket_size=8 returns [1, 8, 4, 5, 2, 7, 3, 6]", () => { + const seeds = standardBracketSeedsForTest(8); + expect(seeds).toEqual([1, 8, 4, 5, 2, 7, 3, 6]); + }); + + test("all seeds present for size 16", () => { + const seeds = standardBracketSeedsForTest(16); + expect(seeds).toHaveLength(16); + const sorted = [...seeds].sort((a, b) => a - b); + expect(sorted).toEqual(Array.from({ length: 16 }, (_, i) => i + 1)); + }); + + test("seed 1 and seed 2 on opposite sides for size 8", () => { + const seeds = standardBracketSeedsForTest(8); + const pos1 = seeds.indexOf(1); + const pos2 = seeds.indexOf(2); + // Seed 1 in first half (0-3), Seed 2 in second half (4-7) + expect(pos1).toBeLessThan(4); + expect(pos2).toBeGreaterThanOrEqual(4); + }); }); describe("TournamentPage", () => { - test("shows loading state initially", () => { - render(); - expect(screen.getByText("Loading...")).toBeDefined(); - }); - - test("shows error for invalid tournament ID", () => { - render(); - expect(screen.getByText("Invalid tournament ID")).toBeDefined(); - }); - - test("shows error for zero tournament ID", () => { - render(); - expect(screen.getByText("Invalid tournament ID")).toBeDefined(); - }); + test("shows loading state initially", () => { + render(); + expect(screen.getByText("Loading...")).toBeDefined(); + }); + + test("shows error for invalid tournament ID", () => { + render(); + expect(screen.getByText("Invalid tournament ID")).toBeDefined(); + }); + + test("shows error for zero tournament ID", () => { + render(); + expect(screen.getByText("Invalid tournament ID")).toBeDefined(); + }); }); diff --git a/frontend/app/pages/TournamentPage.tsx b/frontend/app/pages/TournamentPage.tsx index 0bf4895..f555ba0 100644 --- a/frontend/app/pages/TournamentPage.tsx +++ b/frontend/app/pages/TournamentPage.tsx @@ -11,304 +11,304 @@ type TournamentMatch = components["schemas"]["TournamentMatch"]; type TournamentEntry = components["schemas"]["TournamentEntry"]; function getBorderColor(match: TournamentMatch, userID?: number): string { - if (!match.winner_user_id) { - return "border-black"; - } - if (userID !== undefined && match.winner_user_id === userID) { - return "border-pink-700"; - } - return "border-gray-400"; + if (!match.winner_user_id) { + return "border-black"; + } + if (userID !== undefined && match.winner_user_id === userID) { + return "border-pink-700"; + } + return "border-gray-400"; } function PlayerCard({ entry }: { entry: TournamentEntry | undefined }) { - if (!entry) { - return ( -
- BYE -
- ); - } - return ( - -
- Seed {entry.seed} - - {entry.user.display_name} - - {entry.user.icon_path && ( - - )} -
-
- ); + if (!entry) { + return ( +
+ BYE +
+ ); + } + return ( + +
+ Seed {entry.seed} + + {entry.user.display_name} + + {entry.user.icon_path && ( + + )} +
+
+ ); } function MatchCell({ match }: { match: TournamentMatch }) { - if (match.is_bye) { - return ( -
- BYE -
- ); - } + if (match.is_bye) { + return ( +
+ BYE +
+ ); + } - const p1Color = match.winner_user_id - ? match.winner_user_id === match.player1?.user_id - ? "border-pink-700" - : "border-gray-400" - : "border-black"; - const p2Color = match.winner_user_id - ? match.winner_user_id === match.player2?.user_id - ? "border-pink-700" - : "border-gray-400" - : "border-black"; + const p1Color = match.winner_user_id + ? match.winner_user_id === match.player1?.user_id + ? "border-pink-700" + : "border-gray-400" + : "border-black"; + const p2Color = match.winner_user_id + ? match.winner_user_id === match.player2?.user_id + ? "border-pink-700" + : "border-gray-400" + : "border-black"; - return ( -
-
- {match.player1?.display_name ?? "?"} - {match.player1_score !== undefined && ( - {match.player1_score} - )} -
-
- {match.player2?.display_name ?? "?"} - {match.player2_score !== undefined && ( - {match.player2_score} - )} -
-
- ); + return ( +
+
+ {match.player1?.display_name ?? "?"} + {match.player1_score !== undefined && ( + {match.player1_score} + )} +
+
+ {match.player2?.display_name ?? "?"} + {match.player2_score !== undefined && ( + {match.player2_score} + )} +
+
+ ); } function Connector({ - position, - colSpan, - match, + position, + colSpan, + match, }: { - position: number; - colSpan: number; - match: TournamentMatch | undefined; + position: number; + colSpan: number; + match: TournamentMatch | undefined; }) { - const leftHalf = colSpan / 2; - const rightHalf = colSpan - leftHalf; + const leftHalf = colSpan / 2; + const rightHalf = colSpan - leftHalf; - const leftColor = match - ? getBorderColor(match, match.player1?.user_id) - : "border-black"; - const rightColor = match - ? getBorderColor(match, match.player2?.user_id) - : "border-black"; + const leftColor = match + ? getBorderColor(match, match.player1?.user_id) + : "border-black"; + const rightColor = match + ? getBorderColor(match, match.player2?.user_id) + : "border-black"; - return ( -
-
-
-
-
-
- ); + return ( +
+
+
+
+
+
+ ); } function TournamentBracket({ tournament }: { tournament: Tournament }) { - const { bracket_size, num_rounds, entries, matches } = tournament; + const { bracket_size, num_rounds, entries, matches } = tournament; - const matchByKey = new Map(); - for (const m of matches) { - matchByKey.set(`${m.round}-${m.position}`, m); - } + const matchByKey = new Map(); + for (const m of matches) { + matchByKey.set(`${m.round}-${m.position}`, m); + } - const entryBySeed = new Map(); - for (const e of entries) { - entryBySeed.set(e.seed, e); - } + const entryBySeed = new Map(); + for (const e of entries) { + entryBySeed.set(e.seed, e); + } - const bracketSeeds = standardBracketSeeds(bracket_size); + const bracketSeeds = standardBracketSeeds(bracket_size); - // Build rows top-to-bottom: final → ... → round 0 → players - const rows: React.ReactNode[] = []; + // Build rows top-to-bottom: final → ... → round 0 → players + const rows: React.ReactNode[] = []; - // Rounds from top (final) to bottom (round 0) - for (let round = num_rounds - 1; round >= 0; round--) { - const numPositions = bracket_size / (1 << (round + 1)); - const colSpan = bracket_size / numPositions; + // Rounds from top (final) to bottom (round 0) + for (let round = num_rounds - 1; round >= 0; round--) { + const numPositions = bracket_size / (1 << (round + 1)); + const colSpan = bracket_size / numPositions; - // Match cells for this round - const matchCells: React.ReactNode[] = []; - for (let pos = 0; pos < numPositions; pos++) { - const match = matchByKey.get(`${round}-${pos}`); - matchCells.push( -
- {match ? : null} -
, - ); - } - rows.push( -
- {matchCells} -
, - ); + // Match cells for this round + const matchCells: React.ReactNode[] = []; + for (let pos = 0; pos < numPositions; pos++) { + const match = matchByKey.get(`${round}-${pos}`); + matchCells.push( +
+ {match ? : null} +
, + ); + } + rows.push( +
+ {matchCells} +
, + ); - // Connectors below this round's matches - const connectors: React.ReactNode[] = []; - for (let pos = 0; pos < numPositions; pos++) { - const match = matchByKey.get(`${round}-${pos}`); - connectors.push( - , - ); - } - rows.push( -
- {connectors} -
, - ); - } + // Connectors below this round's matches + const connectors: React.ReactNode[] = []; + for (let pos = 0; pos < numPositions; pos++) { + const match = matchByKey.get(`${round}-${pos}`); + connectors.push( + , + ); + } + rows.push( +
+ {connectors} +
, + ); + } - // Player cards row (bottom) - const playerCards: React.ReactNode[] = []; - for (let slot = 0; slot < bracket_size; slot++) { - const seed = bracketSeeds[slot]!; - const entry = entryBySeed.get(seed); - playerCards.push( -
- -
, - ); - } - rows.push( -
- {playerCards} -
, - ); + // Player cards row (bottom) + const playerCards: React.ReactNode[] = []; + for (let slot = 0; slot < bracket_size; slot++) { + const seed = bracketSeeds[slot]!; + const entry = entryBySeed.get(seed); + playerCards.push( +
+ +
, + ); + } + rows.push( +
+ {playerCards} +
, + ); - return
{rows}
; + return
{rows}
; } // Exported for testing as standardBracketSeedsForTest export { standardBracketSeeds as standardBracketSeedsForTest }; function standardBracketSeeds(bracketSize: number): number[] { - const seeds = new Array(bracketSize).fill(0); - seeds[0] = 1; - for (let size = 2; size <= bracketSize; size *= 2) { - const temp = new Array(size).fill(0); - for (let i = 0; i < size / 2; i++) { - temp[i * 2] = seeds[i]!; - temp[i * 2 + 1] = size + 1 - seeds[i]!; - } - for (let i = 0; i < size; i++) { - seeds[i] = temp[i]!; - } - } - return seeds; + const seeds = new Array(bracketSize).fill(0); + seeds[0] = 1; + for (let size = 2; size <= bracketSize; size *= 2) { + const temp = new Array(size).fill(0); + for (let i = 0; i < size / 2; i++) { + temp[i * 2] = seeds[i]!; + temp[i * 2 + 1] = size + 1 - seeds[i]!; + } + for (let i = 0; i < size; i++) { + seeds[i] = temp[i]!; + } + } + return seeds; } export default function TournamentPage({ - tournamentId, + tournamentId, }: { - tournamentId: string; + tournamentId: string; }) { - usePageTitle(`Tournament | ${APP_NAME}`); + usePageTitle(`Tournament | ${APP_NAME}`); - const id = Number(tournamentId); - const isValidId = id > 0; + const id = Number(tournamentId); + const isValidId = id > 0; - const [tournament, setTournament] = useState(null); - const [loading, setLoading] = useState(isValidId); - const [error, setError] = useState( - isValidId ? null : "Invalid tournament ID", - ); + const [tournament, setTournament] = useState(null); + const [loading, setLoading] = useState(isValidId); + const [error, setError] = useState( + isValidId ? null : "Invalid tournament ID", + ); - useEffect(() => { - if (!isValidId) { - return; - } + useEffect(() => { + if (!isValidId) { + return; + } - const apiClient = createApiClient(); - apiClient - .getTournament(id) - .then(({ tournament }) => setTournament(tournament)) - .catch(() => setError("Failed to load tournament")) - .finally(() => setLoading(false)); - }, [id, isValidId]); + const apiClient = createApiClient(); + apiClient + .getTournament(id) + .then(({ tournament }) => setTournament(tournament)) + .catch(() => setError("Failed to load tournament")) + .finally(() => setLoading(false)); + }, [id, isValidId]); - if (loading) { - return ( -
-

Loading...

-
- ); - } + if (loading) { + return ( +
+

Loading...

+
+ ); + } - if (error || !tournament) { - return ( -
-

{error || "Failed to load tournament"}

-
- ); - } + if (error || !tournament) { + return ( +
+

{error || "Failed to load tournament"}

+
+ ); + } - return ( -
-
-

- {tournament.display_name} -

- -
-
- ); + return ( +
+
+

+ {tournament.display_name} +

+ +
+
+ ); } -- cgit v1.3.1