spotters: most-active-in sort with country/region selector, collection tracking (seen marks, progress, filters), refresh scripts score activity and report new NL entries
Some checks failed
Deploy production / deploy (push) Has been cancelled

This commit is contained in:
2026-10-25 13:24:37 +00:00
parent 3f294df337
commit 253cd69424
58 changed files with 2151 additions and 72 deletions

View File

@@ -15,6 +15,10 @@
TRACTIONS,
tractionLabel
} from '#lib/trains/shared.js';
import AreaSelect from '#lib/spotting/AreaSelect.svelte';
import { GOT } from '#lib/spotting/areas.js';
import SpotItem from '#lib/spotting/SpotItem.svelte';
import SpotProgress from '#lib/spotting/SpotProgress.svelte';
import { Input } from '#lib/components/ui/input/index.js';
import { NativeSelect } from '#lib/components/ui/native-select/index.js';
import Search from 'lucide-svelte/icons/search';
@@ -27,7 +31,7 @@
const f = $derived(data.filters);
const pages = $derived(Math.max(1, Math.ceil(data.total / data.pageSize)));
const filtering = $derived(
!!(f.q || f.maker || f.category || f.traction || f.country || f.era || f.service)
!!(f.q || f.maker || f.category || f.traction || f.country || f.era || f.service || f.got)
);
let form: HTMLFormElement | undefined = $state();
@@ -61,7 +65,8 @@
{ label: '🚇 metros', params: { category: 'metro', sort: 'built' } },
{ label: '🚋 trams', params: { category: 'tram', sort: 'built' } },
{ label: '💪 most powerful', params: { sort: 'power' } },
{ label: '🟡 VIRM', params: { q: 'virm' } }
{ label: '🟡 VIRM', params: { q: 'virm' } },
{ label: '⬜ still to go', params: { got: 'no' } }
];
</script>
@@ -107,6 +112,15 @@
</div>
</dl>
<div class="mb-8">
<SpotProgress
seen={data.collected.seen}
total={data.collected.total}
label="types collected"
href={withParams({ got: 'no' })}
/>
</div>
{#if !filtering && f.page === 1}
<section class="mb-8" aria-labelledby="makers-heading">
<div class="mb-3 flex items-baseline justify-between gap-3">
@@ -194,6 +208,14 @@
{#each Object.entries(SORTS) as [key, label] (key)}<option value={key}>{label}</option
>{/each}
</NativeSelect>
{#if f.sort === 'active'}<AreaSelect
value={f.area}
available={data.areas}
onchange={submit}
/>{/if}
<NativeSelect name="got" aria-label="Collection" value={f.got} onchange={submit}>
{#each Object.entries(GOT) as [key, label] (key)}<option value={key}>{label}</option>{/each}
</NativeSelect>
<noscript><button type="submit" class="pill">search</button></noscript>
</div>
</form>
@@ -216,7 +238,11 @@
{#if data.trains.length}
<ul class="grid">
{#each data.trains as train, i (train.dex)}
<li><TrainCard {train} eager={i < 4} /></li>
<li>
<SpotItem tracker="trains" id={train.id} seen={train.seen} label={train.fullName}>
<TrainCard {train} eager={i < 4} />
</SpotItem>
</li>
{/each}
</ul>
{:else}