diff options
| author | nsfisis <nsfisis@gmail.com> | 2024-08-01 22:22:12 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2024-08-01 22:22:12 +0900 |
| commit | 8f2cceacc8fde328033de7f05bb12e7b1246dd86 (patch) | |
| tree | acb93a52454315c286b9e0f3c624ae1bbcfa32f5 | |
| parent | 00e50b2dcfed209669c46da54dc07905d65887b8 (diff) | |
| download | iosdc-japan-2024-albatross-8f2cceacc8fde328033de7f05bb12e7b1246dd86.tar.gz iosdc-japan-2024-albatross-8f2cceacc8fde328033de7f05bb12e7b1246dd86.tar.zst iosdc-japan-2024-albatross-8f2cceacc8fde328033de7f05bb12e7b1246dd86.zip | |
chore(frontend): [biome] format
35 files changed, 1752 insertions, 1752 deletions
diff --git a/frontend/.eslintrc.cjs b/frontend/.eslintrc.cjs index b87e395..aa810b3 100644 --- a/frontend/.eslintrc.cjs +++ b/frontend/.eslintrc.cjs @@ -1,78 +1,78 @@ /** @type {import('eslint').Linter.Config} */ module.exports = { - root: true, - parserOptions: { - ecmaVersion: "latest", - sourceType: "module", - ecmaFeatures: { - jsx: true, - }, - }, - env: { - browser: true, - commonjs: true, - es6: true, - }, - ignorePatterns: ["!**/.server", "!**/.client"], + root: true, + parserOptions: { + ecmaVersion: "latest", + sourceType: "module", + ecmaFeatures: { + jsx: true, + }, + }, + env: { + browser: true, + commonjs: true, + es6: true, + }, + ignorePatterns: ["!**/.server", "!**/.client"], - // Base config - extends: ["eslint:recommended"], + // Base config + extends: ["eslint:recommended"], - overrides: [ - // React - { - files: ["**/*.{js,jsx,ts,tsx}"], - plugins: ["react", "jsx-a11y"], - extends: [ - "plugin:react/recommended", - "plugin:react/jsx-runtime", - "plugin:react-hooks/recommended", - "plugin:jsx-a11y/recommended", - ], - settings: { - react: { - version: "detect", - }, - formComponents: ["Form"], - linkComponents: [ - { name: "Link", linkAttribute: "to" }, - { name: "NavLink", linkAttribute: "to" }, - ], - "import/resolver": { - typescript: {}, - }, - }, - }, + overrides: [ + // React + { + files: ["**/*.{js,jsx,ts,tsx}"], + plugins: ["react", "jsx-a11y"], + extends: [ + "plugin:react/recommended", + "plugin:react/jsx-runtime", + "plugin:react-hooks/recommended", + "plugin:jsx-a11y/recommended", + ], + settings: { + react: { + version: "detect", + }, + formComponents: ["Form"], + linkComponents: [ + { name: "Link", linkAttribute: "to" }, + { name: "NavLink", linkAttribute: "to" }, + ], + "import/resolver": { + typescript: {}, + }, + }, + }, - // Typescript - { - files: ["**/*.{ts,tsx}"], - plugins: ["@typescript-eslint", "import"], - parser: "@typescript-eslint/parser", - settings: { - "import/internal-regex": "^~/", - "import/resolver": { - node: { - extensions: [".ts", ".tsx"], - }, - typescript: { - alwaysTryTypes: true, - }, - }, - }, - extends: [ - "plugin:@typescript-eslint/recommended", - "plugin:import/recommended", - "plugin:import/typescript", - ], - }, + // Typescript + { + files: ["**/*.{ts,tsx}"], + plugins: ["@typescript-eslint", "import"], + parser: "@typescript-eslint/parser", + settings: { + "import/internal-regex": "^~/", + "import/resolver": { + node: { + extensions: [".ts", ".tsx"], + }, + typescript: { + alwaysTryTypes: true, + }, + }, + }, + extends: [ + "plugin:@typescript-eslint/recommended", + "plugin:import/recommended", + "plugin:import/typescript", + ], + }, - // Node - { - files: [".eslintrc.cjs"], - env: { - node: true, - }, - }, - ], + // Node + { + files: [".eslintrc.cjs"], + env: { + node: true, + }, + }, + ], }; diff --git a/frontend/app/.server/api/client.ts b/frontend/app/.server/api/client.ts index b3c7e9c..a78180b 100644 --- a/frontend/app/.server/api/client.ts +++ b/frontend/app/.server/api/client.ts @@ -2,94 +2,94 @@ import createClient from "openapi-fetch"; import type { operations, paths } from "./schema"; const apiClient = createClient<paths>({ - baseUrl: - process.env.NODE_ENV === "development" - ? "http://localhost:8002/api/" - : "http://api-server/api/", + baseUrl: + process.env.NODE_ENV === "development" + ? "http://localhost:8002/api/" + : "http://api-server/api/", }); export async function apiPostLogin(username: string, password: string) { - const { data, error } = await apiClient.POST("/login", { - body: { username, password }, - }); - if (error) throw new Error(error.message); - return data; + const { data, error } = await apiClient.POST("/login", { + body: { username, password }, + }); + if (error) throw new Error(error.message); + return data; } export async function apiGetGames(token: string) { - const { data, error } = await apiClient.GET("/games", { - params: { - header: { Authorization: `Bearer ${token}` }, - }, - }); - if (error) throw new Error(error.message); - return data; + const { data, error } = await apiClient.GET("/games", { + params: { + header: { Authorization: `Bearer ${token}` }, + }, + }); + if (error) throw new Error(error.message); + return data; } export async function apiGetGame(token: string, gameId: number) { - const { data, error } = await apiClient.GET("/games/{game_id}", { - params: { - header: { Authorization: `Bearer ${token}` }, - path: { game_id: gameId }, - }, - }); - if (error) throw new Error(error.message); - return data; + const { data, error } = await apiClient.GET("/games/{game_id}", { + params: { + header: { Authorization: `Bearer ${token}` }, + path: { game_id: gameId }, + }, + }); + if (error) throw new Error(error.message); + return data; } export async function apiGetToken(token: string) { - const { data, error } = await apiClient.GET("/token", { - params: { - header: { Authorization: `Bearer ${token}` }, - }, - }); - if (error) throw new Error(error.message); - return data; + const { data, error } = await apiClient.GET("/token", { + params: { + header: { Authorization: `Bearer ${token}` }, + }, + }); + if (error) throw new Error(error.message); + return data; } export async function adminApiGetUsers(token: string) { - const { data, error } = await apiClient.GET("/admin/users", { - params: { - header: { Authorization: `Bearer ${token}` }, - }, - }); - if (error) throw new Error(error.message); - return data; + const { data, error } = await apiClient.GET("/admin/users", { + params: { + header: { Authorization: `Bearer ${token}` }, + }, + }); + if (error) throw new Error(error.message); + return data; } export async function adminApiGetGames(token: string) { - const { data, error } = await apiClient.GET("/admin/games", { - params: { - header: { Authorization: `Bearer ${token}` }, - }, - }); - if (error) throw new Error(error.message); - return data; + const { data, error } = await apiClient.GET("/admin/games", { + params: { + header: { Authorization: `Bearer ${token}` }, + }, + }); + if (error) throw new Error(error.message); + return data; } export async function adminApiGetGame(token: string, gameId: number) { - const { data, error } = await apiClient.GET("/admin/games/{game_id}", { - params: { - header: { Authorization: `Bearer ${token}` }, - path: { game_id: gameId }, - }, - }); - if (error) throw new Error(error.message); - return data; + const { data, error } = await apiClient.GET("/admin/games/{game_id}", { + params: { + header: { Authorization: `Bearer ${token}` }, + path: { game_id: gameId }, + }, + }); + if (error) throw new Error(error.message); + return data; } export async function adminApiPutGame( - token: string, - gameId: number, - body: operations["adminPutGame"]["requestBody"]["content"]["application/json"], + token: string, + gameId: number, + body: operations["adminPutGame"]["requestBody"]["content"]["application/json"], ) { - const { data, error } = await apiClient.PUT("/admin/games/{game_id}", { - params: { - header: { Authorization: `Bearer ${token}` }, - path: { game_id: gameId }, - }, - body, - }); - if (error) throw new Error(error.message); - return data; + const { data, error } = await apiClient.PUT("/admin/games/{game_id}", { + params: { + header: { Authorization: `Bearer ${token}` }, + path: { game_id: gameId }, + }, + body, + }); + if (error) throw new Error(error.message); + return data; } diff --git a/frontend/app/.server/auth.ts b/frontend/app/.server/auth.ts index 3d9a492..0c7742a 100644 --- a/frontend/app/.server/auth.ts +++ b/frontend/app/.server/auth.ts @@ -9,105 +9,105 @@ import { sessionStorage } from "./session"; export const authenticator = new Authenticator<string>(sessionStorage); async function login(username: string, password: string): Promise<string> { - return (await apiPostLogin(username, password)).token; + return (await apiPostLogin(username, password)).token; } authenticator.use( - new FormStrategy(async ({ form }) => { - const username = String(form.get("username")); - const password = String(form.get("password")); - return await login(username, password); - }), - "default", + new FormStrategy(async ({ form }) => { + const username = String(form.get("username")); + const password = String(form.get("password")); + return await login(username, password); + }), + "default", ); export type User = components["schemas"]["User"]; export async function isAuthenticated( - request: Request | Session, - options?: { - successRedirect?: never; - failureRedirect?: never; - headers?: never; - }, + request: Request | Session, + options?: { + successRedirect?: never; + failureRedirect?: never; + headers?: never; + }, ): Promise<{ user: User; token: string } | null>; export async function isAuthenticated( - request: Request | Session, - options: { - successRedirect: string; - failureRedirect?: never; - headers?: HeadersInit; - }, + request: Request | Session, + options: { + successRedirect: string; + failureRedirect?: never; + headers?: HeadersInit; + }, ): Promise<null>; export async function isAuthenticated( - request: Request | Session, - options: { - successRedirect?: never; - failureRedirect: string; - headers?: HeadersInit; - }, + request: Request | Session, + options: { + successRedirect?: never; + failureRedirect: string; + headers?: HeadersInit; + }, ): Promise<{ user: User; token: string }>; export async function isAuthenticated( - request: Request | Session, - options: { - successRedirect: string; - failureRedirect: string; - headers?: HeadersInit; - }, + request: Request | Session, + options: { + successRedirect: string; + failureRedirect: string; + headers?: HeadersInit; + }, ): Promise<null>; export async function isAuthenticated( - request: Request | Session, - options: - | { - successRedirect?: never; - failureRedirect?: never; - headers?: never; - } - | { - successRedirect: string; - failureRedirect?: never; - headers?: HeadersInit; - } - | { - successRedirect?: never; - failureRedirect: string; - headers?: HeadersInit; - } - | { - successRedirect: string; - failureRedirect: string; - headers?: HeadersInit; - } = {}, + request: Request | Session, + options: + | { + successRedirect?: never; + failureRedirect?: never; + headers?: never; + } + | { + successRedirect: string; + failureRedirect?: never; + headers?: HeadersInit; + } + | { + successRedirect?: never; + failureRedirect: string; + headers?: HeadersInit; + } + | { + successRedirect: string; + failureRedirect: string; + headers?: HeadersInit; + } = {}, ): Promise<{ user: User; token: string } | null> { - // This function's signature should be compatible with `authenticator.isAuthenticated` but TypeScript does not infer it correctly. - let jwt; - const { successRedirect, failureRedirect, headers } = options; - if (successRedirect && failureRedirect) { - jwt = await authenticator.isAuthenticated(request, { - successRedirect, - failureRedirect, - headers, - }); - } else if (!successRedirect && failureRedirect) { - jwt = await authenticator.isAuthenticated(request, { - failureRedirect, - headers, - }); - } else if (successRedirect && !failureRedirect) { - jwt = await authenticator.isAuthenticated(request, { - successRedirect, - headers, - }); - } else { - jwt = await authenticator.isAuthenticated(request); - } + // This function's signature should be compatible with `authenticator.isAuthenticated` but TypeScript does not infer it correctly. + let jwt; + const { successRedirect, failureRedirect, headers } = options; + if (successRedirect && failureRedirect) { + jwt = await authenticator.isAuthenticated(request, { + successRedirect, + failureRedirect, + headers, + }); + } else if (!successRedirect && failureRedirect) { + jwt = await authenticator.isAuthenticated(request, { + failureRedirect, + headers, + }); + } else if (successRedirect && !failureRedirect) { + jwt = await authenticator.isAuthenticated(request, { + successRedirect, + headers, + }); + } else { + jwt = await authenticator.isAuthenticated(request); + } - if (!jwt) { - return null; - } - const user = jwtDecode<User>(jwt); - return { - user, - token: jwt, - }; + if (!jwt) { + return null; + } + const user = jwtDecode<User>(jwt); + return { + user, + token: jwt, + }; } diff --git a/frontend/app/.server/session.ts b/frontend/app/.server/session.ts index 2000853..79810f4 100644 --- a/frontend/app/.server/session.ts +++ b/frontend/app/.server/session.ts @@ -1,13 +1,13 @@ import { createCookieSessionStorage } from "@remix-run/node"; export const sessionStorage = createCookieSessionStorage({ - cookie: { - name: "albatross_session", - sameSite: "lax", - path: "/", - httpOnly: true, - secrets: ["TODO"], - // secure: process.env.NODE_ENV === "production", - secure: false, // TODO - }, + cookie: { + name: "albatross_session", + sameSite: "lax", + path: "/", + httpOnly: true, + secrets: ["TODO"], + // secure: process.env.NODE_ENV === "production", + secure: false, // TODO + }, }); diff --git a/frontend/app/components/GolfPlayApp.client.tsx b/frontend/app/components/GolfPlayApp.client.tsx index 0ab8fff..ace0710 100644 --- a/frontend/app/components/GolfPlayApp.client.tsx +++ b/frontend/app/components/GolfPlayApp.client.tsx @@ -16,136 +16,136 @@ type Problem = components["schemas"]["Problem"]; type GameState = "connecting" | "waiting" | "starting" | "gaming" | "finished"; export default function GolfPlayApp({ - game, - sockToken, + game, + sockToken, }: { - game: Game; - sockToken: string; + game: Game; + sockToken: string; }) { - // const socketUrl = `wss://t.nil.ninja/iosdc-japan/2024/sock/golf/${game.game_id}/play?token=${sockToken}`; - const socketUrl = - process.env.NODE_ENV === "development" - ? `ws://localhost:8002/sock/golf/${game.game_id}/play?token=${sockToken}` - : `ws://api-server/sock/golf/${game.game_id}/play?token=${sockToken}`; - - const { sendJsonMessage, lastJsonMessage, readyState } = - useWebSocket<WebSocketMessage>(socketUrl, {}); - - const [gameState, setGameState] = useState<GameState>("connecting"); - - const [problem, setProblem] = useState<Problem | null>(null); - - const [startedAt, setStartedAt] = useState<number | null>(null); - - const [timeLeftSeconds, setTimeLeftSeconds] = useState<number | null>(null); - - useEffect(() => { - if (gameState === "starting" && startedAt !== null) { - const timer1 = setInterval(() => { - setTimeLeftSeconds((prev) => { - if (prev === null) { - return null; - } - if (prev <= 1) { - clearInterval(timer1); - setGameState("gaming"); - return 0; - } - return prev - 1; - }); - }, 1000); - - const timer2 = setInterval(() => { - const nowSec = Math.floor(Date.now() / 1000); - const finishedAt = startedAt + game.duration_seconds; - if (nowSec >= finishedAt) { - clearInterval(timer2); - setGameState("finished"); - } - }, 1000); - - return () => { - clearInterval(timer1); - clearInterval(timer2); - }; - } - }, [gameState, startedAt, game.duration_seconds]); - - const [currentScore, setCurrentScore] = useState<number | null>(null); - - const onCodeChange = useDebouncedCallback((code: string) => { - console.log("player:c2s:code"); - sendJsonMessage({ - type: "player:c2s:code", - data: { code }, - }); - }, 1000); - - if (readyState === ReadyState.UNINSTANTIATED) { - throw new Error("WebSocket is not connected"); - } - - useEffect(() => { - if (readyState === ReadyState.CLOSING || readyState === ReadyState.CLOSED) { - if (gameState !== "finished") { - setGameState("connecting"); - } - } else if (readyState === ReadyState.CONNECTING) { - setGameState("connecting"); - } else if (readyState === ReadyState.OPEN) { - if (lastJsonMessage !== null) { - console.log(lastJsonMessage.type); - if (lastJsonMessage.type === "player:s2c:prepare") { - const { problem } = lastJsonMessage.data; - setProblem(problem); - console.log("player:c2s:ready"); - sendJsonMessage({ type: "player:c2s:ready" }); - } else if (lastJsonMessage.type === "player:s2c:start") { - if ( - gameState !== "starting" && - gameState !== "gaming" && - gameState !== "finished" - ) { - const { start_at } = lastJsonMessage.data; - setStartedAt(start_at); - const nowSec = Math.floor(Date.now() / 1000); - setTimeLeftSeconds(start_at - nowSec); - setGameState("starting"); - } - } else if (lastJsonMessage.type === "player:s2c:execresult") { - const { score } = lastJsonMessage.data; - if ( - score !== null && - (currentScore === null || score < currentScore) - ) { - setCurrentScore(score); - } - } - } else { - setGameState("waiting"); - console.log("player:c2s:entry"); - sendJsonMessage({ type: "player:c2s:entry" }); - } - } - }, [sendJsonMessage, lastJsonMessage, readyState, gameState, currentScore]); - - if (gameState === "connecting") { - return <GolfPlayAppConnecting />; - } else if (gameState === "waiting") { - return <GolfPlayAppWaiting />; - } else if (gameState === "starting") { - return <GolfPlayAppStarting timeLeft={timeLeftSeconds!} />; - } else if (gameState === "gaming") { - return ( - <GolfPlayAppGaming - problem={problem!.description} - onCodeChange={onCodeChange} - currentScore={currentScore} - /> - ); - } else if (gameState === "finished") { - return <GolfPlayAppFinished />; - } else { - return null; - } + // const socketUrl = `wss://t.nil.ninja/iosdc-japan/2024/sock/golf/${game.game_id}/play?token=${sockToken}`; + const socketUrl = + process.env.NODE_ENV === "development" + ? `ws://localhost:8002/sock/golf/${game.game_id}/play?token=${sockToken}` + : `ws://api-server/sock/golf/${game.game_id}/play?token=${sockToken}`; + + const { sendJsonMessage, lastJsonMessage, readyState } = + useWebSocket<WebSocketMessage>(socketUrl, {}); + + const [gameState, setGameState] = useState<GameState>("connecting"); + + const [problem, setProblem] = useState<Problem | null>(null); + + const [startedAt, setStartedAt] = useState<number | null>(null); + + const [timeLeftSeconds, setTimeLeftSeconds] = useState<number | null>(null); + + useEffect(() => { + if (gameState === "starting" && startedAt !== null) { + const timer1 = setInterval(() => { + setTimeLeftSeconds((prev) => { + if (prev === null) { + return null; + } + if (prev <= 1) { + clearInterval(timer1); + setGameState("gaming"); + return 0; + } + return prev - 1; + }); + }, 1000); + + const timer2 = setInterval(() => { + const nowSec = Math.floor(Date.now() / 1000); + const finishedAt = startedAt + game.duration_seconds; + if (nowSec >= finishedAt) { + clearInterval(timer2); + setGameState("finished"); + } + }, 1000); + + return () => { + clearInterval(timer1); + clearInterval(timer2); + }; + } + }, [gameState, startedAt, game.duration_seconds]); + + const [currentScore, setCurrentScore] = useState<number | null>(null); + + const onCodeChange = useDebouncedCallback((code: string) => { + console.log("player:c2s:code"); + sendJsonMessage({ + type: "player:c2s:code", + data: { code }, + }); + }, 1000); + + if (readyState === ReadyState.UNINSTANTIATED) { + throw new Error("WebSocket is not connected"); + } + + useEffect(() => { + if (readyState === ReadyState.CLOSING || readyState === ReadyState.CLOSED) { + if (gameState !== "finished") { + setGameState("connecting"); + } + } else if (readyState === ReadyState.CONNECTING) { + setGameState("connecting"); + } else if (readyState === ReadyState.OPEN) { + if (lastJsonMessage !== null) { + console.log(lastJsonMessage.type); + if (lastJsonMessage.type === "player:s2c:prepare") { + const { problem } = lastJsonMessage.data; + setProblem(problem); + console.log("player:c2s:ready"); + sendJsonMessage({ type: "player:c2s:ready" }); + } else if (lastJsonMessage.type === "player:s2c:start") { + if ( + gameState !== "starting" && + gameState !== "gaming" && + gameState !== "finished" + ) { + const { start_at } = lastJsonMessage.data; + setStartedAt(start_at); + const nowSec = Math.floor(Date.now() / 1000); + setTimeLeftSeconds(start_at - nowSec); + setGameState("starting"); + } + } else if (lastJsonMessage.type === "player:s2c:execresult") { + const { score } = lastJsonMessage.data; + if ( + score !== null && + (currentScore === null || score < currentScore) + ) { + setCurrentScore(score); + } + } + } else { + setGameState("waiting"); + console.log("player:c2s:entry"); + sendJsonMessage({ type: "player:c2s:entry" }); + } + } + }, [sendJsonMessage, lastJsonMessage, readyState, gameState, currentScore]); + + if (gameState === "connecting") { + return <GolfPlayAppConnecting />; + } else if (gameState === "waiting") { + return <GolfPlayAppWaiting />; + } else if (gameState === "starting") { + return <GolfPlayAppStarting timeLeft={timeLeftSeconds!} />; + } else if (gameState === "gaming") { + return ( + <GolfPlayAppGaming + problem={problem!.description} + onCodeChange={onCodeChange} + currentScore={currentScore} + /> + ); + } else if (gameState === "finished") { + return <GolfPlayAppFinished />; + } else { + return null; + } } diff --git a/frontend/app/components/GolfPlayApps/GolfPlayAppConnecting.tsx b/frontend/app/components/GolfPlayApps/GolfPlayAppConnecting.tsx index 6a954c1..d97131b 100644 --- a/frontend/app/components/GolfPlayApps/GolfPlayAppConnecting.tsx +++ b/frontend/app/components/GolfPlayApps/GolfPlayAppConnecting.tsx @@ -1,11 +1,11 @@ export default function GolfPlayAppConnecting() { - return ( - <div className="min-h-screen bg-gray-100 flex items-center justify-center"> - <div className="text-center"> - <h1 className="text-4xl font-bold text-black-600 mb-4"> - Connecting... - </h1> - </div> - </div> - ); + return ( + <div className="min-h-screen bg-gray-100 flex items-center justify-center"> + <div className="text-center"> + <h1 className="text-4xl font-bold text-black-600 mb-4"> + Connecting... + </h1> + </div> + </div> + ); } diff --git a/frontend/app/components/GolfPlayApps/GolfPlayAppFinished.tsx b/frontend/app/components/GolfPlayApps/GolfPlayAppFinished.tsx index db06890..c3ef2d4 100644 --- a/frontend/app/components/GolfPlayApps/GolfPlayAppFinished.tsx +++ b/frontend/app/components/GolfPlayApps/GolfPlayAppFinished.tsx @@ -1,9 +1,9 @@ export default function GolfPlayAppFinished() { - return ( - <div className="min-h-screen bg-gray-100 flex items-center justify-center"> - <div className="text-center"> - <h1 className="text-4xl font-bold text-black-600 mb-4">Finished</h1> - </div> - </div> - ); + return ( + <div className="min-h-screen bg-gray-100 flex items-center justify-center"> + <div className="text-center"> + <h1 className="text-4xl font-bold text-black-600 mb-4">Finished</h1> + </div> + </div> + ); } diff --git a/frontend/app/components/GolfPlayApps/GolfPlayAppGaming.tsx b/frontend/app/components/GolfPlayApps/GolfPlayAppGaming.tsx index fc672f5..75ab18e 100644 --- a/frontend/app/components/GolfPlayApps/GolfPlayAppGaming.tsx +++ b/frontend/app/components/GolfPlayApps/GolfPlayAppGaming.tsx @@ -1,41 +1,41 @@ type Props = { - problem: string; - onCodeChange: (code: string) => void; - currentScore: number | null; + problem: string; + onCodeChange: (code: string) => void; + currentScore: number | null; }; export default function GolfPlayAppGaming({ - problem, - onCodeChange, - currentScore, + problem, + onCodeChange, + currentScore, }: Props) { - const handleTextChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => { - onCodeChange(e.target.value); - }; + const handleTextChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => { + onCodeChange(e.target.value); + }; - return ( - <div className="min-h-screen flex"> - <div className="mx-auto flex min-h-full flex-grow"> - <div className="flex w-1/2 flex-col justify-between p-4"> - <div> - <div className="mb-2 text-xl font-bold">TODO</div> - <div className="text-gray-700">{problem}</div> - </div> - <div className="mb-4 mt-auto"> - <div className="font-semibold text-green-500"> - Score: {currentScore == null ? "-" : `${currentScore}`} - </div> - </div> - </div> - <div className="w-1/2 p-4 flex"> - <div className="flex-grow"> - <textarea - className="h-full w-full rounded-lg border border-gray-300 p-2 focus:outline-none focus:ring-2 focus:ring-blue-500" - onChange={handleTextChange} - ></textarea> - </div> - </div> - </div> - </div> - ); + return ( + <div className="min-h-screen flex"> + <div className="mx-auto flex min-h-full flex-grow"> + <div className="flex w-1/2 flex-col justify-between p-4"> + <div> + <div className="mb-2 text-xl font-bold">TODO</div> + <div className="text-gray-700">{problem}</div> + </div> + <div className="mb-4 mt-auto"> + <div className="font-semibold text-green-500"> + Score: {currentScore == null ? "-" : `${currentScore}`} + </div> + </div> + </div> + <div className="w-1/2 p-4 flex"> + <div className="flex-grow"> + <textarea + className="h-full w-full rounded-lg border border-gray-300 p-2 focus:outline-none focus:ring-2 focus:ring-blue-500" + onChange={handleTextChange} + ></textarea> + </div> + </div> + </div> + </div> + ); } diff --git a/frontend/app/components/GolfPlayApps/GolfPlayAppStarting.tsx b/frontend/app/components/GolfPlayApps/GolfPlayAppStarting.tsx index df179f1..0d60fc9 100644 --- a/frontend/app/components/GolfPlayApps/GolfPlayAppStarting.tsx +++ b/frontend/app/components/GolfPlayApps/GolfPlayAppStarting.tsx @@ -1,15 +1,15 @@ type Props = { - timeLeft: number; + timeLeft: number; }; export default function GolfPlayAppStarting({ timeLeft }: Props) { - return ( - <div className="min-h-screen bg-gray-100 flex items-center justify-center"> - <div className="text-center"> - <h1 className="text-4xl font-bold text-black-600 mb-4"> - Starting... ({timeLeft} s) - </h1> - </div> - </div> - ); + return ( + <div className="min-h-screen bg-gray-100 flex items-center justify-center"> + <div className="text-center"> + <h1 className="text-4xl font-bold text-black-600 mb-4"> + Starting... ({timeLeft} s) + </h1> + </div> + </div> + ); } diff --git a/frontend/app/components/GolfPlayApps/GolfPlayAppWaiting.tsx b/frontend/app/components/GolfPlayApps/GolfPlayAppWaiting.tsx index f4fc6c5..a1da33e 100644 --- a/frontend/app/components/GolfPlayApps/GolfPlayAppWaiting.tsx +++ b/frontend/app/components/GolfPlayApps/GolfPlayAppWaiting.tsx @@ -1,584 +1,584 @@ export default function GolfPlayAppWaiting() { - return ( - <> - <div className="min-h-screen bg-gray-100 flex items-center justify-center"> - <div className="text-center"> - <h1 className="text-4xl font-bold text-black-600 mb-4">Waiting...</h1> - </div> - </div> - <style> - {` + return ( + <> + <div className="min-h-screen bg-gray-100 flex items-center justify-center"> + <div className="text-center"> + <h1 className="text-4xl font-bold text-black-600 mb-4">Waiting...</h1> + </div> + </div> + <style> + {` @keyframes changeHeight { 0% { height: 20%; } 50% { height: 100%; } 100% { height: 20%; } } `} - </style> - <div - style={{ - position: "fixed", - bottom: 0, - width: "100%", - display: "flex", - justifyContent: "center", - alignItems: "flex-end", - height: "100px", - margin: "0 2px", - }} - > - <div - style={{ - width: "2%", - margin: "0 2px", - background: - "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", - display: "inline-block", - animation: "changeHeight 1s infinite ease-in-out", - animationDelay: "2.0s", - }} - ></div> - <div - style={{ - width: "2%", - margin: "0 2px", - background: - "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", - display: "inline-block", - animation: "changeHeight 1s infinite ease-in-out", - animationDelay: "1.9s", - }} - ></div> - <div - style={{ - width: "2%", - margin: "0 2px", - background: - "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", - display: "inline-block", - animation: "changeHeight 1s infinite ease-in-out", - animationDelay: "1.8s", - }} - ></div> - <div - style={{ - width: "2%", - margin: "0 2px", - background: - "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", - display: "inline-block", - animation: "changeHeight 1s infinite ease-in-out", - animationDelay: "1.7s", - }} - ></div> - <div - style={{ - width: "2%", - margin: "0 2px", - background: - "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", - display: "inline-block", - animation: "changeHeight 1s infinite ease-in-out", - animationDelay: "1.6s", - }} - ></div> - <div - style={{ - width: "2%", - margin: "0 2px", - background: - "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", - display: "inline-block", - animation: "changeHeight 1s infinite ease-in-out", - animationDelay: "1.5s", - }} - ></div> - <div - style={{ - width: "2%", - margin: "0 2px", - background: - "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", - display: "inline-block", - animation: "changeHeight 1s infinite ease-in-out", - animationDelay: "1.4s", - }} - ></div> - <div - style={{ - width: "2%", - margin: "0 2px", - background: - "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", - display: "inline-block", - animation: "changeHeight 1s infinite ease-in-out", - animationDelay: "1.3s", - }} - ></div> - <div - style={{ - width: "2%", - margin: "0 2px", - background: - "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", - display: "inline-block", - animation: "changeHeight 1s infinite ease-in-out", - animationDelay: "1.2s", - }} - ></div> - <div - style={{ - width: "2%", - margin: "0 2px", - background: - "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", - display: "inline-block", - animation: "changeHeight 1s infinite ease-in-out", - animationDelay: "1.1s", - }} - ></div> - <div - style={{ - width: "2%", - margin: "0 2px", - background: - "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", - display: "inline-block", - animation: "changeHeight 1s infinite ease-in-out", - animationDelay: "1.0s", - }} - ></div> - <div - style={{ - width: "2%", - margin: "0 2px", - background: - "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", - display: "inline-block", - animation: "changeHeight 1s infinite ease-in-out", - animationDelay: "0.9s", - }} - ></div> - <div - style={{ - width: "2%", - margin: "0 2px", - background: - "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", - display: "inline-block", - animation: "changeHeight 1s infinite ease-in-out", - animationDelay: "0.8s", - }} - ></div> - <div - style={{ - width: "2%", - margin: "0 2px", - background: - "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", - display: "inline-block", - animation: "changeHeight 1s infinite ease-in-out", - animationDelay: "0.7s", - }} - ></div> - <div - style={{ - width: "2%", - margin: "0 2px", - background: - "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", - display: "inline-block", - animation: "changeHeight 1s infinite ease-in-out", - animationDelay: "0.6s", - }} - ></div> - <div - style={{ - width: "2%", - margin: "0 2px", - background: - "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", - display: "inline-block", - animation: "changeHeight 1s infinite ease-in-out", - animationDelay: "0.5s", - }} - ></div> - <div - style={{ - width: "2%", - margin: "0 2px", - background: - "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", - display: "inline-block", - animation: "changeHeight 1s infinite ease-in-out", - animationDelay: "0.4s", - }} - ></div> - <div - style={{ - width: "2%", - margin: "0 2px", - background: - "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", - display: "inline-block", - animation: "changeHeight 1s infinite ease-in-out", - animationDelay: "0.3s", - }} - ></div> - <div - style={{ - width: "2%", - margin: "0 2px", - background: - "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", - display: "inline-block", - animation: "changeHeight 1s infinite ease-in-out", - animationDelay: "0.2s", - }} - ></div> - <div - style={{ - width: "2%", - margin: "0 2px", - background: - "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", - display: "inline-block", - animation: "changeHeight 1s infinite ease-in-out", - animationDelay: "0.1s", - }} - ></div> - <div - style={{ - width: "2%", - margin: "0 2px", - background: - "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", - display: "inline-block", - animation: "changeHeight 1s infinite ease-in-out", - animationDelay: "0.5s", - }} - ></div> - <div - style={{ - width: "2%", - margin: "0 2px", - background: - "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", - display: "inline-block", - animation: "changeHeight 1s infinite ease-in-out", - animationDelay: "0.4s", - }} - ></div> - <div - style={{ - width: "2%", - margin: "0 2px", - background: - "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", - display: "inline-block", - animation: "changeHeight 1s infinite ease-in-out", - animationDelay: "0.3s", - }} - ></div> - <div - style={{ - width: "2%", - margin: "0 2px", - background: - "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", - display: "inline-block", - animation: "changeHeight 1s infinite ease-in-out", - animationDelay: "0.2s", - }} - ></div> - <div - style={{ - width: "2%", - margin: "0 2px", - background: - "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", - display: "inline-block", - animation: "changeHeight 1s infinite ease-in-out", - animationDelay: "0.1s", - }} - ></div> + </style> + <div + style={{ + position: "fixed", + bottom: 0, + width: "100%", + display: "flex", + justifyContent: "center", + alignItems: "flex-end", + height: "100px", + margin: "0 2px", + }} + > + <div + style={{ + width: "2%", + margin: "0 2px", + background: + "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", + display: "inline-block", + animation: "changeHeight 1s infinite ease-in-out", + animationDelay: "2.0s", + }} + ></div> + <div + style={{ + width: "2%", + margin: "0 2px", + background: + "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", + display: "inline-block", + animation: "changeHeight 1s infinite ease-in-out", + animationDelay: "1.9s", + }} + ></div> + <div + style={{ + width: "2%", + margin: "0 2px", + background: + "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", + display: "inline-block", + animation: "changeHeight 1s infinite ease-in-out", + animationDelay: "1.8s", + }} + ></div> + <div + style={{ + width: "2%", + margin: "0 2px", + background: + "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", + display: "inline-block", + animation: "changeHeight 1s infinite ease-in-out", + animationDelay: "1.7s", + }} + ></div> + <div + style={{ + width: "2%", + margin: "0 2px", + background: + "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", + display: "inline-block", + animation: "changeHeight 1s infinite ease-in-out", + animationDelay: "1.6s", + }} + ></div> + <div + style={{ + width: "2%", + margin: "0 2px", + background: + "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", + display: "inline-block", + animation: "changeHeight 1s infinite ease-in-out", + animationDelay: "1.5s", + }} + ></div> + <div + style={{ + width: "2%", + margin: "0 2px", + background: + "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", + display: "inline-block", + animation: "changeHeight 1s infinite ease-in-out", + animationDelay: "1.4s", + }} + ></div> + <div + style={{ + width: "2%", + margin: "0 2px", + background: + "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", + display: "inline-block", + animation: "changeHeight 1s infinite ease-in-out", + animationDelay: "1.3s", + }} + ></div> + <div + style={{ + width: "2%", + margin: "0 2px", + background: + "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", + display: "inline-block", + animation: "changeHeight 1s infinite ease-in-out", + animationDelay: "1.2s", + }} + ></div> + <div + style={{ + width: "2%", + margin: "0 2px", + background: + "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", + display: "inline-block", + animation: "changeHeight 1s infinite ease-in-out", + animationDelay: "1.1s", + }} + ></div> + <div + style={{ + width: "2%", + margin: "0 2px", + background: + "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", + display: "inline-block", + animation: "changeHeight 1s infinite ease-in-out", + animationDelay: "1.0s", + }} + ></div> + <div + style={{ + width: "2%", + margin: "0 2px", + background: + "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", + display: "inline-block", + animation: "changeHeight 1s infinite ease-in-out", + animationDelay: "0.9s", + }} + ></div> + <div + style={{ + width: "2%", + margin: "0 2px", + background: + "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", + display: "inline-block", + animation: "changeHeight 1s infinite ease-in-out", + animationDelay: "0.8s", + }} + ></div> + <div + style={{ + width: "2%", + margin: "0 2px", + background: + "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", + display: "inline-block", + animation: "changeHeight 1s infinite ease-in-out", + animationDelay: "0.7s", + }} + ></div> + <div + style={{ + width: "2%", + margin: "0 2px", + background: + "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", + display: "inline-block", + animation: "changeHeight 1s infinite ease-in-out", + animationDelay: "0.6s", + }} + ></div> + <div + style={{ + width: "2%", + margin: "0 2px", + background: + "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", + display: "inline-block", + animation: "changeHeight 1s infinite ease-in-out", + animationDelay: "0.5s", + }} + ></div> + <div + style={{ + width: "2%", + margin: "0 2px", + background: + "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", + display: "inline-block", + animation: "changeHeight 1s infinite ease-in-out", + animationDelay: "0.4s", + }} + ></div> + <div + style={{ + width: "2%", + margin: "0 2px", + background: + "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", + display: "inline-block", + animation: "changeHeight 1s infinite ease-in-out", + animationDelay: "0.3s", + }} + ></div> + <div + style={{ + width: "2%", + margin: "0 2px", + background: + "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", + display: "inline-block", + animation: "changeHeight 1s infinite ease-in-out", + animationDelay: "0.2s", + }} + ></div> + <div + style={{ + width: "2%", + margin: "0 2px", + background: + "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", + display: "inline-block", + animation: "changeHeight 1s infinite ease-in-out", + animationDelay: "0.1s", + }} + ></div> + <div + style={{ + width: "2%", + margin: "0 2px", + background: + "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", + display: "inline-block", + animation: "changeHeight 1s infinite ease-in-out", + animationDelay: "0.5s", + }} + ></div> + <div + style={{ + width: "2%", + margin: "0 2px", + background: + "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", + display: "inline-block", + animation: "changeHeight 1s infinite ease-in-out", + animationDelay: "0.4s", + }} + ></div> + <div + style={{ + width: "2%", + margin: "0 2px", + background: + "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", + display: "inline-block", + animation: "changeHeight 1s infinite ease-in-out", + animationDelay: "0.3s", + }} + ></div> + <div + style={{ + width: "2%", + margin: "0 2px", + background: + "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", + display: "inline-block", + animation: "changeHeight 1s infinite ease-in-out", + animationDelay: "0.2s", + }} + ></div> + <div + style={{ + width: "2%", + margin: "0 2px", + background: + "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", + display: "inline-block", + animation: "changeHeight 1s infinite ease-in-out", + animationDelay: "0.1s", + }} + ></div> - <div - style={{ - width: "2%", - margin: "0 2px", - background: - "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", - display: "inline-block", - animation: "changeHeight 1s infinite ease-in-out", - animationDelay: "0.1s", - }} - ></div> - <div - style={{ - width: "2%", - margin: "0 2px", - background: - "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", - display: "inline-block", - animation: "changeHeight 1s infinite ease-in-out", - animationDelay: "0.2s", - }} - ></div> - <div - style={{ - width: "2%", - margin: "0 2px", - background: - "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", - display: "inline-block", - animation: "changeHeight 1s infinite ease-in-out", - animationDelay: "0.3s", - }} - ></div> - <div - style={{ - width: "2%", - margin: "0 2px", - background: - "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", - display: "inline-block", - animation: "changeHeight 1s infinite ease-in-out", - animationDelay: "0.4s", - }} - ></div> - <div - style={{ - width: "2%", - margin: "0 2px", - background: - "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", - display: "inline-block", - animation: "changeHeight 1s infinite ease-in-out", - animationDelay: "0.5s", - }} - ></div> - <div - style={{ - width: "2%", - margin: "0 2px", - background: - "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", - display: "inline-block", - animation: "changeHeight 1s infinite ease-in-out", - animationDelay: "0.1s", - }} - ></div> - <div - style={{ - width: "2%", - margin: "0 2px", - background: - "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", - display: "inline-block", - animation: "changeHeight 1s infinite ease-in-out", - animationDelay: "0.2s", - }} - ></div> - <div - style={{ - width: "2%", - margin: "0 2px", - background: - "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", - display: "inline-block", - animation: "changeHeight 1s infinite ease-in-out", - animationDelay: "0.3s", - }} - ></div> - <div - style={{ - width: "2%", - margin: "0 2px", - background: - "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", - display: "inline-block", - animation: "changeHeight 1s infinite ease-in-out", - animationDelay: "0.4s", - }} - ></div> - <div - style={{ - width: "2%", - margin: "0 2px", - background: - "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", - display: "inline-block", - animation: "changeHeight 1s infinite ease-in-out", - animationDelay: "0.5s", - }} - ></div> - <div - style={{ - width: "2%", - margin: "0 2px", - background: - "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", - display: "inline-block", - animation: "changeHeight 1s infinite ease-in-out", - animationDelay: "0.6s", - }} - ></div> - <div - style={{ - width: "2%", - margin: "0 2px", - background: - "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", - display: "inline-block", - animation: "changeHeight 1s infinite ease-in-out", - animationDelay: "0.7s", - }} - ></div> - <div - style={{ - width: "2%", - margin: "0 2px", - background: - "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", - display: "inline-block", - animation: "changeHeight 1s infinite ease-in-out", - animationDelay: "0.8s", - }} - ></div> - <div - style={{ - width: "2%", - margin: "0 2px", - background: - "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", - display: "inline-block", - animation: "changeHeight 1s infinite ease-in-out", - animationDelay: "0.9s", - }} - ></div> - <div - style={{ - width: "2%", - margin: "0 2px", - background: - "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", - display: "inline-block", - animation: "changeHeight 1s infinite ease-in-out", - animationDelay: "1.0s", - }} - ></div> - <div - style={{ - width: "2%", - margin: "0 2px", - background: - "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", - display: "inline-block", - animation: "changeHeight 1s infinite ease-in-out", - animationDelay: "1.1s", - }} - ></div> - <div - style={{ - width: "2%", - margin: "0 2px", - background: - "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", - display: "inline-block", - animation: "changeHeight 1s infinite ease-in-out", - animationDelay: "1.2s", - }} - ></div> - <div - style={{ - width: "2%", - margin: "0 2px", - background: - "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", - display: "inline-block", - animation: "changeHeight 1s infinite ease-in-out", - animationDelay: "1.3s", - }} - ></div> - <div - style={{ - width: "2%", - margin: "0 2px", - background: - "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", - display: "inline-block", - animation: "changeHeight 1s infinite ease-in-out", - animationDelay: "1.4s", - }} - ></div> - <div - style={{ - width: "2%", - margin: "0 2px", - background: - "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", - display: "inline-block", - animation: "changeHeight 1s infinite ease-in-out", - animationDelay: "1.5s", - }} - ></div> - <div - style={{ - width: "2%", - margin: "0 2px", - background: - "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", - display: "inline-block", - animation: "changeHeight 1s infinite ease-in-out", - animationDelay: "1.6s", - }} - ></div> - <div - style={{ - width: "2%", - margin: "0 2px", - background: - "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", - display: "inline-block", - animation: "changeHeight 1s infinite ease-in-out", - animationDelay: "1.7s", - }} - ></div> - <div - style={{ - width: "2%", - margin: "0 2px", - background: - "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", - display: "inline-block", - animation: "changeHeight 1s infinite ease-in-out", - animationDelay: "1.8s", - }} - ></div> - <div - style={{ - width: "2%", - margin: "0 2px", - background: - "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", - display: "inline-block", - animation: "changeHeight 1s infinite ease-in-out", - animationDelay: "1.9s", - }} - ></div> - <div - style={{ - width: "2%", - margin: "0 2px", - background: - "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", - display: "inline-block", - animation: "changeHeight 1s infinite ease-in-out", - animationDelay: "2.0s", - }} - ></div> - </div> - </> - ); + <div + style={{ + width: "2%", + margin: "0 2px", + background: + "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", + display: "inline-block", + animation: "changeHeight 1s infinite ease-in-out", + animationDelay: "0.1s", + }} + ></div> + <div + style={{ + width: "2%", + margin: "0 2px", + background: + "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", + display: "inline-block", + animation: "changeHeight 1s infinite ease-in-out", + animationDelay: "0.2s", + }} + ></div> + <div + style={{ + width: "2%", + margin: "0 2px", + background: + "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", + display: "inline-block", + animation: "changeHeight 1s infinite ease-in-out", + animationDelay: "0.3s", + }} + ></div> + <div + style={{ + width: "2%", + margin: "0 2px", + background: + "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", + display: "inline-block", + animation: "changeHeight 1s infinite ease-in-out", + animationDelay: "0.4s", + }} + ></div> + <div + style={{ + width: "2%", + margin: "0 2px", + background: + "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", + display: "inline-block", + animation: "changeHeight 1s infinite ease-in-out", + animationDelay: "0.5s", + }} + ></div> + <div + style={{ + width: "2%", + margin: "0 2px", + background: + "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", + display: "inline-block", + animation: "changeHeight 1s infinite ease-in-out", + animationDelay: "0.1s", + }} + ></div> + <div + style={{ + width: "2%", + margin: "0 2px", + background: + "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", + display: "inline-block", + animation: "changeHeight 1s infinite ease-in-out", + animationDelay: "0.2s", + }} + ></div> + <div + style={{ + width: "2%", + margin: "0 2px", + background: + "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", + display: "inline-block", + animation: "changeHeight 1s infinite ease-in-out", + animationDelay: "0.3s", + }} + ></div> + <div + style={{ + width: "2%", + margin: "0 2px", + background: + "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", + display: "inline-block", + animation: "changeHeight 1s infinite ease-in-out", + animationDelay: "0.4s", + }} + ></div> + <div + style={{ + width: "2%", + margin: "0 2px", + background: + "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", + display: "inline-block", + animation: "changeHeight 1s infinite ease-in-out", + animationDelay: "0.5s", + }} + ></div> + <div + style={{ + width: "2%", + margin: "0 2px", + background: + "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", + display: "inline-block", + animation: "changeHeight 1s infinite ease-in-out", + animationDelay: "0.6s", + }} + ></div> + <div + style={{ + width: "2%", + margin: "0 2px", + background: + "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", + display: "inline-block", + animation: "changeHeight 1s infinite ease-in-out", + animationDelay: "0.7s", + }} + ></div> + <div + style={{ + width: "2%", + margin: "0 2px", + background: + "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", + display: "inline-block", + animation: "changeHeight 1s infinite ease-in-out", + animationDelay: "0.8s", + }} + ></div> + <div + style={{ + width: "2%", + margin: "0 2px", + background: + "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", + display: "inline-block", + animation: "changeHeight 1s infinite ease-in-out", + animationDelay: "0.9s", + }} + ></div> + <div + style={{ + width: "2%", + margin: "0 2px", + background: + "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", + display: "inline-block", + animation: "changeHeight 1s infinite ease-in-out", + animationDelay: "1.0s", + }} + ></div> + <div + style={{ + width: "2%", + margin: "0 2px", + background: + "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", + display: "inline-block", + animation: "changeHeight 1s infinite ease-in-out", + animationDelay: "1.1s", + }} + ></div> + <div + style={{ + width: "2%", + margin: "0 2px", + background: + "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", + display: "inline-block", + animation: "changeHeight 1s infinite ease-in-out", + animationDelay: "1.2s", + }} + ></div> + <div + style={{ + width: "2%", + margin: "0 2px", + background: + "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", + display: "inline-block", + animation: "changeHeight 1s infinite ease-in-out", + animationDelay: "1.3s", + }} + ></div> + <div + style={{ + width: "2%", + margin: "0 2px", + background: + "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", + display: "inline-block", + animation: "changeHeight 1s infinite ease-in-out", + animationDelay: "1.4s", + }} + ></div> + <div + style={{ + width: "2%", + margin: "0 2px", + background: + "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", + display: "inline-block", + animation: "changeHeight 1s infinite ease-in-out", + animationDelay: "1.5s", + }} + ></div> + <div + style={{ + width: "2%", + margin: "0 2px", + background: + "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", + display: "inline-block", + animation: "changeHeight 1s infinite ease-in-out", + animationDelay: "1.6s", + }} + ></div> + <div + style={{ + width: "2%", + margin: "0 2px", + background: + "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", + display: "inline-block", + animation: "changeHeight 1s infinite ease-in-out", + animationDelay: "1.7s", + }} + ></div> + <div + style={{ + width: "2%", + margin: "0 2px", + background: + "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", + display: "inline-block", + animation: "changeHeight 1s infinite ease-in-out", + animationDelay: "1.8s", + }} + ></div> + <div + style={{ + width: "2%", + margin: "0 2px", + background: + "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", + display: "inline-block", + animation: "changeHeight 1s infinite ease-in-out", + animationDelay: "1.9s", + }} + ></div> + <div + style={{ + width: "2%", + margin: "0 2px", + background: + "linear-gradient(345deg, rgb(230, 36, 136) 0%, rgb(240, 184, 106) 100%)", + display: "inline-block", + animation: "changeHeight 1s infinite ease-in-out", + animationDelay: "2.0s", + }} + ></div> + </div> + </> + ); } diff --git a/frontend/app/components/GolfWatchApp.client.tsx b/frontend/app/components/GolfWatchApp.client.tsx index 0373944..aefe945 100644 --- a/frontend/app/components/GolfWatchApp.client.tsx +++ b/frontend/app/components/GolfWatchApp.client.tsx @@ -14,126 +14,126 @@ type Game = components["schemas"]["Game"]; type GameState = "connecting" | "waiting" | "starting" | "gaming" | "finished"; export default function GolfWatchApp({ - game, - sockToken, + game, + sockToken, }: { - game: Game; - sockToken: string; + game: Game; + sockToken: string; }) { - // const socketUrl = `wss://t.nil.ninja/iosdc-japan/2024/sock/golf/${game.game_id}/watch?token=${sockToken}`; - const socketUrl = - process.env.NODE_ENV === "development" - ? `ws://localhost:8002/sock/golf/${game.game_id}/watch?token=${sockToken}` - : `ws://api-server/sock/golf/${game.game_id}/watch?token=${sockToken}`; + // const socketUrl = `wss://t.nil.ninja/iosdc-japan/2024/sock/golf/${game.game_id}/watch?token=${sockToken}`; + const socketUrl = + process.env.NODE_ENV === "development" + ? `ws://localhost:8002/sock/golf/${game.game_id}/watch?token=${sockToken}` + : `ws://api-server/sock/golf/${game.game_id}/watch?token=${sockToken}`; - const { lastJsonMessage, readyState } = useWebSocket<WebSocketMessage>( - socketUrl, - {}, - ); + const { lastJsonMessage, readyState } = useWebSocket<WebSocketMessage>( + socketUrl, + {}, + ); - const [gameState, setGameState] = useState<GameState>("connecting"); + const [gameState, setGameState] = useState<GameState>("connecting"); - const [startedAt, setStartedAt] = useState<number | null>(null); + const [startedAt, setStartedAt] = useState<number | null>(null); - const [timeLeftSeconds, setTimeLeftSeconds] = useState<number | null>(null); + const [timeLeftSeconds, setTimeLeftSeconds] = useState<number | null>(null); - useEffect(() => { - if (gameState === "starting" && startedAt !== null) { - const timer1 = setInterval(() => { - setTimeLeftSeconds((prev) => { - if (prev === null) { - return null; - } - if (prev <= 1) { - clearInterval(timer1); - setGameState("gaming"); - return 0; - } - return prev - 1; - }); - }, 1000); + useEffect(() => { + if (gameState === "starting" && startedAt !== null) { + const timer1 = setInterval(() => { + setTimeLeftSeconds((prev) => { + if (prev === null) { + return null; + } + if (prev <= 1) { + clearInterval(timer1); + setGameState("gaming"); + return 0; + } + return prev - 1; + }); + }, 1000); - const timer2 = setInterval(() => { - const nowSec = Math.floor(Date.now() / 1000); - const finishedAt = startedAt + game.duration_seconds; - if (nowSec >= finishedAt) { - clearInterval(timer2); - setGameState("finished"); - } - }, 1000); + const timer2 = setInterval(() => { + const nowSec = Math.floor(Date.now() / 1000); + const finishedAt = startedAt + game.duration_seconds; + if (nowSec >= finishedAt) { + clearInterval(timer2); + setGameState("finished"); + } + }, 1000); - return () => { - clearInterval(timer1); - clearInterval(timer2); - }; - } - }, [gameState, startedAt, game.duration_seconds]); + return () => { + clearInterval(timer1); + clearInterval(timer2); + }; + } + }, [gameState, startedAt, game.duration_seconds]); - const [scoreA, setScoreA] = useState<number | null>(null); - const [scoreB, setScoreB] = useState<number | null>(null); - const [codeA, setCodeA] = useState<string>(""); - const [codeB, setCodeB] = useState<string>(""); + const [scoreA, setScoreA] = useState<number | null>(null); + const [scoreB, setScoreB] = useState<number | null>(null); + const [codeA, setCodeA] = useState<string>(""); + const [codeB, setCodeB] = useState<string>(""); - if (readyState === ReadyState.UNINSTANTIATED) { - throw new Error("WebSocket is not connected"); - } + if (readyState === ReadyState.UNINSTANTIATED) { + throw new Error("WebSocket is not connected"); + } - useEffect(() => { - if (readyState === ReadyState.CLOSING || readyState === ReadyState.CLOSED) { - if (gameState !== "finished") { - setGameState("connecting"); - } - } else if (readyState === ReadyState.CONNECTING) { - setGameState("connecting"); - } else if (readyState === ReadyState.OPEN) { - if (lastJsonMessage !== null) { - console.log(lastJsonMessage.type); - if (lastJsonMessage.type === "watcher:s2c:start") { - if ( - gameState !== "starting" && - gameState !== "gaming" && - gameState !== "finished" - ) { - const { start_at } = lastJsonMessage.data; - setStartedAt(start_at); - const nowSec = Math.floor(Date.now() / 1000); - setTimeLeftSeconds(start_at - nowSec); - setGameState("starting"); - } - } else if (lastJsonMessage.type === "watcher:s2c:code") { - const { player_id, code } = lastJsonMessage.data; - setCodeA(code); - } else if (lastJsonMessage.type === "watcher:s2c:execresult") { - const { score } = lastJsonMessage.data; - if (score !== null && (scoreA === null || score < scoreA)) { - setScoreA(score); - } - } - } else { - setGameState("waiting"); - } - } - }, [lastJsonMessage, readyState, gameState, scoreA]); + useEffect(() => { + if (readyState === ReadyState.CLOSING || readyState === ReadyState.CLOSED) { + if (gameState !== "finished") { + setGameState("connecting"); + } + } else if (readyState === ReadyState.CONNECTING) { + setGameState("connecting"); + } else if (readyState === ReadyState.OPEN) { + if (lastJsonMessage !== null) { + console.log(lastJsonMessage.type); + if (lastJsonMessage.type === "watcher:s2c:start") { + if ( + gameState !== "starting" && + gameState !== "gaming" && + gameState !== "finished" + ) { + const { start_at } = lastJsonMessage.data; + setStartedAt(start_at); + const nowSec = Math.floor(Date.now() / 1000); + setTimeLeftSeconds(start_at - nowSec); + setGameState("starting"); + } + } else if (lastJsonMessage.type === "watcher:s2c:code") { + const { player_id, code } = lastJsonMessage.data; + setCodeA(code); + } else if (lastJsonMessage.type === "watcher:s2c:execresult") { + const { score } = lastJsonMessage.data; + if (score !== null && (scoreA === null || score < scoreA)) { + setScoreA(score); + } + } + } else { + setGameState("waiting"); + } + } + }, [lastJsonMessage, readyState, gameState, scoreA]); - if (gameState === "connecting") { - return <GolfWatchAppConnecting />; - } else if (gameState === "waiting") { - return <GolfWatchAppWaiting />; - } else if (gameState === "starting") { - return <GolfWatchAppStarting timeLeft={timeLeftSeconds!} />; - } else if (gameState === "gaming") { - return ( - <GolfWatchAppGaming - problem={game.problem!.description} - codeA={codeA} - scoreA={scoreA} - codeB={codeB} - scoreB={scoreB} - /> - ); - } else if (gameState === "finished") { - return <GolfWatchAppFinished />; - } else { - return null; - } + if (gameState === "connecting") { + return <GolfWatchAppConnecting />; + } else if (gameState === "waiting") { + return <GolfWatchAppWaiting />; + } else if (gameState === "starting") { + return <GolfWatchAppStarting timeLeft={timeLeftSeconds!} />; + } else if (gameState === "gaming") { + return ( + <GolfWatchAppGaming + problem={game.problem!.description} + codeA={codeA} + scoreA={scoreA} + codeB={codeB} + scoreB={scoreB} + /> + ); + } else if (gameState === "finished") { + return <GolfWatchAppFinished />; + } else { + return null; + } } diff --git a/frontend/app/components/GolfWatchApps/GolfWatchAppConnecting.tsx b/frontend/app/components/GolfWatchApps/GolfWatchAppConnecting.tsx index b91339a..3704656 100644 --- a/frontend/app/components/GolfWatchApps/GolfWatchAppConnecting.tsx +++ b/frontend/app/components/GolfWatchApps/GolfWatchAppConnecting.tsx @@ -1,11 +1,11 @@ export default function GolfWatchAppConnecting() { - return ( - <div className="min-h-screen bg-gray-100 flex items-center justify-center"> - <div className="text-center"> - <h1 className="text-4xl font-bold text-black-600 mb-4"> - Connecting... - </h1> - </div> - </div> - ); + return ( + <div className="min-h-screen bg-gray-100 flex items-center justify-center"> + <div className="text-center"> + <h1 className="text-4xl font-bold text-black-600 mb-4"> + Connecting... + </h1> + </div> + </div> + ); } diff --git a/frontend/app/components/GolfWatchApps/GolfWatchAppFinished.tsx b/frontend/app/components/GolfWatchApps/GolfWatchAppFinished.tsx index 866e18a..58cb2df 100644 --- a/frontend/app/components/GolfWatchApps/GolfWatchAppFinished.tsx +++ b/frontend/app/components/GolfWatchApps/GolfWatchAppFinished.tsx @@ -1,9 +1,9 @@ export default function GolfWatchAppFinished() { - return ( - <div className="min-h-screen bg-gray-100 flex items-center justify-center"> - <div className="text-center"> - <h1 className="text-4xl font-bold text-black-600 mb-4">Finished</h1> - </div> - </div> - ); + return ( + <div className="min-h-screen bg-gray-100 flex items-center justify-center"> + <div className="text-center"> + <h1 className="text-4xl font-bold text-black-600 mb-4">Finished</h1> + </div> + </div> + ); } diff --git a/frontend/app/components/GolfWatchApps/GolfWatchAppGaming.tsx b/frontend/app/components/GolfWatchApps/GolfWatchAppGaming.tsx index 10d68fe..22277f8 100644 --- a/frontend/app/components/GolfWatchApps/GolfWatchAppGaming.tsx +++ b/frontend/app/components/GolfWatchApps/GolfWatchAppGaming.tsx @@ -1,41 +1,41 @@ type Props = { - problem: string; - codeA: string; - scoreA: number | null; - codeB: string; - scoreB: number | null; + problem: string; + codeA: string; + scoreA: number | null; + codeB: string; + scoreB: number | null; }; export default function GolfWatchAppGaming({ - problem, - codeA, - scoreA, - codeB, - scoreB, + problem, + codeA, + scoreA, + codeB, + scoreB, }: Props) { - return ( - <div style={{ display: "flex", flexDirection: "column" }}> - <div style={{ display: "flex", flex: 1, justifyContent: "center" }}> - <div>{problem}</div> - </div> - <div style={{ display: "flex", flex: 3 }}> - <div style={{ display: "flex", flex: 3, flexDirection: "column" }}> - <div style={{ flex: 1, justifyContent: "center" }}>{scoreA}</div> - <div style={{ flex: 3 }}> - <pre> - <code>{codeA}</code> - </pre> - </div> - </div> - <div style={{ display: "flex", flex: 3, flexDirection: "column" }}> - <div style={{ flex: 1, justifyContent: "center" }}>{scoreB}</div> - <div style={{ flex: 3 }}> - <pre> - <code>{codeB}</code> - </pre> - </div> - </div> - </div> - </div> - ); + return ( + <div style={{ display: "flex", flexDirection: "column" }}> + <div style={{ display: "flex", flex: 1, justifyContent: "center" }}> + <div>{problem}</div> + </div> + <div style={{ display: "flex", flex: 3 }}> + <div style={{ display: "flex", flex: 3, flexDirection: "column" }}> + <div style={{ flex: 1, justifyContent: "center" }}>{scoreA}</div> + <div style={{ flex: 3 }}> + <pre> + <code>{codeA}</code> + </pre> + </div> + </div> + <div style={{ display: "flex", flex: 3, flexDirection: "column" }}> + <div style={{ flex: 1, justifyContent: "center" }}>{scoreB}</div> + <div style={{ flex: 3 }}> + <pre> + <code>{codeB}</code> + </pre> + </div> + </div> + </div> + </div> + ); } diff --git a/frontend/app/components/GolfWatchApps/GolfWatchAppStarting.tsx b/frontend/app/components/GolfWatchApps/GolfWatchAppStarting.tsx index 3c9919c..ef72cec 100644 --- a/frontend/app/components/GolfWatchApps/GolfWatchAppStarting.tsx +++ b/frontend/app/components/GolfWatchApps/GolfWatchAppStarting.tsx @@ -1,15 +1,15 @@ type Props = { - timeLeft: number; + timeLeft: number; }; export default function GolfWatchAppStarting({ timeLeft }: Props) { - return ( - <div className="min-h-screen bg-gray-100 flex items-center justify-center"> - <div className="text-center"> - <h1 className="text-4xl font-bold text-black-600 mb-4"> - Starting... ({timeLeft} s) - </h1> - </div> - </div> - ); + return ( + <div className="min-h-screen bg-gray-100 flex items-center justify-center"> + <div className="text-center"> + <h1 className="text-4xl font-bold text-black-600 mb-4"> + Starting... ({timeLeft} s) + </h1> + </div> + </div> + ); } diff --git a/frontend/app/components/GolfWatchApps/GolfWatchAppWaiting.tsx b/frontend/app/components/GolfWatchApps/GolfWatchAppWaiting.tsx index 6733b3b..d58ec19 100644 --- a/frontend/app/components/GolfWatchApps/GolfWatchAppWaiting.tsx +++ b/frontend/app/components/GolfWatchApps/GolfWatchAppWaiting.tsx @@ -1,3 +1,3 @@ export default function GolfWatchAppWaiting() { - return <div>Waiting...</div>; + return <div>Waiting...</div>; } diff --git a/frontend/app/entry.client.tsx b/frontend/app/entry.client.tsx index 7860ee3..92d1585 100644 --- a/frontend/app/entry.client.tsx +++ b/frontend/app/entry.client.tsx @@ -3,10 +3,10 @@ import { StrictMode, startTransition } from "react"; import { hydrateRoot } from "react-dom/client"; startTransition(() => { - hydrateRoot( - document, - <StrictMode> - <RemixBrowser /> - </StrictMode>, - ); + hydrateRoot( + document, + <StrictMode> + <RemixBrowser /> + </StrictMode>, + ); }); diff --git a/frontend/app/entry.server.tsx b/frontend/app/entry.server.tsx index 77b6f15..a1ffa99 100644 --- a/frontend/app/entry.server.tsx +++ b/frontend/app/entry.server.tsx @@ -9,116 +9,116 @@ import { renderToPipeableStream } from "react-dom/server"; const ABORT_DELAY = 5_000; export default function handleRequest( - request: Request, - responseStatusCode: number, - responseHeaders: Headers, - remixContext: EntryContext, + request: Request, + responseStatusCode: number, + responseHeaders: Headers, + remixContext: EntryContext, ) { - return isbot(request.headers.get("user-agent") || "") - ? handleBotRequest( - request, - responseStatusCode, - responseHeaders, - remixContext, - ) - : handleBrowserRequest( - request, - responseStatusCode, - responseHeaders, - remixContext, - ); + return isbot(request.headers.get("user-agent") || "") + ? handleBotRequest( + request, + responseStatusCode, + responseHeaders, + remixContext, + ) + : handleBrowserRequest( + request, + responseStatusCode, + responseHeaders, + remixContext, + ); } function handleBotRequest( - request: Request, - responseStatusCode: number, - responseHeaders: Headers, - remixContext: EntryContext, + request: Request, + responseStatusCode: number, + responseHeaders: Headers, + remixContext: EntryContext, ) { - return new Promise((resolve, reject) => { - let shellRendered = false; - const { pipe, abort } = renderToPipeableStream( - <RemixServer - context={remixContext} - url={request.url} - abortDelay={ABORT_DELAY} - />, - { - onAllReady() { - shellRendered = true; - const body = new PassThrough(); - const stream = createReadableStreamFromReadable(body); + return new Promise((resolve, reject) => { + let shellRendered = false; + const { pipe, abort } = renderToPipeableStream( + <RemixServer + context={remixContext} + url={request.url} + abortDelay={ABORT_DELAY} + />, + { + onAllReady() { + shellRendered = true; + const body = new PassThrough(); + const stream = createReadableStreamFromReadable(body); - responseHeaders.set("Content-Type", "text/html"); + responseHeaders.set("Content-Type", "text/html"); - resolve( - new Response(stream, { - headers: responseHeaders, - status: responseStatusCode, - }), - ); + resolve( + new Response(stream, { + headers: responseHeaders, + status: responseStatusCode, + }), + ); - pipe(body); - }, - onShellError(error: unknown) { - reject(error); - }, - onError(error: unknown) { - responseStatusCode = 500; - if (shellRendered) { - console.error(error); - } - }, - }, - ); + pipe(body); + }, + onShellError(error: unknown) { + reject(error); + }, + onError(error: unknown) { + responseStatusCode = 500; + if (shellRendered) { + console.error(error); + } + }, + }, + ); - setTimeout(abort, ABORT_DELAY); - }); + setTimeout(abort, ABORT_DELAY); + }); } function handleBrowserRequest( - request: Request, - responseStatusCode: number, - responseHeaders: Headers, - remixContext: EntryContext, + request: Request, + responseStatusCode: number, + responseHeaders: Headers, + remixContext: EntryContext, ) { - return new Promise((resolve, reject) => { - let shellRendered = false; - const { pipe, abort } = renderToPipeableStream( - <RemixServer - context={remixContext} - url={request.url} - abortDelay={ABORT_DELAY} - />, - { - onShellReady() { - shellRendered = true; - const body = new PassThrough(); - const stream = createReadableStreamFromReadable(body); + return new Promise((resolve, reject) => { + let shellRendered = false; + const { pipe, abort } = renderToPipeableStream( + <RemixServer + context={remixContext} + url={request.url} + abortDelay={ABORT_DELAY} + />, + { + onShellReady() { + shellRendered = true; + const body = new PassThrough(); + const stream = createReadableStreamFromReadable(body); - responseHeaders.set("Content-Type", "text/html"); + responseHeaders.set("Content-Type", "text/html"); - resolve( - new Response(stream, { - headers: responseHeaders, - status: responseStatusCode, - }), - ); + resolve( + new Response(stream, { + headers: responseHeaders, + status: responseStatusCode, + }), + ); - pipe(body); - }, - onShellError(error: unknown) { - reject(error); - }, - onError(error: unknown) { - responseStatusCode = 500; - if (shellRendered) { - console.error(error); - } - }, - }, - ); + pipe(body); + }, + onShellError(error: unknown) { + reject(error); + }, + onError(error: unknown) { + responseStatusCode = 500; + if (shellRendered) { + console.error(error); + } + }, + }, + ); - setTimeout(abort, ABORT_DELAY); - }); + setTimeout(abort, ABORT_DELAY); + }); } diff --git a/frontend/app/root.tsx b/frontend/app/root.tsx index 9768f45..58a06dd 100644 --- a/frontend/app/root.tsx +++ b/frontend/app/root.tsx @@ -1,35 +1,35 @@ import type { LinksFunction } from "@remix-run/node"; import { - Links, - Meta, - Outlet, - Scripts, - ScrollRestoration, + Links, + Meta, + Outlet, + Scripts, + ScrollRestoration, } from "@remix-run/react"; import "./tailwind.css"; export const links: LinksFunction = () => { - return [{ rel: "icon", href: "/favicon.svg" }]; + return [{ rel: "icon", href: "/favicon.svg" }]; }; export function Layout({ children }: { children: React.ReactNode }) { - return ( - <html lang="en"> - <head> - <meta charSet="utf-8" /> - <meta name="viewport" content="width=device-width, initial-scale=1" /> - <Meta /> - <Links /> - </head> - <body> - {children} - <ScrollRestoration /> - <Scripts /> - </body> - </html> - ); + return ( + <html lang="en"> + <head> + <meta charSet="utf-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1" /> + <Meta /> + <Links /> + </head> + <body> + {children} + <ScrollRestoration /> + <Scripts /> + </body> + </html> + ); } export default function App() { - return <Outlet />; + return <Outlet />; } diff --git a/frontend/app/routes/_index.tsx b/frontend/app/routes/_index.tsx index b5951ab..c56d4b2 100644 --- a/frontend/app/routes/_index.tsx +++ b/frontend/app/routes/_index.tsx @@ -2,25 +2,25 @@ import type { MetaFunction } from "@remix-run/node"; import { Link } from "@remix-run/react"; export const meta: MetaFunction = () => { - return [{ title: "iOSDC Japan 2024 Albatross.swift" }]; + return [{ title: "iOSDC Japan 2024 Albatross.swift" }]; }; export default function Index() { - return ( - <div className="min-h-screen bg-gray-100 flex items-center justify-center"> - <div className="text-center"> - <h1 className="text-4xl font-bold text-blue-600 mb-4"> - iOSDC Japan 2024 Albatross.swift - </h1> - <p> - <Link - to="/login" - className="text-lg text-white bg-blue-500 px-4 py-2 rounded hover:bg-blue-600 transition duration-300" - > - Login - </Link> - </p> - </div> - </div> - ); + return ( + <div className="min-h-screen bg-gray-100 flex items-center justify-center"> + <div className="text-center"> + <h1 className="text-4xl font-bold text-blue-600 mb-4"> + iOSDC Japan 2024 Albatross.swift + </h1> + <p> + <Link + to="/login" + className="text-lg text-white bg-blue-500 px-4 py-2 rounded hover:bg-blue-600 transition duration-300" + > + Login + </Link> + </p> + </div> + </div> + ); } diff --git a/frontend/app/routes/admin.dashboard.tsx b/frontend/app/routes/admin.dashboard.tsx index 1d172af..ce3e910 100644 --- a/frontend/app/routes/admin.dashboard.tsx +++ b/frontend/app/routes/admin.dashboard.tsx @@ -3,29 +3,29 @@ import { Link } from "@remix-run/react"; import { isAuthenticated } from "../.server/auth"; export const meta: MetaFunction = () => { - return [{ title: "[Admin] Dashboard | iOSDC Japan 2024 Albatross.swift" }]; + return [{ title: "[Admin] Dashboard | iOSDC Japan 2024 Albatross.swift" }]; }; export async function loader({ request }: LoaderFunctionArgs) { - const { user } = await isAuthenticated(request, { - failureRedirect: "/login", - }); - if (!user.is_admin) { - throw new Error("Unauthorized"); - } - return null; + const { user } = await isAuthenticated(request, { + failureRedirect: "/login", + }); + if (!user.is_admin) { + throw new Error("Unauthorized"); + } + return null; } export default function AdminDashboard() { - return ( - <div> - <h1>[Admin] Dashboard</h1> - <p> - <Link to="/admin/users">Users</Link> - </p> - <p> - <Link to="/admin/games">Games</Link> - </p> - </div> - ); + return ( + <div> + <h1>[Admin] Dashboard</h1> + <p> + <Link to="/admin/users">Users</Link> + </p> + <p> + <Link to="/admin/games">Games</Link> + </p> + </div> + ); } diff --git a/frontend/app/routes/admin.games.tsx b/frontend/app/routes/admin.games.tsx index adba7f5..af3554e 100644 --- a/frontend/app/routes/admin.games.tsx +++ b/frontend/app/routes/admin.games.tsx @@ -4,37 +4,37 @@ import { adminApiGetGames } from "../.server/api/client"; import { isAuthenticated } from "../.server/auth"; export const meta: MetaFunction = () => { - return [{ title: "[Admin] Games | iOSDC Japan 2024 Albatross.swift" }]; + return [{ title: "[Admin] Games | iOSDC Japan 2024 Albatross.swift" }]; }; export async function loader({ request }: LoaderFunctionArgs) { - const { user, token } = await isAuthenticated(request, { - failureRedirect: "/login", - }); - if (!user.is_admin) { - throw new Error("Unauthorized"); - } - const { games } = await adminApiGetGames(token); - return { games }; + const { user, token } = await isAuthenticated(request, { + failureRedirect: "/login", + }); + if (!user.is_admin) { + throw new Error("Unauthorized"); + } + const { games } = await adminApiGetGames(token); + return { games }; } export default function AdminGames() { - const { games } = useLoaderData<typeof loader>()!; + const { games } = useLoaderData<typeof loader>()!; - return ( - <div> - <div> - <h1>[Admin] Games</h1> - <ul> - {games.map((game) => ( - <li key={game.game_id}> - <Link to={`/admin/games/${game.game_id}`}> - {game.display_name} (id={game.game_id}) - </Link> - </li> - ))} - </ul> - </div> - </div> - ); + return ( + <div> + <div> + <h1>[Admin] Games</h1> + <ul> + {games.map((game) => ( + <li key={game.game_id}> + <Link to={`/admin/games/${game.game_id}`}> + {game.display_name} (id={game.game_id}) + </Link> + </li> + ))} + </ul> + </div> + </div> + ); } diff --git a/frontend/app/routes/admin.games_.$gameId.tsx b/frontend/app/routes/admin.games_.$gameId.tsx index a7bd651..34860ab 100644 --- a/frontend/app/routes/admin.games_.$gameId.tsx +++ b/frontend/app/routes/admin.games_.$gameId.tsx @@ -1,107 +1,107 @@ import type { - ActionFunctionArgs, - LoaderFunctionArgs, - MetaFunction, + ActionFunctionArgs, + LoaderFunctionArgs, + MetaFunction, } from "@remix-run/node"; import { Form, useLoaderData } from "@remix-run/react"; import { adminApiGetGame, adminApiPutGame } from "../.server/api/client"; import { isAuthenticated } from "../.server/auth"; export const meta: MetaFunction<typeof loader> = ({ data }) => { - return [ - { - title: data - ? `[Admin] Game Edit ${data.game.display_name} | iOSDC Japan 2024 Albatross.swift` - : "[Admin] Game Edit | iOSDC Japan 2024 Albatross.swift", - }, - ]; + return [ + { + title: data + ? `[Admin] Game Edit ${data.game.display_name} | iOSDC Japan 2024 Albatross.swift` + : "[Admin] Game Edit | iOSDC Japan 2024 Albatross.swift", + }, + ]; }; export async function loader({ request, params }: LoaderFunctionArgs) { - const { user, token } = await isAuthenticated(request, { - failureRedirect: "/login", - }); - if (!user.is_admin) { - throw new Error("Unauthorized"); - } - const { gameId } = params; - const { game } = await adminApiGetGame(token, Number(gameId)); - return { game }; + const { user, token } = await isAuthenticated(request, { + failureRedirect: "/login", + }); + if (!user.is_admin) { + throw new Error("Unauthorized"); + } + const { gameId } = params; + const { game } = await adminApiGetGame(token, Number(gameId)); + return { game }; } export async function action({ request, params }: ActionFunctionArgs) { - const { user, token } = await isAuthenticated(request, { - failureRedirect: "/login", - }); - if (!user.is_admin) { - throw new Error("Unauthorized"); - } - const { gameId } = params; + const { user, token } = await isAuthenticated(request, { + failureRedirect: "/login", + }); + if (!user.is_admin) { + throw new Error("Unauthorized"); + } + const { gameId } = params; - const formData = await request.formData(); - const action = formData.get("action"); + const formData = await request.formData(); + const action = formData.get("action"); - const nextState = - action === "open" - ? "waiting_entries" - : action === "start" - ? "prepare" - : null; - if (!nextState) { - throw new Error("Invalid action"); - } + const nextState = + action === "open" + ? "waiting_entries" + : action === "start" + ? "prepare" + : null; + if (!nextState) { + throw new Error("Invalid action"); + } - await adminApiPutGame(token, Number(gameId), { - state: nextState, - }); - return null; + await adminApiPutGame(token, Number(gameId), { + state: nextState, + }); + return null; } export default function AdminGameEdit() { - const { game } = useLoaderData<typeof loader>()!; + const { game } = useLoaderData<typeof loader>()!; - return ( - <div> - <div> - <h1>[Admin] Game Edit {game.display_name}</h1> - <ul> - <li>ID: {game.game_id}</li> - <li>State: {game.state}</li> - <li>Display Name: {game.display_name}</li> - <li>Duration Seconds: {game.duration_seconds}</li> - <li> - Started At:{" "} - {game.started_at - ? new Date(game.started_at * 1000).toString() - : "-"} - </li> - <li>Problem ID: {game.problem ? game.problem.problem_id : "-"}</li> - </ul> - <div> - <Form method="post"> - <div> - <button - type="submit" - name="action" - value="open" - disabled={game.state !== "closed"} - > - Open - </button> - </div> - <div> - <button - type="submit" - name="action" - value="start" - disabled={game.state !== "waiting_start"} - > - Start - </button> - </div> - </Form> - </div> - </div> - </div> - ); + return ( + <div> + <div> + <h1>[Admin] Game Edit {game.display_name}</h1> + <ul> + <li>ID: {game.game_id}</li> + <li>State: {game.state}</li> + <li>Display Name: {game.display_name}</li> + <li>Duration Seconds: {game.duration_seconds}</li> + <li> + Started At:{" "} + {game.started_at + ? new Date(game.started_at * 1000).toString() + : "-"} + </li> + <li>Problem ID: {game.problem ? game.problem.problem_id : "-"}</li> + </ul> + <div> + <Form method="post"> + <div> + <button + type="submit" + name="action" + value="open" + disabled={game.state !== "closed"} + > + Open + </button> + </div> + <div> + <button + type="submit" + name="action" + value="start" + disabled={game.state !== "waiting_start"} + > + Start + </button> + </div> + </Form> + </div> + </div> + </div> + ); } diff --git a/frontend/app/routes/admin.users.tsx b/frontend/app/routes/admin.users.tsx index 8d9a8f2..9eed263 100644 --- a/frontend/app/routes/admin.users.tsx +++ b/frontend/app/routes/admin.users.tsx @@ -4,36 +4,36 @@ import { adminApiGetUsers } from "../.server/api/client"; import { isAuthenticated } from "../.server/auth"; export const meta: MetaFunction = () => { - return [{ title: "[Admin] Users | iOSDC Japan 2024 Albatross.swift" }]; + return [{ title: "[Admin] Users | iOSDC Japan 2024 Albatross.swift" }]; }; export async function loader({ request }: LoaderFunctionArgs) { - const { user, token } = await isAuthenticated(request, { - failureRedirect: "/login", - }); - if (!user.is_admin) { - throw new Error("Unauthorized"); - } - const { users } = await adminApiGetUsers(token); - return { users }; + const { user, token } = await isAuthenticated(request, { + failureRedirect: "/login", + }); + if (!user.is_admin) { + throw new Error("Unauthorized"); + } + const { users } = await adminApiGetUsers(token); + return { users }; } export default function AdminUsers() { - const { users } = useLoaderData<typeof loader>()!; + const { users } = useLoaderData<typeof loader>()!; - return ( - <div> - <div> - <h1>[Admin] Users</h1> - <ul> - {users.map((user) => ( - <li key={user.user_id}> - {user.display_name} (id={user.user_id} username={user.username}) - {user.is_admin && <span> admin</span>} - </li> - ))} - </ul> - </div> - </div> - ); + return ( + <div> + <div> + <h1>[Admin] Users</h1> + <ul> + {users.map((user) => ( + <li key={user.user_id}> + {user.display_name} (id={user.user_id} username={user.username}) + {user.is_admin && <span> admin</span>} + </li> + ))} + </ul> + </div> + </div> + ); } diff --git a/frontend/app/routes/dashboard.tsx b/frontend/app/routes/dashboard.tsx index 1f80634..1c2137d 100644 --- a/frontend/app/routes/dashboard.tsx +++ b/frontend/app/routes/dashboard.tsx @@ -5,72 +5,72 @@ import { apiGetGames } from "../.server/api/client"; import { isAuthenticated } from "../.server/auth"; export const meta: MetaFunction = () => { - return [{ title: "Dashboard | iOSDC Japan 2024 Albatross.swift" }]; + return [{ title: "Dashboard | iOSDC Japan 2024 Albatross.swift" }]; }; export async function loader({ request }: LoaderFunctionArgs) { - const { user, token } = await isAuthenticated(request, { - failureRedirect: "/login", - }); - if (user.is_admin) { - return redirect("/admin/dashboard"); - } - const { games } = await apiGetGames(token); - return { - user, - games, - }; + const { user, token } = await isAuthenticated(request, { + failureRedirect: "/login", + }); + if (user.is_admin) { + return redirect("/admin/dashboard"); + } + const { games } = await apiGetGames(token); + return { + user, + games, + }; } export default function Dashboard() { - const { user, games } = useLoaderData<typeof loader>()!; + const { user, games } = useLoaderData<typeof loader>()!; - return ( - <div className="min-h-screen p-8"> - <div className="p-6 rounded shadow-md max-w-4xl mx-auto"> - <h1 className="text-3xl font-bold mb-4"> - {user.username}{" "} - {user.is_admin && <span className="text-red-500 text-lg">admin</span>} - </h1> - <h2 className="text-2xl font-semibold mb-2">User</h2> - <div className="mb-6"> - <ul className="list-disc list-inside"> - <li>Name: {user.display_name}</li> - </ul> - </div> - <h2 className="text-2xl font-semibold mb-2">Games</h2> - <div> - <ul className="list-disc list-inside"> - {games.map((game) => ( - <li key={game.game_id}> - {game.display_name}{" "} - {game.state === "closed" || game.state === "finished" ? ( - <span className="inline-block px-6 py-2 text-gray-400 bg-gray-200 cursor-not-allowed rounded"> - Entry - </span> - ) : ( - <Link - to={`/golf/${game.game_id}/play`} - className="inline-block px-6 py-2 text-white bg-blue-500 hover:bg-blue-700 rounded" - > - Entry - </Link> - )} - </li> - ))} - </ul> - </div> - <div> - <Form method="post" action="/logout"> - <button - className="mt-6 px-6 py-2 text-white bg-red-500 hover:bg-red-700 rounded" - type="submit" - > - Logout - </button> - </Form> - </div> - </div> - </div> - ); + return ( + <div className="min-h-screen p-8"> + <div className="p-6 rounded shadow-md max-w-4xl mx-auto"> + <h1 className="text-3xl font-bold mb-4"> + {user.username}{" "} + {user.is_admin && <span className="text-red-500 text-lg">admin</span>} + </h1> + <h2 className="text-2xl font-semibold mb-2">User</h2> + <div className="mb-6"> + <ul className="list-disc list-inside"> + <li>Name: {user.display_name}</li> + </ul> + </div> + <h2 className="text-2xl font-semibold mb-2">Games</h2> + <div> + <ul className="list-disc list-inside"> + {games.map((game) => ( + <li key={game.game_id}> + {game.display_name}{" "} + {game.state === "closed" || game.state === "finished" ? ( + <span className="inline-block px-6 py-2 text-gray-400 bg-gray-200 cursor-not-allowed rounded"> + Entry + </span> + ) : ( + <Link + to={`/golf/${game.game_id}/play`} + className="inline-block px-6 py-2 text-white bg-blue-500 hover:bg-blue-700 rounded" + > + Entry + </Link> + )} + </li> + ))} + </ul> + </div> + <div> + <Form method="post" action="/logout"> + <button + className="mt-6 px-6 py-2 text-white bg-red-500 hover:bg-red-700 rounded" + type="submit" + > + Logout + </button> + </Form> + </div> + </div> + </div> + ); } diff --git a/frontend/app/routes/golf.$gameId.play.tsx b/frontend/app/routes/golf.$gameId.play.tsx index 0ce3353..d498200 100644 --- a/frontend/app/routes/golf.$gameId.play.tsx +++ b/frontend/app/routes/golf.$gameId.play.tsx @@ -7,40 +7,40 @@ import GolfPlayApp from "../components/GolfPlayApp.client"; import GolfPlayAppConnecting from "../components/GolfPlayApps/GolfPlayAppConnecting"; export const meta: MetaFunction<typeof loader> = ({ data }) => { - return [ - { - title: data - ? `Golf Playing ${data.game.display_name} | iOSDC Japan 2024 Albatross.swift` - : "Golf Playing | iOSDC Japan 2024 Albatross.swift", - }, - ]; + return [ + { + title: data + ? `Golf Playing ${data.game.display_name} | iOSDC Japan 2024 Albatross.swift` + : "Golf Playing | iOSDC Japan 2024 Albatross.swift", + }, + ]; }; export async function loader({ params, request }: LoaderFunctionArgs) { - const { token } = await isAuthenticated(request, { - failureRedirect: "/login", - }); + const { token } = await isAuthenticated(request, { + failureRedirect: "/login", + }); - const fetchGame = async () => { - return (await apiGetGame(token, Number(params.gameId))).game; - }; - const fetchSockToken = async () => { - return (await apiGetToken(token)).token; - }; + const fetchGame = async () => { + return (await apiGetGame(token, Number(params.gameId))).game; + }; + const fetchSockToken = async () => { + return (await apiGetToken(token)).token; + }; - const [game, sockToken] = await Promise.all([fetchGame(), fetchSockToken()]); - return { - game, - sockToken, - }; + const [game, sockToken] = await Promise.all([fetchGame(), fetchSockToken()]); + return { + game, + sockToken, + }; } export default function GolfPlay() { - const { game, sockToken } = useLoaderData<typeof loader>(); + const { game, sockToken } = useLoaderData<typeof loader>(); - return ( - <ClientOnly fallback={<GolfPlayAppConnecting />}> - {() => <GolfPlayApp game={game} sockToken={sockToken} />} - </ClientOnly> - ); + return ( + <ClientOnly fallback={<GolfPlayAppConnecting />}> + {() => <GolfPlayApp game={game} sockToken={sockToken} />} + </ClientOnly> + ); } diff --git a/frontend/app/routes/golf.$gameId.watch.tsx b/frontend/app/routes/golf.$gameId.watch.tsx index 0f0e085..0203e27 100644 --- a/frontend/app/routes/golf.$gameId.watch.tsx +++ b/frontend/app/routes/golf.$gameId.watch.tsx @@ -7,40 +7,40 @@ import GolfWatchApp from "../components/GolfWatchApp.client"; import GolfWatchAppConnecting from "../components/GolfWatchApps/GolfWatchAppConnecting"; export const meta: MetaFunction<typeof loader> = ({ data }) => { - return [ - { - title: data - ? `Golf Watching ${data.game.display_name} | iOSDC Japan 2024 Albatross.swift` - : "Golf Watching | iOSDC Japan 2024 Albatross.swift", - }, - ]; + return [ + { + title: data + ? `Golf Watching ${data.game.display_name} | iOSDC Japan 2024 Albatross.swift` + : "Golf Watching | iOSDC Japan 2024 Albatross.swift", + }, + ]; }; export async function loader({ params, request }: LoaderFunctionArgs) { - const { token } = await isAuthenticated(request, { - failureRedirect: "/login", - }); + const { token } = await isAuthenticated(request, { + failureRedirect: "/login", + }); - const fetchGame = async () => { - return (await apiGetGame(token, Number(params.gameId))).game; - }; - const fetchSockToken = async () => { - return (await apiGetToken(token)).token; - }; + const fetchGame = async () => { + return (await apiGetGame(token, Number(params.gameId))).game; + }; + const fetchSockToken = async () => { + return (await apiGetToken(token)).token; + }; - const [game, sockToken] = await Promise.all([fetchGame(), fetchSockToken()]); - return { - game, - sockToken, - }; + const [game, sockToken] = await Promise.all([fetchGame(), fetchSockToken()]); + return { + game, + sockToken, + }; } export default function GolfWatch() { - const { game, sockToken } = useLoaderData<typeof loader>(); + const { game, sockToken } = useLoaderData<typeof loader>(); - return ( - <ClientOnly fallback={<GolfWatchAppConnecting />}> - {() => <GolfWatchApp game={game} sockToken={sockToken} />} - </ClientOnly> - ); + return ( + <ClientOnly fallback={<GolfWatchAppConnecting />}> + {() => <GolfWatchApp game={game} sockToken={sockToken} />} + </ClientOnly> + ); } diff --git a/frontend/app/routes/login.tsx b/frontend/app/routes/login.tsx index f63df08..95effaa 100644 --- a/frontend/app/routes/login.tsx +++ b/frontend/app/routes/login.tsx @@ -1,74 +1,74 @@ import type { - ActionFunctionArgs, - LoaderFunctionArgs, - MetaFunction, + ActionFunctionArgs, + LoaderFunctionArgs, + MetaFunction, } from "@remix-run/node"; import { Form } from "@remix-run/react"; import { authenticator } from "../.server/auth"; export const meta: MetaFunction = () => { - return [{ title: "Login | iOSDC Japan 2024 Albatross.swift" }]; + return [{ title: "Login | iOSDC Japan 2024 Albatross.swift" }]; }; export async function loader({ request }: LoaderFunctionArgs) { - return await authenticator.isAuthenticated(request, { - successRedirect: "/dashboard", - }); + return await authenticator.isAuthenticated(request, { + successRedirect: "/dashboard", + }); } export async function action({ request }: ActionFunctionArgs) { - return await authenticator.authenticate("default", request, { - successRedirect: "/dashboard", - failureRedirect: "/login", - }); + return await authenticator.authenticate("default", request, { + successRedirect: "/dashboard", + failureRedirect: "/login", + }); } export default function Login() { - return ( - <div className="min-h-screen bg-gray-100 flex items-center justify-center"> - <Form - method="post" - className="bg-white p-8 rounded shadow-md w-full max-w-sm" - > - <h2 className="text-2xl font-bold mb-6 text-center">Login</h2> - <div className="mb-4"> - <label - htmlFor="username" - className="block text-sm font-medium text-gray-700" - > - Username - </label> - <input - type="text" - name="username" - id="username" - required - className="mt-1 p-2 block w-full border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500" - /> - </div> - <div className="mb-6"> - <label - htmlFor="password" - className="block text-sm font-medium text-gray-700" - > - Password - </label> - <input - type="password" - name="password" - id="password" - autoComplete="current-password" - required - className="mt-1 p-2 block w-full border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500" - /> - </div> - <button - type="submit" - className="w-full bg-blue-500 text-white py-2 rounded hover:bg-blue-600 transition duration-300" - > - Log In - </button> - </Form> - </div> - ); + return ( + <div className="min-h-screen bg-gray-100 flex items-center justify-center"> + <Form + method="post" + className="bg-white p-8 rounded shadow-md w-full max-w-sm" + > + <h2 className="text-2xl font-bold mb-6 text-center">Login</h2> + <div className="mb-4"> + <label + htmlFor="username" + className="block text-sm font-medium text-gray-700" + > + Username + </label> + <input + type="text" + name="username" + id="username" + required + className="mt-1 p-2 block w-full border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500" + /> + </div> + <div className="mb-6"> + <label + htmlFor="password" + className="block text-sm font-medium text-gray-700" + > + Password + </label> + <input + type="password" + name="password" + id="password" + autoComplete="current-password" + required + className="mt-1 p-2 block w-full border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500" + /> + </div> + <button + type="submit" + className="w-full bg-blue-500 text-white py-2 rounded hover:bg-blue-600 transition duration-300" + > + Log In + </button> + </Form> + </div> + ); } diff --git a/frontend/app/routes/logout.tsx b/frontend/app/routes/logout.tsx index 030bde0..012d9e9 100644 --- a/frontend/app/routes/logout.tsx +++ b/frontend/app/routes/logout.tsx @@ -2,6 +2,6 @@ import type { ActionFunctionArgs } from "@remix-run/node"; import { authenticator } from "../.server/auth"; export async function action({ request }: ActionFunctionArgs) { - await authenticator.logout(request, { redirectTo: "/" }); - return null; + await authenticator.logout(request, { redirectTo: "/" }); + return null; } diff --git a/frontend/biome.json b/frontend/biome.json index c13b764..2bafdc7 100644 --- a/frontend/biome.json +++ b/frontend/biome.json @@ -1,18 +1,17 @@ { - "$schema": "https://biomejs.dev/schemas/1.8.3/schema.json", - "files": { - "ignore": ["./.cache", "./build", "./app/.server/api/schema.d.ts"] - }, - "vcs": { - "enabled": true, - "clientKind": "git", - "useIgnoreFile": true - }, - "linter": { - "enabled": false - }, - "formatter": { - "indentStyle": "space", - "indentWidth": 2 - } + "$schema": "https://biomejs.dev/schemas/1.8.3/schema.json", + "files": { + "ignore": ["./.cache", "./build", "./app/.server/api/schema.d.ts"] + }, + "vcs": { + "enabled": true, + "clientKind": "git", + "useIgnoreFile": true + }, + "linter": { + "enabled": false + }, + "formatter": { + "enabled": true + } } diff --git a/frontend/package.json b/frontend/package.json index e7fdf45..d54b3e0 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,54 +1,55 @@ { - "name": "iosdc-japan-2024-albatross-frontend", - "private": true, - "sideEffects": false, - "type": "module", - "scripts": { - "build": "remix vite:build", - "dev": "remix vite:dev", - "lint": "eslint --ignore-path .gitignore --cache --cache-location ./node_modules/.cache/eslint .", - "openapi-typescript": "npx --no-install openapi-typescript --output ./app/.server/api/schema.d.ts ../openapi.yaml", - "start": "remix-serve ./build/server/index.js", - "typecheck": "tsc" - }, - "dependencies": { - "@remix-run/node": "^2.10.3", - "@remix-run/react": "^2.10.3", - "@remix-run/serve": "^2.10.3", - "isbot": "^5.1.13", - "jwt-decode": "^4.0.0", - "openapi-fetch": "^0.10.2", - "react": "^18.3.1", - "react-dom": "^18.3.1", - "react-use-websocket": "^4.8.1", - "remix-auth": "^3.7.0", - "remix-auth-form": "^1.5.0", - "remix-utils": "^7.6.0", - "sakura.css": "^1.5.0", - "use-debounce": "^10.0.1" - }, - "devDependencies": { - "@biomejs/biome": "^1.8.3", - "@remix-run/dev": "^2.10.3", - "@types/react": "^18.3.3", - "@types/react-dom": "^18.3.0", - "@typescript-eslint/eslint-plugin": "^6.7.4", - "@typescript-eslint/parser": "^6.7.4", - "autoprefixer": "^10.4.19", - "eslint": "^8.56.0", - "eslint-import-resolver-typescript": "^3.6.1", - "eslint-plugin-import": "^2.29.1", - "eslint-plugin-jsx-a11y": "^6.9.0", - "eslint-plugin-react": "^7.35.0", - "eslint-plugin-react-hooks": "^4.6.2", - "openapi-typescript": "^7.1.0", - "postcss": "^8.4.40", - "tailwindcss": "^3.4.7", - "typescript": "^5.5.4", - "vite": "^5.3.5", - "vite-tsconfig-paths": "^4.3.2" - }, - "engines": { - "node": ">=20.0.0" - } + "name": "iosdc-japan-2024-albatross-frontend", + "private": true, + "sideEffects": false, + "type": "module", + "scripts": { + "build": "remix vite:build", + "check": "biome check --write", + "dev": "remix vite:dev", + "lint": "eslint --ignore-path .gitignore --cache --cache-location ./node_modules/.cache/eslint .", + "openapi-typescript": "openapi-typescript --output ./app/.server/api/schema.d.ts ../openapi.yaml", + "start": "remix-serve ./build/server/index.js", + "typecheck": "tsc" + }, + "dependencies": { + "@remix-run/node": "^2.10.3", + "@remix-run/react": "^2.10.3", + "@remix-run/serve": "^2.10.3", + "isbot": "^5.1.13", + "jwt-decode": "^4.0.0", + "openapi-fetch": "^0.10.2", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-use-websocket": "^4.8.1", + "remix-auth": "^3.7.0", + "remix-auth-form": "^1.5.0", + "remix-utils": "^7.6.0", + "sakura.css": "^1.5.0", + "use-debounce": "^10.0.1" + }, + "devDependencies": { + "@biomejs/biome": "^1.8.3", + "@remix-run/dev": "^2.10.3", + "@types/react": "^18.3.3", + "@types/react-dom": "^18.3.0", + "@typescript-eslint/eslint-plugin": "^6.7.4", + "@typescript-eslint/parser": "^6.7.4", + "autoprefixer": "^10.4.19", + "eslint": "^8.56.0", + "eslint-import-resolver-typescript": "^3.6.1", + "eslint-plugin-import": "^2.29.1", + "eslint-plugin-jsx-a11y": "^6.9.0", + "eslint-plugin-react": "^7.35.0", + "eslint-plugin-react-hooks": "^4.6.2", + "openapi-typescript": "^7.1.0", + "postcss": "^8.4.40", + "tailwindcss": "^3.4.7", + "typescript": "^5.5.4", + "vite": "^5.3.5", + "vite-tsconfig-paths": "^4.3.2" + }, + "engines": { + "node": ">=20.0.0" + } } diff --git a/frontend/postcss.config.js b/frontend/postcss.config.js index 2aa7205..7b75c83 100644 --- a/frontend/postcss.config.js +++ b/frontend/postcss.config.js @@ -1,6 +1,6 @@ export default { - plugins: { - tailwindcss: {}, - autoprefixer: {}, - }, + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, }; diff --git a/frontend/tailwind.config.ts b/frontend/tailwind.config.ts index 34d03da..acee505 100644 --- a/frontend/tailwind.config.ts +++ b/frontend/tailwind.config.ts @@ -1,9 +1,9 @@ import type { Config } from "tailwindcss"; export default { - content: ["./app/**/{**,.client,.server}/**/*.{js,jsx,ts,tsx}"], - theme: { - extend: {}, - }, - plugins: [], + content: ["./app/**/{**,.client,.server}/**/*.{js,jsx,ts,tsx}"], + theme: { + extend: {}, + }, + plugins: [], } satisfies Config; diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index d52b6b9..33e429f 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -1,31 +1,31 @@ { - "include": [ - "**/*.ts", - "**/*.tsx", - "**/.server/**/*.ts", - "**/.server/**/*.tsx", - "**/.client/**/*.ts", - "**/.client/**/*.tsx" - ], - "compilerOptions": { - "lib": ["DOM", "DOM.Iterable", "ES2022"], - "types": ["@remix-run/node", "vite/client"], - "isolatedModules": true, - "esModuleInterop": true, - "jsx": "react-jsx", - "module": "ESNext", - "moduleResolution": "Bundler", - "resolveJsonModule": true, - "target": "ES2022", - "strict": true, - "noUncheckedIndexedAccess": true, - "allowJs": true, - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true, - "baseUrl": ".", - "paths": { - "~/*": ["./app/*"] - }, - "noEmit": true - } + "include": [ + "**/*.ts", + "**/*.tsx", + "**/.server/**/*.ts", + "**/.server/**/*.tsx", + "**/.client/**/*.ts", + "**/.client/**/*.tsx" + ], + "compilerOptions": { + "lib": ["DOM", "DOM.Iterable", "ES2022"], + "types": ["@remix-run/node", "vite/client"], + "isolatedModules": true, + "esModuleInterop": true, + "jsx": "react-jsx", + "module": "ESNext", + "moduleResolution": "Bundler", + "resolveJsonModule": true, + "target": "ES2022", + "strict": true, + "noUncheckedIndexedAccess": true, + "allowJs": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "baseUrl": ".", + "paths": { + "~/*": ["./app/*"] + }, + "noEmit": true + } } diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 54066fb..e07fb91 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -3,14 +3,14 @@ import { defineConfig } from "vite"; import tsconfigPaths from "vite-tsconfig-paths"; export default defineConfig({ - plugins: [ - remix({ - future: { - v3_fetcherPersist: true, - v3_relativeSplatPath: true, - v3_throwAbortReason: true, - }, - }), - tsconfigPaths(), - ], + plugins: [ + remix({ + future: { + v3_fetcherPersist: true, + v3_relativeSplatPath: true, + v3_throwAbortReason: true, + }, + }), + tsconfigPaths(), + ], }); |
