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

@@ -12,6 +12,8 @@ import {
type PlaneEra,
type PlaneSort
} from '#lib/planes/shared.js';
import { parseArea, parseGot, type Got } from '#lib/spotting/areas.js';
import { activitySql, gotSql, seenSql } from '#lib/server/spotting.js';
export const PAGE_SIZE = 60;
@@ -23,6 +25,9 @@ export type PlaneFilters = {
era: PlaneEra | '';
service: '' | 'yes' | 'no';
sort: PlaneSort;
/** Area for the "most active" sort (?in=, default NL). */
area: string;
got: Got;
page: number;
};
@@ -38,12 +43,16 @@ export function parseFilters(params: URLSearchParams): PlaneFilters {
engine: pick(params.get('engine'), ENGINES),
era: pick(params.get('era'), Object.keys(ERAS) as PlaneEra[]),
service: pick(params.get('service'), ['yes', 'no'] as const),
sort: sort && sort in SORTS ? (sort as PlaneSort) : 'built',
sort: sort && sort in SORTS ? (sort as PlaneSort) : 'active',
area: parseArea(params.get('in')),
got: parseGot(params.get('got')),
page: Math.min(Math.max(1, Math.floor(Number(params.get('page')) || 1)), 1000)
};
}
const cardColumns = {
id: planeTypes.id,
seen: seenSql('planes', planeTypes.id),
dex: planeTypes.dex,
slug: planeTypes.slug,
name: planeTypes.name,
@@ -89,7 +98,14 @@ export async function searchPlanes(
}
if (f.service === 'yes') where.push(eq(planeTypes.inService, true));
if (f.service === 'no') where.push(eq(planeTypes.inService, false));
const got = gotSql('planes', planeTypes.id, f.got);
if (got) where.push(got);
const order = {
active: [
sql`${activitySql('planes', planeTypes.id, f.area)} desc`,
sql`${planeTypes.numberBuilt} desc nulls last`,
asc(planeTypes.dex)
],
built: [sql`${planeTypes.numberBuilt} desc nulls last`, asc(planeTypes.dex)],
dex: [asc(planeTypes.dex)],
name: [asc(planeTypes.fullName)],
@@ -120,6 +136,7 @@ export async function searchPlanes(
}
export type MakerTile = {
id: number;
slug: string;
name: string;
types: number;
@@ -141,6 +158,7 @@ export async function planeOverview(): Promise<Overview> {
const [makers, [counts]] = await Promise.all([
db
.select({
id: planeMakers.id,
slug: planeMakers.slug,
name: planeMakers.name,
types: planeMakers.typeCount,