feat(web): add bgm package browser
This commit is contained in:
@@ -2,6 +2,8 @@ import { Link, Route, Routes } from 'react-router-dom';
|
||||
import SearchPage from './pages/SearchPage';
|
||||
import ModPage from './pages/ModPage';
|
||||
import FullSetupPage from './pages/FullSetupPage';
|
||||
import BgmPage from './pages/BgmPage';
|
||||
import BgmPackagePage from './pages/BgmPackagePage';
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
@@ -15,6 +17,9 @@ export default function App() {
|
||||
<Link to="/" className="hover:text-zinc-100">
|
||||
Search
|
||||
</Link>
|
||||
<Link to="/bgm" className="hover:text-zinc-100">
|
||||
BGM
|
||||
</Link>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
@@ -23,6 +28,8 @@ export default function App() {
|
||||
<Route path="/" element={<SearchPage />} />
|
||||
<Route path="/mod/:id" element={<ModPage />} />
|
||||
<Route path="/mod/:id/setup" element={<FullSetupPage />} />
|
||||
<Route path="/bgm" element={<BgmPage />} />
|
||||
<Route path="/bgm/:id" element={<BgmPackagePage />} />
|
||||
</Routes>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
import { Link, useParams } from 'react-router-dom';
|
||||
import packages from 'virtual:bgm/packages';
|
||||
|
||||
/**
|
||||
* Detail view for a single bgm package, looked up by id from the
|
||||
* `virtual:bgm/packages` module's list of all discovered packages.
|
||||
*/
|
||||
export default function BgmPackagePage() {
|
||||
const { id } = useParams<{ id: string }>();
|
||||
const pkg = packages.find((p) => p.meta.id === id);
|
||||
|
||||
if (!pkg) {
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<p className="text-sm text-zinc-400">No package “{id}”.</p>
|
||||
<Link to="/bgm" className="text-sm text-zinc-300 underline">
|
||||
Back to all packages
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const parts = Object.values(pkg.parts);
|
||||
const surfaces = Object.values(pkg.surfaces);
|
||||
const setups = Object.values(pkg.setups);
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<Link to="/bgm" className="text-sm text-zinc-400 hover:text-zinc-200">
|
||||
← All packages
|
||||
</Link>
|
||||
<h1 className="mt-1 text-2xl font-semibold">{pkg.meta.title ?? pkg.meta.id}</h1>
|
||||
<p className="mt-1 text-sm text-zinc-400">
|
||||
{pkg.meta.designer ? `${pkg.meta.designer} · ` : ''}
|
||||
{pkg.meta.players ? `${pkg.meta.players} players · ` : ''}
|
||||
{pkg.meta.language}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<section className="space-y-2">
|
||||
<h2 className="text-lg font-semibold">Parts</h2>
|
||||
<ul className="grid gap-2 sm:grid-cols-2">
|
||||
{parts.map((part) => (
|
||||
<li key={`${part.type}#${part.id}`} className="rounded-lg border border-zinc-800 bg-zinc-900 p-3">
|
||||
<div className="font-medium">
|
||||
{part.type}#{part.id}
|
||||
</div>
|
||||
<div className="mt-1 text-xs text-zinc-400">
|
||||
{part.size ? `size ${part.size.join('×')}mm` : ''}
|
||||
{part.fillet ? ` · fillet ${part.fillet}mm` : ''}
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section className="space-y-2">
|
||||
<h2 className="text-lg font-semibold">Surfaces</h2>
|
||||
<ul className="grid gap-2 sm:grid-cols-2">
|
||||
{surfaces.map((surface) => (
|
||||
<li key={`${surface.type}#${surface.id}`} className="rounded-lg border border-zinc-800 bg-zinc-900 p-3">
|
||||
<div className="font-medium">
|
||||
{surface.type}#{surface.id}
|
||||
</div>
|
||||
<div className="mt-1 text-xs text-zinc-400">
|
||||
{surface.size ? `size ${surface.size.join('×')}mm` : ''} · {surface.layout.length} routes
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section className="space-y-2">
|
||||
<h2 className="text-lg font-semibold">Setups</h2>
|
||||
{setups.map((setup) => (
|
||||
<div key={`${setup.type}#${setup.id}`} className="rounded-lg border border-zinc-800 bg-zinc-900 p-3">
|
||||
<div className="font-medium">
|
||||
{setup.type}#{setup.id}
|
||||
</div>
|
||||
<pre className="mt-2 overflow-x-auto text-xs text-zinc-400">
|
||||
{JSON.stringify(setup.setup, null, 2)}
|
||||
</pre>
|
||||
</div>
|
||||
))}
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import { Link } from 'react-router-dom';
|
||||
import packages from 'virtual:bgm/packages';
|
||||
|
||||
/**
|
||||
* Lists every discovered bgm package. The `virtual:bgm/packages` module's
|
||||
* default export is an array of all packages the loader found in the games
|
||||
* root.
|
||||
*/
|
||||
export default function BgmPage() {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<h1 className="text-2xl font-semibold">Board games</h1>
|
||||
<p className="mt-1 text-sm text-zinc-400">
|
||||
{packages.length} package{packages.length === 1 ? '' : 's'} discovered.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<ul className="grid gap-2 sm:grid-cols-2">
|
||||
{packages.map((pkg) => (
|
||||
<li key={pkg.meta.id}>
|
||||
<Link
|
||||
to={`/bgm/${pkg.meta.id}`}
|
||||
className="block rounded-lg border border-zinc-800 bg-zinc-900 p-3 hover:border-zinc-600"
|
||||
>
|
||||
<div className="font-medium">{pkg.meta.title ?? pkg.meta.id}</div>
|
||||
<div className="mt-1 text-xs text-zinc-400">
|
||||
{pkg.meta.designer ? `${pkg.meta.designer} · ` : ''}
|
||||
{pkg.meta.players ? `${pkg.meta.players} players · ` : ''}
|
||||
{pkg.meta.language}
|
||||
</div>
|
||||
<div className="mt-1 text-xs text-zinc-500">
|
||||
{Object.keys(pkg.parts).length} parts · {Object.keys(pkg.surfaces).length} surfaces ·{' '}
|
||||
{Object.keys(pkg.setups).length} setups
|
||||
</div>
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Vendored
+19
-1
@@ -1 +1,19 @@
|
||||
/// <reference types="vite/client" />
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
/**
|
||||
* Ambient types for the virtual `bgm` modules served by the bgm vite plugin.
|
||||
*
|
||||
* - `virtual:bgm/packages` — every discovered package.
|
||||
* - `virtual:bgm/package/<id>` — a single package's assembled JSON.
|
||||
*/
|
||||
declare module 'virtual:bgm/packages' {
|
||||
import type { SerializedPackage } from '@tts/bgm';
|
||||
const packages: SerializedPackage[];
|
||||
export default packages;
|
||||
}
|
||||
|
||||
declare module 'virtual:bgm/package/*' {
|
||||
import type { SerializedPackage } from '@tts/bgm';
|
||||
const pkg: SerializedPackage;
|
||||
export default pkg;
|
||||
}
|
||||
Reference in New Issue
Block a user