diff options
| -rw-r--r-- | backend/api/generated.go | 55 | ||||
| -rw-r--r-- | backend/api/handler.go | 3 | ||||
| -rw-r--r-- | frontend/app/api/schema.d.ts | 2 | ||||
| -rw-r--r-- | frontend/app/components/Gaming/CodePopover.tsx | 43 | ||||
| -rw-r--r-- | frontend/app/components/Gaming/RankingTable.tsx | 5 | ||||
| -rw-r--r-- | frontend/app/routes/dashboard.tsx | 19 | ||||
| -rw-r--r-- | frontend/package-lock.json | 385 | ||||
| -rw-r--r-- | frontend/package.json | 1 | ||||
| -rw-r--r-- | openapi/api-server.yaml | 5 |
9 files changed, 461 insertions, 57 deletions
diff --git a/backend/api/generated.go b/backend/api/generated.go index a6ba8ba..8a74b01 100644 --- a/backend/api/generated.go +++ b/backend/api/generated.go @@ -80,9 +80,10 @@ type Problem struct { // RankingEntry defines model for RankingEntry. type RankingEntry struct { - Player User `json:"player"` - Score int `json:"score"` - SubmittedAt int64 `json:"submitted_at"` + Code nullable.Nullable[string] `json:"code"` + Player User `json:"player"` + Score int `json:"score"` + SubmittedAt int64 `json:"submitted_at"` } // User defines model for User. @@ -1129,30 +1130,30 @@ func (sh *strictHandler) PostLogin(ctx echo.Context) error { // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/backend/api/handler.go b/backend/api/handler.go index d498770..6561151 100644 --- a/backend/api/handler.go +++ b/backend/api/handler.go @@ -253,6 +253,8 @@ func (h *Handler) GetGameWatchRanking(ctx context.Context, request GetGameWatchR } ranking := make([]RankingEntry, len(rows)) for i, row := range rows { + // TODO: check if game is finished. + code := &row.Submission.Code ranking[i] = RankingEntry{ Player: User{ UserID: int(row.User.UserID), @@ -264,6 +266,7 @@ func (h *Handler) GetGameWatchRanking(ctx context.Context, request GetGameWatchR }, Score: int(row.Submission.CodeSize), SubmittedAt: row.Submission.CreatedAt.Time.Unix(), + Code: toNullable(code), } } return GetGameWatchRanking200JSONResponse{ diff --git a/frontend/app/api/schema.d.ts b/frontend/app/api/schema.d.ts index 6f69292..af454e8 100644 --- a/frontend/app/api/schema.d.ts +++ b/frontend/app/api/schema.d.ts @@ -211,6 +211,8 @@ export interface components { score: number; /** @example 946684800 */ submitted_at: number; + /** @example echo 'hello world'; */ + code: string | null; }; }; responses: { diff --git a/frontend/app/components/Gaming/CodePopover.tsx b/frontend/app/components/Gaming/CodePopover.tsx new file mode 100644 index 0000000..a574a77 --- /dev/null +++ b/frontend/app/components/Gaming/CodePopover.tsx @@ -0,0 +1,43 @@ +import { Popover } from "@base-ui-components/react/popover"; +import { faCode, faXmark } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { calcCodeSize } from "../../states/play"; +import BorderedContainer from "../BorderedContainer"; +import CodeBlock from "../Gaming/CodeBlock"; + +type Props = { + code: string; +}; + +export default function CodePopover({ code }: Props) { + const codeSize = calcCodeSize(code); + + return ( + <Popover.Root> + <Popover.Trigger> + <FontAwesomeIcon icon={faCode} fixedWidth /> + </Popover.Trigger> + <Popover.Portal> + <Popover.Positioner> + <Popover.Popup> + <BorderedContainer className="grow flex flex-col gap-4"> + <div className="flex flex-row gap-2 items-center"> + <div className="grow font-semibold text-lg"> + コードサイズ: {codeSize} + </div> + <Popover.Close className="p-1 bg-gray-50 border-1 border-gray-300 rounded-sm"> + <FontAwesomeIcon + icon={faXmark} + fixedWidth + className="text-gray-500" + /> + </Popover.Close> + </div> + <CodeBlock code={code} language="php" /> + </BorderedContainer> + </Popover.Popup> + </Popover.Positioner> + </Popover.Portal> + </Popover.Root> + ); +} diff --git a/frontend/app/components/Gaming/RankingTable.tsx b/frontend/app/components/Gaming/RankingTable.tsx index a1e41f5..38d5200 100644 --- a/frontend/app/components/Gaming/RankingTable.tsx +++ b/frontend/app/components/Gaming/RankingTable.tsx @@ -1,6 +1,7 @@ import { useAtomValue } from "jotai"; import React from "react"; import { rankingAtom } from "../../states/watch"; +import CodePopover from "./CodePopover"; function TableHeaderCell({ children }: { children: React.ReactNode }) { return ( @@ -40,6 +41,7 @@ export default function RankingTable() { <TableHeaderCell>プレイヤー</TableHeaderCell> <TableHeaderCell>スコア</TableHeaderCell> <TableHeaderCell>提出時刻</TableHeaderCell> + <TableHeaderCell>コード</TableHeaderCell> </tr> </thead> <tbody className="bg-white divide-y divide-gray-300"> @@ -54,6 +56,9 @@ export default function RankingTable() { <TableBodyCell> {formatUnixTimestamp(entry.submitted_at)} </TableBodyCell> + <TableBodyCell> + {entry.code && <CodePopover code={entry.code} />} + </TableBodyCell> </tr> ))} </tbody> diff --git a/frontend/app/routes/dashboard.tsx b/frontend/app/routes/dashboard.tsx index d0fea47..b7bfb01 100644 --- a/frontend/app/routes/dashboard.tsx +++ b/frontend/app/routes/dashboard.tsx @@ -24,8 +24,6 @@ export async function loader({ request }: LoaderFunctionArgs) { export default function Dashboard() { const { user, games } = useLoaderData<typeof loader>()!; - const isOnlineQualifyingOpen = new Date() < new Date(2025, 2, 21, 16, 30); - return ( <div className="p-6 bg-gray-100 min-h-screen flex flex-col items-center gap-4"> {user.icon_path && ( @@ -36,23 +34,6 @@ export default function Dashboard() { /> )} <h1 className="text-3xl font-bold text-gray-800">{user.display_name}</h1> - {isOnlineQualifyingOpen && ( - <BorderedContainerWithCaption caption="オンライン予選開催中 (3/21 決勝当日まで)"> - <p className="text-gray-900 max-w-prose"> - 現在オンライン予選を開催中です。 - 予選問題2問を両方解いたプレイヤーのうち合計スコアが最も小さい2名が、3/21 - (金) の PHPerKaigi day0 に実施される決勝戦への進出枠を獲得します。 - 当日は、会場の Track A まで是非お越しください! - </p> - <p className="text-gray-600 max-w-prose"> - ※ 当日会場 Track A - にいらっしゃらない場合、次点のスコアを獲得されている方が自動的に決勝進出となります。 - </p> - <p className="text-gray-600 max-w-prose"> - ※ 決勝に参加する予定のない方でも、プレイしていただくことは可能です。 - </p> - </BorderedContainerWithCaption> - )} <BorderedContainerWithCaption caption="試合一覧"> <div className="px-4"> {games.length === 0 ? ( diff --git a/frontend/package-lock.json b/frontend/package-lock.json index a4ae09f..9bcfa0a 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -6,6 +6,7 @@ "": { "name": "phperkaigi-2025-albatross-frontend", "dependencies": { + "@base-ui-components/react": "^1.0.0-alpha.7", "@fortawesome/fontawesome-svg-core": "^6.7.2", "@fortawesome/free-solid-svg-icons": "^6.7.2", "@fortawesome/react-fontawesome": "^0.2.2", @@ -474,10 +475,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.9.tgz", - "integrity": "sha512-aA63XwOkcl4xxQa3HjPMqOP6LiK0ZDv3mUPYEFXkpHbaFjtGggE1A61FjFzJnB+p7/oy2gA8E+rcBNl/zC1tMg==", - "devOptional": true, + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz", + "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==", "license": "MIT", "dependencies": { "regenerator-runtime": "^0.14.0" @@ -540,6 +540,37 @@ "node": ">=6.9.0" } }, + "node_modules/@base-ui-components/react": { + "version": "1.0.0-alpha.7", + "resolved": "https://registry.npmjs.org/@base-ui-components/react/-/react-1.0.0-alpha.7.tgz", + "integrity": "sha512-M4MxUvwAfERYSMqrUmTSf4J5yHXXGHqXw5wE7WLz8eCIhFmVWog4j5hYRU6QZ0VhqlyVPhs9KqSnts+FFyPmew==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.26.10", + "@floating-ui/react": "^0.27.5", + "@floating-ui/utils": "^0.2.9", + "@react-aria/overlays": "^3.26.1", + "prop-types": "^15.8.1", + "use-sync-external-store": "^1.4.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17 || ^18 || ^19", + "react": "^17 || ^18 || ^19", + "react-dom": "^17 || ^18 || ^19" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@biomejs/biome": { "version": "1.9.4", "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-1.9.4.tgz", @@ -1482,7 +1513,6 @@ "version": "1.6.9", "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.9.tgz", "integrity": "sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw==", - "dev": true, "license": "MIT", "dependencies": { "@floating-ui/utils": "^0.2.9" @@ -1492,18 +1522,31 @@ "version": "1.6.13", "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.13.tgz", "integrity": "sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w==", - "dev": true, "license": "MIT", "dependencies": { "@floating-ui/core": "^1.6.0", "@floating-ui/utils": "^0.2.9" } }, + "node_modules/@floating-ui/react": { + "version": "0.27.5", + "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.27.5.tgz", + "integrity": "sha512-BX3jKxo39Ba05pflcQmqPPwc0qdNsdNi/eweAFtoIdrJWNen2sVEWMEac3i6jU55Qfx+lOcdMNKYn2CtWmlnOQ==", + "license": "MIT", + "dependencies": { + "@floating-ui/react-dom": "^2.1.2", + "@floating-ui/utils": "^0.2.9", + "tabbable": "^6.0.0" + }, + "peerDependencies": { + "react": ">=17.0.0", + "react-dom": ">=17.0.0" + } + }, "node_modules/@floating-ui/react-dom": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.2.tgz", "integrity": "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==", - "dev": true, "license": "MIT", "dependencies": { "@floating-ui/dom": "^1.0.0" @@ -1517,9 +1560,59 @@ "version": "0.2.9", "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.9.tgz", "integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==", - "dev": true, "license": "MIT" }, + "node_modules/@formatjs/ecma402-abstract": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.3.4.tgz", + "integrity": "sha512-qrycXDeaORzIqNhBOx0btnhpD1c+/qFIHAN9znofuMJX6QBwtbrmlpWfD4oiUUD2vJUOIYFA/gYtg2KAMGG7sA==", + "license": "MIT", + "dependencies": { + "@formatjs/fast-memoize": "2.2.7", + "@formatjs/intl-localematcher": "0.6.1", + "decimal.js": "^10.4.3", + "tslib": "^2.8.0" + } + }, + "node_modules/@formatjs/fast-memoize": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.7.tgz", + "integrity": "sha512-Yabmi9nSvyOMrlSeGGWDiH7rf3a7sIwplbvo/dlz9WCIjzIQAfy1RMf4S0X3yG724n5Ghu2GmEl5NJIV6O9sZQ==", + "license": "MIT", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@formatjs/icu-messageformat-parser": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.11.2.tgz", + "integrity": "sha512-AfiMi5NOSo2TQImsYAg8UYddsNJ/vUEv/HaNqiFjnI3ZFfWihUtD5QtuX6kHl8+H+d3qvnE/3HZrfzgdWpsLNA==", + "license": "MIT", + "dependencies": { + "@formatjs/ecma402-abstract": "2.3.4", + "@formatjs/icu-skeleton-parser": "1.8.14", + "tslib": "^2.8.0" + } + }, + "node_modules/@formatjs/icu-skeleton-parser": { + "version": "1.8.14", + "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.14.tgz", + "integrity": "sha512-i4q4V4qslThK4Ig8SxyD76cp3+QJ3sAqr7f6q9VVfeGtxG9OhiAk3y9XF6Q41OymsKzsGQ6OQQoJNY4/lI8TcQ==", + "license": "MIT", + "dependencies": { + "@formatjs/ecma402-abstract": "2.3.4", + "tslib": "^2.8.0" + } + }, + "node_modules/@formatjs/intl-localematcher": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.6.1.tgz", + "integrity": "sha512-ePEgLgVCqi2BBFnTMWPfIghu6FkbZnnBVhO2sSxvLfrdFw7wCHAHiDoM2h4NRgjbaY7+B7HgOLZGkK187pZTZg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.8.0" + } + }, "node_modules/@fortawesome/fontawesome-common-types": { "version": "6.7.2", "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.7.2.tgz", @@ -1632,6 +1725,43 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@internationalized/date": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.7.0.tgz", + "integrity": "sha512-VJ5WS3fcVx0bejE/YHfbDKR/yawZgKqn/if+oEeLqNwBtPzVB06olkfcnojTmEMX+gTpH+FlQ69SHNitJ8/erQ==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0" + } + }, + "node_modules/@internationalized/message": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@internationalized/message/-/message-3.1.6.tgz", + "integrity": "sha512-JxbK3iAcTIeNr1p0WIFg/wQJjIzJt9l/2KNY/48vXV7GRGZSv3zMxJsce008fZclk2cDC8y0Ig3odceHO7EfNQ==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0", + "intl-messageformat": "^10.1.0" + } + }, + "node_modules/@internationalized/number": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@internationalized/number/-/number-3.6.0.tgz", + "integrity": "sha512-PtrRcJVy7nw++wn4W2OuePQQfTqDzfusSuY1QTtui4wa7r+rGVtR75pO8CyKvHvzyQYi3Q1uO5sY0AsB4e65Bw==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0" + } + }, + "node_modules/@internationalized/string": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/@internationalized/string/-/string-3.2.5.tgz", + "integrity": "sha512-rKs71Zvl2OKOHM+mzAFMIyqR5hI1d1O6BBkMK2/lkfg3fkmVh9Eeg0awcA8W2WqYqDOv6a86DIOlFpggwLtbuw==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -2433,6 +2563,132 @@ "dev": true, "license": "MIT" }, + "node_modules/@react-aria/focus": { + "version": "3.20.1", + "resolved": "https://registry.npmjs.org/@react-aria/focus/-/focus-3.20.1.tgz", + "integrity": "sha512-lgYs+sQ1TtBrAXnAdRBQrBo0/7o5H6IrfDxec1j+VRpcXL0xyk0xPq+m3lZp8typzIghqDgpnKkJ5Jf4OrzPIw==", + "license": "Apache-2.0", + "dependencies": { + "@react-aria/interactions": "^3.24.1", + "@react-aria/utils": "^3.28.1", + "@react-types/shared": "^3.28.0", + "@swc/helpers": "^0.5.0", + "clsx": "^2.0.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-aria/i18n": { + "version": "3.12.7", + "resolved": "https://registry.npmjs.org/@react-aria/i18n/-/i18n-3.12.7.tgz", + "integrity": "sha512-eLbYO2xrpeOKIEmLv2KD5LFcB0wltFqS+pUjsOzkKZg6H3b6AFDmJPxr/a0x2KGHtpGJvuHwCSbpPi9PzSSQLg==", + "license": "Apache-2.0", + "dependencies": { + "@internationalized/date": "^3.7.0", + "@internationalized/message": "^3.1.6", + "@internationalized/number": "^3.6.0", + "@internationalized/string": "^3.2.5", + "@react-aria/ssr": "^3.9.7", + "@react-aria/utils": "^3.28.1", + "@react-types/shared": "^3.28.0", + "@swc/helpers": "^0.5.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-aria/interactions": { + "version": "3.24.1", + "resolved": "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.24.1.tgz", + "integrity": "sha512-OWEcIC6UQfWq4Td5Ptuh4PZQ4LHLJr/JL2jGYvuNL6EgL3bWvzPrRYIF/R64YbfVxIC7FeZpPSkS07sZ93/NoA==", + "license": "Apache-2.0", + "dependencies": { + "@react-aria/ssr": "^3.9.7", + "@react-aria/utils": "^3.28.1", + "@react-stately/flags": "^3.1.0", + "@react-types/shared": "^3.28.0", + "@swc/helpers": "^0.5.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-aria/overlays": { + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/@react-aria/overlays/-/overlays-3.26.1.tgz", + "integrity": "sha512-AtQ0mp+H0alFFkojKBADEUIc1AKFsSobH4QNoxQa3V4bZKQoXxga7cRhD5RRYanu3XCQOkIxZJ3vdVK/LVVBXA==", + "license": "Apache-2.0", + "dependencies": { + "@react-aria/focus": "^3.20.1", + "@react-aria/i18n": "^3.12.7", + "@react-aria/interactions": "^3.24.1", + "@react-aria/ssr": "^3.9.7", + "@react-aria/utils": "^3.28.1", + "@react-aria/visually-hidden": "^3.8.21", + "@react-stately/overlays": "^3.6.14", + "@react-types/button": "^3.11.0", + "@react-types/overlays": "^3.8.13", + "@react-types/shared": "^3.28.0", + "@swc/helpers": "^0.5.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-aria/ssr": { + "version": "3.9.7", + "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.7.tgz", + "integrity": "sha512-GQygZaGlmYjmYM+tiNBA5C6acmiDWF52Nqd40bBp0Znk4M4hP+LTmI0lpI1BuKMw45T8RIhrAsICIfKwZvi2Gg==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-aria/utils": { + "version": "3.28.1", + "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.28.1.tgz", + "integrity": "sha512-mnHFF4YOVu9BRFQ1SZSKfPhg3z+lBRYoW5mLcYTQihbKhz48+I1sqRkP7ahMITr8ANH3nb34YaMME4XWmK2Mgg==", + "license": "Apache-2.0", + "dependencies": { + "@react-aria/ssr": "^3.9.7", + "@react-stately/flags": "^3.1.0", + "@react-stately/utils": "^3.10.5", + "@react-types/shared": "^3.28.0", + "@swc/helpers": "^0.5.0", + "clsx": "^2.0.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-aria/visually-hidden": { + "version": "3.8.21", + "resolved": "https://registry.npmjs.org/@react-aria/visually-hidden/-/visually-hidden-3.8.21.tgz", + "integrity": "sha512-iii5qO+cVHrHiOeiBYCnTRUQG2eOgEPFmiMG4dAuby8+pJJ8U4BvffX2sDTYWL6ztLLBYyrsUHPSw1Ld03JhmA==", + "license": "Apache-2.0", + "dependencies": { + "@react-aria/interactions": "^3.24.1", + "@react-aria/utils": "^3.28.1", + "@react-types/shared": "^3.28.0", + "@swc/helpers": "^0.5.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, "node_modules/@react-router/dev": { "version": "7.3.0", "resolved": "https://registry.npmjs.org/@react-router/dev/-/dev-7.3.0.tgz", @@ -2583,6 +2839,74 @@ "react-router": "7.3.0" } }, + "node_modules/@react-stately/flags": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@react-stately/flags/-/flags-3.1.0.tgz", + "integrity": "sha512-KSHOCxTFpBtxhIRcKwsD1YDTaNxFtCYuAUb0KEihc16QwqZViq4hasgPBs2gYm7fHRbw7WYzWKf6ZSo/+YsFlg==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0" + } + }, + "node_modules/@react-stately/overlays": { + "version": "3.6.14", + "resolved": "https://registry.npmjs.org/@react-stately/overlays/-/overlays-3.6.14.tgz", + "integrity": "sha512-RRalTuHdwrKO1BmXKaqBtE1GGUXU4VUAWwgh4lsP2EFSixDHmOVLxHFDWYvOPChBhpi8KXfLEgm6DEgPBvLBZQ==", + "license": "Apache-2.0", + "dependencies": { + "@react-stately/utils": "^3.10.5", + "@react-types/overlays": "^3.8.13", + "@swc/helpers": "^0.5.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-stately/utils": { + "version": "3.10.5", + "resolved": "https://registry.npmjs.org/@react-stately/utils/-/utils-3.10.5.tgz", + "integrity": "sha512-iMQSGcpaecghDIh3mZEpZfoFH3ExBwTtuBEcvZ2XnGzCgQjeYXcMdIUwAfVQLXFTdHUHGF6Gu6/dFrYsCzySBQ==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-types/button": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/@react-types/button/-/button-3.11.0.tgz", + "integrity": "sha512-gJh5i0JiBiZGZGDo+tXMp6xbixPM7IKZ0sDuxTYBG49qNzzWJq0uNYltO3emwSVXFSsBgRV/Wu8kQGhfuN7wIw==", + "license": "Apache-2.0", + "dependencies": { + "@react-types/shared": "^3.28.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-types/overlays": { + "version": "3.8.13", + "resolved": "https://registry.npmjs.org/@react-types/overlays/-/overlays-3.8.13.tgz", + "integrity": "sha512-xgT843KIh1otvYPQ6kCGTVUICiMF5UQ7SZUQZd4Zk3VtiFIunFVUvTvL03cpt0026UmY7tbv7vFrPKcT6xjsjw==", + "license": "Apache-2.0", + "dependencies": { + "@react-types/shared": "^3.28.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-types/shared": { + "version": "3.28.0", + "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.28.0.tgz", + "integrity": "sha512-9oMEYIDc3sk0G5rysnYvdNrkSg7B04yTKl50HHSZVbokeHpnU0yRmsDaWb9B/5RprcKj8XszEk5guBO8Sa/Q+Q==", + "license": "Apache-2.0", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, "node_modules/@redocly/ajv": { "version": "8.11.2", "resolved": "https://registry.npmjs.org/@redocly/ajv/-/ajv-8.11.2.tgz", @@ -2963,6 +3287,15 @@ "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", "license": "MIT" }, + "node_modules/@swc/helpers": { + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", + "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, "node_modules/@tailwindcss/node": { "version": "4.0.12", "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.0.12.tgz", @@ -4262,7 +4595,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -4769,6 +5101,12 @@ } } }, + "node_modules/decimal.js": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.5.0.tgz", + "integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==", + "license": "MIT" + }, "node_modules/decode-named-character-reference": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.1.0.tgz", @@ -6552,6 +6890,18 @@ "node": ">= 0.4" } }, + "node_modules/intl-messageformat": { + "version": "10.7.16", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.7.16.tgz", + "integrity": "sha512-UmdmHUmp5CIKKjSoE10la5yfU+AYJAaiYLsodbjL4lji83JNvgOQUjGaGhGrpFCb0Uh7sl7qfP1IyILa8Z40ug==", + "license": "BSD-3-Clause", + "dependencies": { + "@formatjs/ecma402-abstract": "2.3.4", + "@formatjs/fast-memoize": "2.2.7", + "@formatjs/icu-messageformat-parser": "2.11.2", + "tslib": "^2.8.0" + } + }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -9480,7 +9830,6 @@ "version": "0.14.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", - "devOptional": true, "license": "MIT" }, "node_modules/regex": { @@ -10436,6 +10785,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/tabbable": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", + "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==", + "license": "MIT" + }, "node_modules/tailwindcss": { "version": "4.0.12", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.12.tgz", @@ -10523,7 +10878,6 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, "license": "0BSD" }, "node_modules/turbo-stream": { @@ -10918,6 +11272,15 @@ } } }, + "node_modules/use-sync-external-store": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz", + "integrity": "sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", diff --git a/frontend/package.json b/frontend/package.json index 5fcb574..d692339 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -15,6 +15,7 @@ "start": "react-router-serve ./build/server/index.js" }, "dependencies": { + "@base-ui-components/react": "^1.0.0-alpha.7", "@fortawesome/fontawesome-svg-core": "^6.7.2", "@fortawesome/free-solid-svg-icons": "^6.7.2", "@fortawesome/react-fontawesome": "^0.2.2", diff --git a/openapi/api-server.yaml b/openapi/api-server.yaml index e9915bf..8fd2874 100644 --- a/openapi/api-server.yaml +++ b/openapi/api-server.yaml @@ -402,7 +402,12 @@ components: type: integer example: 946684800 x-go-type: int64 + code: + type: string + nullable: true + example: "echo 'hello world';" required: - player - score - submitted_at + - code |
