ui changes and cs prices
All checks were successful
Test and deploy staging / staging (push) Successful in 11m52s

This commit is contained in:
2026-09-25 00:44:06 +00:00
parent c757d0882a
commit abac077541
200 changed files with 6236 additions and 2311 deletions

View File

@@ -1,6 +1,8 @@
<script lang="ts">
import { resolve } from '$app/paths';
import ProgressBar from '$lib/components/ProgressBar.svelte';
import * as Card from '$lib/components/ui/card/index.js';
import { Button } from '$lib/components/ui/button/index.js';
import type { PageProps } from './$types';
let { data }: PageProps = $props();
@@ -24,33 +26,33 @@
<section class="mt-10">
<h2 class="text-2xl font-semibold">Pokémon</h2>
<a
href={resolve('/projects/game-tracker/pokemon')}
class="mt-4 block rounded-lg border border-[var(--site-border)] p-5 hover:bg-[var(--site-hover)]"
<Card.Root class="mt-4 py-0 transition-colors hover:bg-muted/50"
><Card.Content class="p-0"
><a href={resolve('/projects/game-tracker/pokemon')} class="block p-5">
<div class="grid grid-cols-[minmax(0,1fr)_8rem_auto] items-center gap-3">
<p class="text-xl font-semibold">catchemall</p>
<div class="w-32">
<ProgressBar
value={data.pokemon.caught.entries}
max={data.pokemon.totals.entries}
label="Pokémon caught"
small
/>
</div>
<p class="text-xl font-semibold tabular-nums">
{percent(data.pokemon.caught.entries, data.pokemon.totals.entries)}%
</p>
</div>
<p class="mt-2 text-[var(--site-muted)]">
{data.pokemon.caught.entries} of {data.pokemon.totals.entries} obtainable entries ·
{data.pokemon.caught.species} of {data.pokemon.totals.species} species · {data.pokemon
.totals.games} games
</p>
</a></Card.Content
></Card.Root
>
<div class="flex flex-wrap items-baseline justify-between gap-3">
<p class="text-xl font-semibold">catchemall</p>
<p class="text-xl font-semibold tabular-nums">
{percent(data.pokemon.caught.entries, data.pokemon.totals.entries)}%
</p>
</div>
<div class="mt-3">
<ProgressBar
value={data.pokemon.caught.entries}
max={data.pokemon.totals.entries}
label="Pokémon caught"
small
/>
</div>
<p class="mt-2 text-[var(--site-muted)]">
{data.pokemon.caught.entries} of {data.pokemon.totals.entries} obtainable entries ·
{data.pokemon.caught.species} of {data.pokemon.totals.species} species · {data.pokemon
.totals.games} games
</p>
</a>
<a
href={resolve('/projects/game-tracker/pokemon/locations')}
class="mt-3 inline-block underline underline-offset-4">browse Pokémon locations</a
<Button variant="link" href={resolve('/projects/game-tracker/pokemon/locations')} class="mt-3"
>browse Pokémon locations</Button
>
</section>
@@ -59,39 +61,43 @@
<ul class="mt-4 grid gap-4 md:grid-cols-2">
{#each data.callOfDuty as game (game.slug)}
<li>
<a
href={resolve('/projects/game-tracker/call-of-duty/[slug]', { slug: game.slug })}
class="block h-full rounded-lg border border-[var(--site-border)] p-5 hover:bg-[var(--site-hover)]"
>
<div class="flex flex-wrap items-baseline justify-between gap-3">
<p class="text-xl font-semibold">{game.name}</p>
<p class="text-[var(--site-muted)]">{game.releaseYear}</p>
</div>
<dl class="mt-4 grid gap-3">
{#each [{ key: 'achievements', label: 'achievements', ...game.achievements }, ...game.milestones] as row (row.key)}
<div>
<div class="flex justify-between gap-3 text-[var(--site-muted)]">
<dt>{row.label}</dt>
<dd class="tabular-nums">{row.value}/{row.total}</dd>
</div>
<div class="mt-1">
<ProgressBar value={row.value} max={row.total} label={row.label} small />
</div>
<Card.Root class="h-full py-0 transition-colors hover:bg-muted/50"
><Card.Content class="p-0"
><a
href={resolve('/projects/game-tracker/call-of-duty/[slug]', { slug: game.slug })}
class="block h-full p-5"
>
<div class="flex flex-wrap items-baseline justify-between gap-3">
<p class="text-xl font-semibold">{game.name}</p>
<p class="text-[var(--site-muted)]">{game.releaseYear}</p>
</div>
{/each}
</dl>
{#if game.tiers.length}
<p class="mt-4 text-[var(--site-muted)]">
{game.tiers
.map((tier) =>
tier.scope === 'class'
? `${tier.label} ${tier.value}/${tier.total} classes`
: `${tier.label} ${tier.achieved ? '✓' : `${percent(tier.value, tier.total)}%`}`
)
.join(' · ')}
</p>
{/if}
</a>
<dl class="mt-4 grid gap-3">
{#each [{ key: 'achievements', label: 'achievements', ...game.achievements }, ...game.milestones] as row (row.key)}
<div
class="grid grid-cols-[minmax(0,1fr)_8rem_auto] items-center gap-3 text-sm text-[var(--site-muted)]"
>
<dt class="truncate">{row.label}</dt>
<div class="w-32">
<ProgressBar value={row.value} max={row.total} label={row.label} small />
</div>
<dd class="min-w-12 text-right tabular-nums">{row.value}/{row.total}</dd>
</div>
{/each}
</dl>
{#if game.tiers.length}
<p class="mt-4 text-[var(--site-muted)]">
{game.tiers
.map((tier) =>
tier.scope === 'class'
? `${tier.label} ${tier.value}/${tier.total} classes`
: `${tier.label} ${tier.achieved ? '✓' : `${percent(tier.value, tier.total)}%`}`
)
.join(' · ')}
</p>
{/if}
</a></Card.Content
></Card.Root
>
</li>
{:else}
<li class="text-[var(--site-muted)]">the Call of Duty catalog has not been loaded yet.</li>

View File

@@ -4,6 +4,7 @@
import { deserialize } from '$app/forms';
import { SvelteSet } from 'svelte/reactivity';
import ProgressBar from '$lib/components/ProgressBar.svelte';
import * as Tabs from '$lib/components/ui/tabs/index.js';
import { appliesTo, summarizeGame } from '$lib/game-tracker/mastery';
import type { PageProps } from './$types';
@@ -131,19 +132,12 @@
</div>
<div class="mt-8 flex flex-wrap items-center justify-between gap-4">
<div role="tablist" class="flex gap-6 text-lg">
{#each tabs as name (name)}
<button
role="tab"
type="button"
aria-selected={tab === name}
onclick={() => (tab = name)}
class={tab === name
? 'underline underline-offset-8'
: 'text-[var(--site-muted)] hover:text-[var(--site-text)]'}>{name}</button
>
{/each}
</div>
<Tabs.Root bind:value={tab}
><Tabs.List
>{#each tabs as name (name)}<Tabs.Trigger value={name}>{name}</Tabs.Trigger
>{/each}</Tabs.List
></Tabs.Root
>
<label class="flex cursor-pointer items-center gap-3">
<input type="checkbox" bind:checked={hideDone} class="h-5 w-5 accent-[var(--site-text)]" />
hide finished

View File

@@ -12,6 +12,8 @@
import { SvelteSet } from 'svelte/reactivity';
import type { PageProps } from './$types';
import ProgressBar from '$lib/components/ProgressBar.svelte';
import { Button } from '$lib/components/ui/button/index.js';
import { Input } from '$lib/components/ui/input/index.js';
import PokemonDetails from '$lib/catch-em-all/PokemonDetails.svelte';
import { summarizeProgress } from '$lib/catch-em-all/progress';
@@ -171,12 +173,7 @@
{data.pokemon.length} Pokémon · {data.games.length} games
</p>
</div>
<button
type="button"
onclick={openRules}
class="rounded border border-[var(--site-border)] px-4 py-2 hover:bg-[var(--site-hover)]"
>set rules</button
>
<Button variant="outline" type="button" onclick={openRules}>set rules</Button>
</div>
{#if !data.user}<p class="mt-6">
@@ -232,12 +229,12 @@
<div class="mt-8 grid gap-4 sm:grid-cols-[minmax(0,1fr)_minmax(0,1fr)]">
<label class="block"
>search Pokémon
<input
<Input
type="search"
bind:value={search}
oninput={() => (pageNumber = 1)}
placeholder="name or number"
class="mt-2 block w-full rounded border border-[var(--site-border)] bg-[var(--site-bg)] px-3 py-3"
class="mt-2 h-11"
/>
</label>
<label class="block"
@@ -348,18 +345,20 @@
)} of {filteredPokemon.length}
</p>
<div class="flex items-center gap-4">
<button
<Button
variant="outline"
size="sm"
type="button"
disabled={pageNumber === 1}
onclick={() => (pageNumber -= 1)}
class="disabled:opacity-40">previous</button
onclick={() => (pageNumber -= 1)}>previous</Button
>
<span class="text-[var(--site-muted)]">{pageNumber}/{pageCount}</span>
<button
<Button
variant="outline"
size="sm"
type="button"
disabled={pageNumber === pageCount}
onclick={() => (pageNumber += 1)}
class="disabled:opacity-40">next</button
onclick={() => (pageNumber += 1)}>next</Button
>
</div>
</div>

View File

@@ -1,5 +1,7 @@
<script lang="ts">
import { resolve } from '$app/paths';
import { Input } from '$lib/components/ui/input/index.js';
import * as Card from '$lib/components/ui/card/index.js';
import type { PageProps } from './$types';
let { data }: PageProps = $props();
@@ -33,12 +35,7 @@
<div class="mt-8 grid gap-4 sm:grid-cols-[minmax(0,1fr)_minmax(0,16rem)]">
<label class="block"
>search
<input
type="search"
bind:value={search}
placeholder="route, city, cave…"
class="mt-2 block w-full rounded border border-[var(--site-border)] bg-[var(--site-bg)] px-3 py-3"
/>
<Input type="search" bind:value={search} placeholder="route, city, cave…" class="mt-2 h-11" />
</label>
<label class="block"
>region
@@ -65,20 +62,24 @@
<h2 class="text-2xl font-semibold">{name}</h2>
<ul class="mt-3 grid gap-x-8 sm:grid-cols-2 lg:grid-cols-3">
{#each locations as location (location.id)}
<li class="border-t border-[var(--site-border)]">
<a
href={resolve('/projects/game-tracker/pokemon/locations/[id=integer]', {
id: String(location.id)
})}
class="flex items-baseline justify-between gap-3 py-3 hover:text-[var(--site-muted)]"
<li>
<Card.Root class="transition-colors hover:bg-muted/50"
><Card.Content class="p-0">
<a
href={resolve('/projects/game-tracker/pokemon/locations/[id=integer]', {
id: String(location.id)
})}
class="flex items-baseline justify-between gap-3 p-4"
>
<span>{location.name}</span>
<span class="shrink-0 text-sm text-[var(--site-muted)]"
>{location.pokemon ? `${location.pokemon} Pokémon` : ''}{location.hasPicture
? ' · picture'
: ''}</span
>
</a>
</Card.Content></Card.Root
>
<span>{location.name}</span>
<span class="shrink-0 text-sm text-[var(--site-muted)]"
>{location.pokemon ? `${location.pokemon} Pokémon` : ''}{location.hasPicture
? ' · picture'
: ''}</span
>
</a>
</li>
{/each}
</ul>

View File

@@ -2,6 +2,9 @@
import { resolve } from '$app/paths';
import { enhance } from '$app/forms';
import { page } from '$app/state';
import { Button } from '$lib/components/ui/button/index.js';
import { Input } from '$lib/components/ui/input/index.js';
import { Textarea } from '$lib/components/ui/textarea/index.js';
import type { PageProps } from './$types';
let { data, form }: PageProps = $props();
@@ -78,12 +81,13 @@
{#if form?.message}<p role="alert" class="mt-3 text-red-500">{form.message}</p>{/if}
<label class="mt-4 block"
>notes
<textarea
<Textarea
name="description"
rows="4"
maxlength="5000"
class="mt-2 block w-full rounded border border-[var(--site-border)] bg-[var(--site-bg)] px-3 py-2"
>{data.location.description ?? ''}</textarea
rows={4}
maxlength={5000}
value={data.location.description ?? ''}
class="mt-2"
/>
>
</label>
<div class="mt-4 grid gap-4 sm:grid-cols-2">
@@ -98,23 +102,19 @@
</label>
<label class="block"
>picture credit
<input
<Input
name="imageCredit"
value={data.location.imageCredit ?? ''}
maxlength="300"
maxlength={300}
placeholder="e.g. my own screenshot"
class="mt-2 block w-full rounded border border-[var(--site-border)] bg-[var(--site-bg)] px-3 py-2"
class="mt-2"
/>
</label>
</div>
{#if data.location.imageUrl}<label class="mt-3 flex items-center gap-2"
><input type="checkbox" name="removePicture" /> remove the current picture</label
>{/if}
<button
disabled={saving}
class="mt-5 rounded bg-[var(--site-text)] px-4 py-2 font-semibold text-[var(--site-bg)] disabled:opacity-40"
>{saving ? 'saving…' : 'save'}</button
>
<Button type="submit" disabled={saving} class="mt-5">{saving ? 'saving…' : 'save'}</Button>
</form>
{/if}