game systems: game system pages, game system create

components: moved DevTool to client component
This commit is contained in:
Emmaline Autumn 2024-03-19 11:20:15 -06:00
parent 50e5ff0663
commit 56f0442d33
10 changed files with 200 additions and 21 deletions

View File

@ -0,0 +1,15 @@
"use server";
import { prisma } from "@/prisma/prismaClient";
export const createGameSystem = async (name: string) => {
const { id } = await prisma.gameSystem.create({
data: {
name,
},
select: {
id: true,
},
});
return id;
};

View File

@ -0,0 +1,7 @@
"use server";
import { prisma } from "@/prisma/prismaClient";
// DEV TOOL ONLY
export async function deleteAllGameSystems() {
await prisma.gameSystem.deleteMany();
}

View File

@ -0,0 +1,49 @@
import { Sticky } from "@/lib/sticky";
import { prisma } from "@/prisma/prismaClient";
export default async function GameSystem(
{ params: { id } }: { params: { id: string } },
) {
if (!id) throw "HOW DID YOU GET HERE?";
const gameSystem = await prisma.gameSystem.findFirst({
where: {
id,
},
select: {
id: true,
name: true,
schemas: {
select: {
name: true,
id: true,
publications: {
select: {
name: true,
id: true,
},
},
},
},
},
});
return (
<>
<section className="heading">
<h2 className="strapline">Game System</h2>
<h1>{gameSystem?.name}</h1>
</section>
<section>
<ul>
{gameSystem?.schemas.map((schema) => (
<li key={schema.id}>{schema.name}</li>
))}
</ul>
</section>
<Sticky sidedness={-1}>
<h1>HELLO!</h1>
</Sticky>
</>
);
}

View File

@ -0,0 +1,26 @@
"use client";
import { deleteAllGameSystems } from "@/actions/GameSystems/deleteAll";
import { DevTool } from "@/components/devtools/DevTool";
import { useRouter } from "next/navigation";
import { FC, PropsWithChildren } from "react";
export const GameSystemsClient: FC<PropsWithChildren> = ({ children }) => {
const router = useRouter();
// DEV TOOL ONLY
async function deleteAll() {
await deleteAllGameSystems();
router.refresh();
}
return (
<>
<DevTool id="game-system-home">
<button onClick={deleteAll} className="btn-primary bg-lime-600">
Delete All Game Systems
</button>
</DevTool>
{children}
</>
);
};

View File

@ -0,0 +1,35 @@
import { prisma } from "@/prisma/prismaClient";
import { redirect } from "next/navigation";
export default function CreateGameSystem() {
async function create(form: FormData) {
"use server";
const name = form.get("name")?.toString();
if (!name) return;
const { id } = await prisma.gameSystem.create({
data: {
name,
},
select: {
id: true,
},
});
redirect(`/game-systems/${id}`);
}
return (
<form action={create}>
<input
type="text"
// {...bind}
name="name"
placeholder="Create a new game system..."
className="w-min"
/>
<button className="btn-primary p-2 px-2 ml-2" type="submit">
Create
</button>
</form>
);
}

33
app/game-systems/page.tsx Normal file
View File

@ -0,0 +1,33 @@
import { prisma } from "@/prisma/prismaClient";
import CreateGameSystem from "./create";
import { GameSystemsClient } from "./client";
import Link from "next/link";
export default async function GameSystems() {
const existingGameSystems = await prisma.gameSystem.findMany({
orderBy: {
created: "asc",
},
});
return (
<GameSystemsClient>
<section className="heading">
<h2 className="strapline">Tabletop Commander</h2>
<h1>Game Systems</h1>
</section>
<section className="mb-6">
<CreateGameSystem />
</section>
<section className="">
<ul>
{existingGameSystems.map((g) => (
<li key={g.id} className="odd:bg-black/20 p-2 text-lg">
<Link href={`/game-systems/${g.id}`}>{g.name}</Link>
</li>
))}
</ul>
</section>
</GameSystemsClient>
);
}

View File

@ -0,0 +1,16 @@
"use client";
import { FC, PropsWithChildren, use, useEffect } from "react";
import { DevToolboxContext } from "./context";
export const DevTool: FC<PropsWithChildren<{ id: string }>> = (
{ children, id },
) => {
const { addTool, removeTool } = use(DevToolboxContext);
useEffect(() => {
addTool(id, children);
(() => removeTool(id));
}, [addTool, children, id, removeTool]);
return <></>;
};

View File

@ -1,5 +1,5 @@
import { Portal } from "@/lib/portal/components"; import { Portal } from "@/lib/portal/components";
import { FC, PropsWithChildren, use, useEffect, useState } from "react"; import { FC, use, useState } from "react";
import { DevToolboxContext } from "./context"; import { DevToolboxContext } from "./context";
import { WrenchScrewdriverIcon } from "@heroicons/react/24/solid"; import { WrenchScrewdriverIcon } from "@heroicons/react/24/solid";
import { XMarkIcon } from "@heroicons/react/16/solid"; import { XMarkIcon } from "@heroicons/react/16/solid";
@ -38,15 +38,3 @@ export const DevToolbox: FC = () => {
) )
: <></>; : <></>;
}; };
export const DevTool: FC<PropsWithChildren<{ id: string }>> = (
{ children, id },
) => {
const { addTool, removeTool } = use(DevToolboxContext);
useEffect(() => {
addTool(id, children);
(() => removeTool(id));
}, [addTool, children, id, removeTool]);
return <></>;
};

View File

@ -9,20 +9,17 @@ interface IProps {
export const Portal: FC<PropsWithChildren<IProps>> = ( export const Portal: FC<PropsWithChildren<IProps>> = (
{ children, className = "root-portal", el = "div" }, { children, className = "root-portal", el = "div" },
) => { ) => {
const [container] = useState(() => { const [container, setContainer] = useState<HTMLElement>();
// This will be executed only on the initial render
// https://reactjs.org/docs/hooks-reference.html#lazy-initial-state
return document.createElement(el);
});
// todo: this smells. appending the same element?
useEffect(() => { useEffect(() => {
const container = document.createElement(el);
container.classList.add(className); container.classList.add(className);
document.body.appendChild(container); document.body.appendChild(container);
setContainer(container);
return () => { return () => {
document.body.removeChild(container); document.body.removeChild(container);
}; };
}, [className, container]); }, [className, el]);
return createPortal(children, container); return container && createPortal(children, container);
}; };

13
prisma/prismaClient.ts Normal file
View File

@ -0,0 +1,13 @@
import { PrismaClient } from "@prisma/client";
const prismaClientSingleton = () => {
return new PrismaClient();
};
declare global {
var prismaGlobal: undefined | ReturnType<typeof prismaClientSingleton>;
}
export const prisma = globalThis.prismaGlobal ?? prismaClientSingleton();
if (process.env.NODE_ENV !== "production") globalThis.prismaGlobal = prisma;