full e2e behavior, nice

This commit is contained in:
2025-08-08 00:04:46 -04:00
parent 9d4b17b762
commit eb064273ed
11 changed files with 117 additions and 27 deletions

View File

@@ -35,6 +35,10 @@ const App = () => (
path="/:game" path="/:game"
component={lazy(() => import("./routes/[game]/index"))} component={lazy(() => import("./routes/[game]/index"))}
/> />
<Route
path="/:game/:instance"
component={lazy(() => import("./routes/[game]/[instance]"))}
/>
</Router> </Router>
); );

View File

@@ -1,6 +1,7 @@
import { import {
Accessor, Accessor,
createContext, createContext,
createEffect,
createResource, createResource,
JSX, JSX,
Show, Show,
@@ -11,7 +12,7 @@ import Hand from "./Hand";
import Pile from "./Pile"; import Pile from "./Pile";
import { GameState, newDeck, shuffle, Hand as THand } from "../types/cards"; import { GameState, newDeck, shuffle, Hand as THand } from "../types/cards";
import { createStore, produce, SetStoreFunction, Store } from "solid-js/store"; import { createStore, produce, SetStoreFunction, Store } from "solid-js/store";
import { getGameState, updateGameState } from "../db/Instances"; import api from "../api";
export const GameContext = createContext<{ export const GameContext = createContext<{
gameState: Accessor<GameState | undefined>; gameState: Accessor<GameState | undefined>;
@@ -19,17 +20,24 @@ export const GameContext = createContext<{
}>(); }>();
export default (props: { instanceId: number }) => { export default (props: { instanceId: number }) => {
const [gameState, { refetch }] = createResource(() => const [gameState, { refetch }] = createResource<GameState>(() =>
getGameState(props.instanceId) api.gameState[":gameId"]
.$get({ param: { gameId: props.instanceId.toString() } })
.then((res) => res.json())
); );
const setGameState = (state: GameState) => const setGameState = (state: GameState) =>
updateGameState(props.instanceId, state).then(refetch); api.gameState[":gameId"]
.$put({
param: { gameId: props.instanceId.toString() },
json: state,
})
.then(refetch);
return ( return (
<GameContext.Provider value={{ gameState, setGameState }}> <GameContext.Provider value={{ gameState, setGameState }}>
<Show when={gameState() != undefined}> <Show when={gameState() != undefined}>
<div <div
onClick={() => {}}
class="full column center" class="full column center"
style={{ "row-gap": "20px", "font-size": "32px" }} style={{ "row-gap": "20px", "font-size": "32px" }}
> >

View File

@@ -2,7 +2,6 @@ import { A, useParams } from "@solidjs/router";
import { createEffect, createResource, Show, Suspense } from "solid-js"; import { createEffect, createResource, Show, Suspense } from "solid-js";
import Game from "../../components/Game"; import Game from "../../components/Game";
import { getGameState } from "../../db/Instances";
export default () => { export default () => {
const params = useParams<{ game: string; instance: string }>(); const params = useParams<{ game: string; instance: string }>();

View File

@@ -1,32 +1,27 @@
import { A, useParams } from "@solidjs/router"; import { A, useParams } from "@solidjs/router";
import { createEffect, createResource, For, Suspense } from "solid-js"; import { createEffect, createResource, For, Suspense } from "solid-js";
import * as Instance from "../../db/Instances"; import api from "../../api";
export default () => { export default () => {
const params = useParams<{ game: string }>(); const param = useParams<{ game: string }>();
const [instances, { refetch }] = createResource( const [instances, { refetch }] = createResource(
() => params.game, () => param.game,
() => Instance.queryInstances(params.game) async () =>
api.instances.$get({ query: param }).then((res) => res.json())
); );
return ( return (
<Suspense> <Suspense>
<div style={{ padding: "20px" }}> <div style={{ padding: "20px" }}>
<h1 style={{ margin: 0 }}>{params.game}</h1> <h1 style={{ margin: 0 }}>{param.game}</h1>
<button <button onClick={() => null}>New Game</button>
onClick={() =>
Instance.createInstance(params.game).then(refetch)
}
>
New Game
</button>
<ul> <ul>
<For each={instances() ?? []}> <For each={instances() ?? []}>
{(instance) => ( {(instance) => (
<li> <li>
<A href={`/${params.game}/${instance.id}`}> <A href={`/${param.game}/${instance.id}`}>
{instance.id} {instance.id}
</A> </A>
</li> </li>

View File

@@ -1,19 +1,17 @@
import { A } from "@solidjs/router"; import { A } from "@solidjs/router";
import { createEffect, createResource, For } from "solid-js"; import { createEffect, createResource, For } from "solid-js";
import * as Games from "../db/Games";
import api from "../api"; import api from "../api";
export default () => { export default () => {
const [ping] = createResource(async () => const [games] = createResource(async () =>
api.ping.$get().then(async (res) => await res.text()) api.games.$get().then((res) => res.json())
); );
return ( return (
<div style={{ padding: "20px" }}> <div style={{ padding: "20px" }}>
{ping()} <For each={games()}>
{/* <For each={games()}>
{(game) => <A href={`/${game.name}`}>{game.name}</A>} {(game) => <A href={`/${game.name}`}>{game.name}</A>}
</For> */} </For>
</div> </div>
); );
}; };

View File

@@ -9,8 +9,10 @@
"start": "NODE_ENV=production bun run --port 5001 src/index.ts" "start": "NODE_ENV=production bun run --port 5001 src/index.ts"
}, },
"dependencies": { "dependencies": {
"@hono/zod-validator": "^0.7.2",
"@prisma/client": "6.13.0", "@prisma/client": "6.13.0",
"hono": "^4.8.12" "hono": "^4.8.12",
"zod": "^4.0.15"
}, },
"devDependencies": { "devDependencies": {
"@types/bun": "latest", "@types/bun": "latest",

View File

@@ -1,6 +1,68 @@
import { Hono } from "hono"; import { Hono } from "hono";
import { prisma } from "./db/db";
import { zValidator } from "@hono/zod-validator";
import { z } from "zod";
const api = new Hono().get("/ping", (c) => c.text("pong")); const api = new Hono()
.get("/ping", (c) => c.text("pong"))
.get("/games", async (c) => {
const games = await prisma.game.findMany();
return c.json(games);
})
.get(
"/instances",
zValidator("query", z.object({ game: z.string() })),
async (c) => {
const { game } = c.req.valid("query");
const instances = await prisma.instance.findMany({
where: {
game: {
name: game,
},
},
select: {
id: true,
},
});
return c.json(instances);
}
)
.get(
"/gameState/:gameId",
zValidator("param", z.object({ gameId: z.string() })),
async (c) => {
const { gameId } = c.req.valid("param");
const instance = await prisma.instance.findUnique({
where: {
id: Number(gameId),
},
});
return c.json(instance?.gameState);
}
)
.put(
"/gameState/:gameId",
zValidator("param", z.object({ gameId: z.string() }), (result) => {
console.log(JSON.stringify(result, null, 2));
}),
zValidator("json", z.any()),
async (c) => {
const { gameId } = c.req.valid("param");
const gameState = c.req.valid("json");
await prisma.instance.update({
data: { gameState },
where: {
id: Number(gameId),
},
});
return c.text("", 200);
}
);
export default api; export default api;
export type ApiType = typeof api; export type ApiType = typeof api;

22
pnpm-lock.yaml generated
View File

@@ -29,12 +29,18 @@ importers:
packages/server: packages/server:
dependencies: dependencies:
'@hono/zod-validator':
specifier: ^0.7.2
version: 0.7.2(hono@4.8.12)(zod@4.0.15)
'@prisma/client': '@prisma/client':
specifier: 6.13.0 specifier: 6.13.0
version: 6.13.0(prisma@6.13.0) version: 6.13.0(prisma@6.13.0)
hono: hono:
specifier: ^4.8.12 specifier: ^4.8.12
version: 4.8.12 version: 4.8.12
zod:
specifier: ^4.0.15
version: 4.0.15
devDependencies: devDependencies:
'@types/bun': '@types/bun':
specifier: latest specifier: latest
@@ -289,6 +295,12 @@ packages:
cpu: [x64] cpu: [x64]
os: [win32] os: [win32]
'@hono/zod-validator@0.7.2':
resolution: {integrity: sha512-ub5eL/NeZ4eLZawu78JpW/J+dugDAYhwqUIdp9KYScI6PZECij4Hx4UsrthlEUutqDDhPwRI0MscUfNkvn/mqQ==}
peerDependencies:
hono: '>=3.9.0'
zod: ^3.25.0 || ^4.0.0
'@jridgewell/gen-mapping@0.3.12': '@jridgewell/gen-mapping@0.3.12':
resolution: {integrity: sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==} resolution: {integrity: sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==}
@@ -958,6 +970,9 @@ packages:
resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
engines: {node: '>=12'} engines: {node: '>=12'}
zod@4.0.15:
resolution: {integrity: sha512-2IVHb9h4Mt6+UXkyMs0XbfICUh1eUrlJJAOupBHUhLRnKkruawyDddYRCs0Eizt900ntIMk9/4RksYl+FgSpcQ==}
snapshots: snapshots:
'@ampproject/remapping@2.3.0': '@ampproject/remapping@2.3.0':
@@ -1154,6 +1169,11 @@ snapshots:
'@esbuild/win32-x64@0.25.8': '@esbuild/win32-x64@0.25.8':
optional: true optional: true
'@hono/zod-validator@0.7.2(hono@4.8.12)(zod@4.0.15)':
dependencies:
hono: 4.8.12
zod: 4.0.15
'@jridgewell/gen-mapping@0.3.12': '@jridgewell/gen-mapping@0.3.12':
dependencies: dependencies:
'@jridgewell/sourcemap-codec': 1.5.4 '@jridgewell/sourcemap-codec': 1.5.4
@@ -1778,3 +1798,5 @@ snapshots:
string-width: 4.2.3 string-width: 4.2.3
y18n: 5.0.8 y18n: 5.0.8
yargs-parser: 21.1.1 yargs-parser: 21.1.1
zod@4.0.15: {}