Feed: add custom date range (From/To) inputs alongside search bar

This commit is contained in:
Davide Scaini
2026-05-15 08:32:31 +02:00
parent ed6a7ed39c
commit c905449114
+44 -16
View File
@@ -42,6 +42,8 @@
let datePre = 'all'; let datePre = 'all';
let dateFrom = ''; let dateFrom = '';
let dateTo = ''; let dateTo = '';
let customFrom = '';
let customTo = '';
let query = ''; let query = '';
let shown = PAGE_SIZE; let shown = PAGE_SIZE;
let loading = true; let loading = true;
@@ -83,7 +85,8 @@
return true; return true;
}); });
$: allYears = [...new Set(all.map(a => a.started_at?.slice(0, 4)).filter(Boolean) as string[])].sort().reverse(); $: allYears = [...new Set(all.map(a => a.started_at?.slice(0, 4)).filter(Boolean) as string[])].sort().reverse();
$: ({ dateFrom, dateTo } = computeDateRange(datePre)); $: dateFrom = (customFrom || customTo) ? customFrom : computeDateRange(datePre).dateFrom;
$: dateTo = (customFrom || customTo) ? (customTo ? customTo + '￿' : '') : computeDateRange(datePre).dateTo;
$: withDate = !dateFrom && !dateTo ? withPrivacy : withPrivacy.filter(a => $: withDate = !dateFrom && !dateTo ? withPrivacy : withPrivacy.filter(a =>
(!dateFrom || a.started_at >= dateFrom) && (!dateTo || a.started_at < dateTo) (!dateFrom || a.started_at >= dateFrom) && (!dateTo || a.started_at < dateTo)
); );
@@ -126,7 +129,7 @@
} }
} }
$: if (sport || datePre || query) shown = PAGE_SIZE; // reset pagination on filter change $: if (sport || datePre || query || customFrom || customTo) shown = PAGE_SIZE; // reset pagination on filter change
// When a search query is active, eagerly load all remaining shards so the // When a search query is active, eagerly load all remaining shards so the
// full history is searched (not just the initially-loaded recent year). // full history is searched (not just the initially-loaded recent year).
@@ -153,18 +156,22 @@
$: if (mounted) { $: if (mounted) {
const params = new URLSearchParams(window.location.search); const params = new URLSearchParams(window.location.search);
if (sport === 'all') params.delete('sport'); else params.set('sport', sport); if (sport === 'all') params.delete('sport'); else params.set('sport', sport);
if (datePre === 'all') params.delete('date'); else params.set('date', datePre); if (datePre === 'all' || customFrom || customTo) params.delete('date'); else params.set('date', datePre);
if (!query.trim()) params.delete('q'); else params.set('q', query.trim()); if (!query.trim()) params.delete('q'); else params.set('q', query.trim());
if (!customFrom) params.delete('from'); else params.set('from', customFrom);
if (!customTo) params.delete('to'); else params.set('to', customTo);
const qs = params.toString(); const qs = params.toString();
history.replaceState(null, '', qs ? `?${qs}` : window.location.pathname); history.replaceState(null, '', qs ? `?${qs}` : window.location.pathname);
} }
onMount(async () => { onMount(async () => {
const params = new URLSearchParams(window.location.search); const params = new URLSearchParams(window.location.search);
sport = (params.get('sport') as Sport | 'all') ?? 'all'; sport = (params.get('sport') as Sport | 'all') ?? 'all';
datePre = params.get('date') ?? 'all'; datePre = params.get('date') ?? 'all';
query = params.get('q') ?? ''; query = params.get('q') ?? '';
mounted = true; customFrom = params.get('from') ?? '';
customTo = params.get('to') ?? '';
mounted = true;
// Resolve the logged-in handle so we can show the owner their private activities. // Resolve the logged-in handle so we can show the owner their private activities.
if ((window as any).__bincioMe !== undefined) { if ((window as any).__bincioMe !== undefined) {
@@ -215,14 +222,34 @@
]; ];
</script> </script>
<!-- Search --> <!-- Search + date range -->
<div class="relative mb-4"> <div class="flex flex-col md:flex-row gap-2 mb-4">
<input <input
type="search" type="search"
placeholder="Search activities…" placeholder="Search activities…"
bind:value={query} bind:value={query}
class="w-full px-4 py-2 rounded-lg bg-zinc-900 border border-zinc-700 text-white placeholder-zinc-500 text-sm focus:outline-none focus:border-zinc-500 transition-colors" class="flex-1 px-4 py-2 rounded-lg bg-zinc-900 border border-zinc-700 text-white placeholder-zinc-500 text-sm focus:outline-none focus:border-zinc-500 transition-colors"
/> />
<div class="flex gap-2">
<div class="flex items-center gap-1.5 px-3 py-2 rounded-lg bg-zinc-900 border border-zinc-700 focus-within:border-zinc-500 transition-colors">
<span class="text-xs text-zinc-500 whitespace-nowrap select-none">From</span>
<input
type="date"
bind:value={customFrom}
on:change={() => { datePre = 'all'; }}
class="bg-transparent text-white text-sm focus:outline-none [color-scheme:dark]"
/>
</div>
<div class="flex items-center gap-1.5 px-3 py-2 rounded-lg bg-zinc-900 border border-zinc-700 focus-within:border-zinc-500 transition-colors">
<span class="text-xs text-zinc-500 whitespace-nowrap select-none">To</span>
<input
type="date"
bind:value={customTo}
on:change={() => { datePre = 'all'; }}
class="bg-transparent text-white text-sm focus:outline-none [color-scheme:dark]"
/>
</div>
</div>
</div> </div>
<!-- Sport filter bar --> <!-- Sport filter bar -->
@@ -254,14 +281,15 @@
<!-- Date filter bar --> <!-- Date filter bar -->
<div class="flex gap-2 mb-6 flex-wrap"> <div class="flex gap-2 mb-6 flex-wrap">
{#each [{ value: 'all', label: 'All time' }, { value: '7d', label: '7 days' }, { value: '30d', label: '30 days' }, { value: '6mo', label: '6 months' }, ...allYears.map(y => ({ value: y, label: y }))] as d} {#each [{ value: 'all', label: 'All time' }, { value: '7d', label: '7 days' }, { value: '30d', label: '30 days' }, { value: '6mo', label: '6 months' }, ...allYears.map(y => ({ value: y, label: y }))] as d}
{@const isActive = datePre === d.value && !customFrom && !customTo}
<button <button
class="px-3 py-1 rounded-full text-sm font-medium border transition-colors" class="px-3 py-1 rounded-full text-sm font-medium border transition-colors"
class:border-zinc-700={datePre !== d.value} class:border-zinc-700={!isActive}
class:text-zinc-400={datePre !== d.value} class:text-zinc-400={!isActive}
class:border-[--accent]={datePre === d.value} class:border-[--accent]={isActive}
class:text-white={datePre === d.value} class:text-white={isActive}
style={datePre === d.value ? 'background:var(--accent-dim)' : ''} style={isActive ? 'background:var(--accent-dim)' : ''}
on:click={() => datePre = d.value} on:click={() => { datePre = d.value; customFrom = ''; customTo = ''; }}
> >
{d.label} {d.label}
</button> </button>