Compare commits

...

3 Commits

Author SHA1 Message Date
Yanick Champoux 712b0a5ef2 Merge branch 'stats' 2024-02-06 15:40:56 -05:00
Yanick Champoux 23359b86ee add the stats and the menu 2024-02-06 15:40:47 -05:00
Yanick Champoux 2bbeefa26a add listing stats 2024-02-06 13:05:52 -05:00
8 changed files with 143 additions and 32 deletions

View File

@ -17,8 +17,7 @@
"devDependencies": {
"@changesets/cli": "^2.27.1",
"@playwright/test": "^1.28.1",
"@sveltejs/adapter-auto": "^3.0.0",
"@sveltejs/kit": "^2.0.0",
"@sveltejs/kit": "^2.5.0",
"@sveltejs/vite-plugin-svelte": "^3.0.0",
"@types/eslint": "8.56.0",
"eslint": "^8.56.0",
@ -34,7 +33,6 @@
},
"type": "module",
"dependencies": {
"@sveltejs/adapter-node": "^4.0.1",
"@sveltejs/adapter-static": "^3.0.1",
"@vincjo/datatables": "^1.14.4",
"@yanick/updeep-remeda": "^2.2.0",

View File

@ -0,0 +1,13 @@
<a target="_blank" {href}>{username}</a>
<script>
export let username;
$: href = `https://boardgamegeek.com/collection/user/${username}?trade=1&subtype=boardgame&ff=1`;
</script>
<style>
a {
color: var(--primary);
text-decoration: underline;
}
</style>

View File

@ -0,0 +1,15 @@
export default function clickOutside(node) {
const handleClick = (event) => {
if (node && !node.contains(event.target) && !event.defaultPrevented) {
node.dispatchEvent(new CustomEvent('click_outside', node));
}
};
document.addEventListener('click', handleClick, true);
return {
destroy() {
document.removeEventListener('click', handleClick, true);
},
};
}

View File

@ -2,20 +2,23 @@
<title>Ottawa board games, trades and sales</title>
</svelte:head>
<main class="responsive">
<AppTop />
<slot />
</main>
<script>
import 'beercss/dist/cdn/beer.min.css';
// import 'beercss/dist/cdn/beer.min.js';
import { setContext } from 'svelte';
import { dev, browser } from '$app/environment';
import { readable, writable } from 'svelte/store';
import { persisted } from 'svelte-persisted-store';
import u from '@yanick/updeep-remeda';
import AppTop from './AppTop.svelte';
const prefix = dev ? '/dev/' : '/db/';
const games = readable([], async (set) => {
const games = readable([], (set) => {
if (!browser) return () => {};
fetch(prefix + 'games.json')
@ -26,17 +29,18 @@
});
setContext('games', games);
const sellers = readable({}, async (set) => {
const sellers = readable({}, (set) => {
if (!browser) return () => {};
const sellers_list = await fetch(prefix + 'sellers.json').then((doc) =>
doc.json(),
);
set(
Object.fromEntries(
sellers_list.map((seller) => [seller.username, seller]),
),
);
const sellers_list = fetch(prefix + 'sellers.json')
.then((doc) => doc.json())
.then((sellers_list) =>
set(
Object.fromEntries(
sellers_list.map((seller) => [seller.username, seller]),
),
),
);
return () => {};
});
setContext('sellers', sellers);

View File

@ -1,12 +1,3 @@
<header>
<nav>
<h5 class="max left-align">Ottawa board games for sale and trade</h5>
<a class="button circle transparent" href="/about">
<i>question_mark</i>
</a>
</nav>
</header>
<article>
{#await $games}
<div class="medium-height middle-align center-align">
@ -27,7 +18,6 @@
const games = getContext('games');
const sellers = getContext('sellers');
$: console.log($sellers);
function pretty_date(date) {
if (!date) return '';

50
src/routes/AppTop.svelte Normal file
View File

@ -0,0 +1,50 @@
<header>
<nav>
<button
class="circle transparent"
on:click={() => (show_menu = !show_menu)}>
<i>menu</i>
</button>
<h5 class="max left-align">Ottawa board games for sale and trade</h5>
</nav>
</header>
<dialog
class="left primary-container"
open={show_menu}
use:clickOutside
on:click_outside={() => (show_menu = false)}>
<nav class="drawer primary-container">
<header></header>
<a href="/" on:click={hide_menu}>
<i>list</i>
<span class="max">listing</span>
</a>
<a href="/stats/" on:click={hide_menu}>
<i>numbers</i>
<span>stats</span>
</a>
<a href="/about/" on:click={hide_menu}>
<i>question_mark</i>
<span>about</span>
</a>
<a class="backsie" on:click={hide_menu}>
<i>arrow_back</i>
<span class="max">back</span>
</a>
</nav>
</dialog>
<script>
import clickOutside from '$lib/directives/clickOutside.js';
let show_menu = false;
const hide_menu = () => (show_menu = false);
</script>
<style>
.backsie {
margin-top: 2em;
}
dialog {
font-size: var(--font-size-10);
}
</style>

View File

@ -1,12 +1,3 @@
<header>
<nav>
<h5 class="max left-align">Ottawa board games for sale and trade</h5>
<a class="button circle transparent" href="/">
<i>arrow_back</i>
</a>
</nav>
</header>
<article>
<p>
<code>bgg.babyl.ca</code> is an aggregation of board games for sale or

View File

@ -0,0 +1,50 @@
<article>
<p>Currently listing {nbr_games} games from {nbr_sellers} sellers.</p>
{#each Object.keys($sellers).sort() as username (username)}
<div class="row">
<BggUser {username} />
<span class="max neighbourhood">
{#if $sellers[username].neighbourhood}
({$sellers[username].neighbourhood})
{/if}
</span>
<span class="nbr_games">
{nbr_games_for(username)}
</span>
</div>
{/each}
</article>
<script>
// TODO avatars?
import { getContext } from 'svelte';
import BggUser from '$lib/components/BggUser.svelte';
const games = getContext('games');
const sellers = getContext('sellers');
$: nbr_games = $games.length;
$: nbr_sellers = Object.values($sellers).length;
const nbr_games_for = (username) =>
$games.filter((game) => game.username === username).length;
</script>
<style>
.nbr_games:after {
content: ' games';
}
.nbr_games:before {
content: 'selling ';
}
div {
margin-left: 2em;
margin-right: 2em;
}
article {
font-size: var(--font-size-10);
}
</style>