From 7d8e3a6de0ce82fc32f95835234f0480480cbf67 Mon Sep 17 00:00:00 2001 From: vikingowl Date: Tue, 4 Nov 2025 23:45:21 +0100 Subject: [PATCH] feat: Add sorting and result filtering to matches page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds client-side sorting and filtering capabilities: Sorting options: - Date (newest/oldest) - Duration (shortest/longest) - Score difference (close games/blowouts) - Toggle ascending/descending order Result filters: - All matches - Wins only (Team A won) - Losses only (Team B won) - Ties only Features: - Reactive $derived computed matches list - Shows filtered count vs total matches - Empty state when no matches match filters - Clear filter button when results are empty - Works seamlessly with pagination (Load More) Completes Phase 5.2 advanced features from TODO.md. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/routes/matches/+page.svelte | 164 +++++++++++++++++++++++++++++--- 1 file changed, 150 insertions(+), 14 deletions(-) diff --git a/src/routes/matches/+page.svelte b/src/routes/matches/+page.svelte index 6933b9a..b396b80 100644 --- a/src/routes/matches/+page.svelte +++ b/src/routes/matches/+page.svelte @@ -26,6 +26,11 @@ let nextPageTime = $state(data.nextPageTime); let isLoadingMore = $state(false); + // Sorting and filtering state + let sortBy = $state<'date' | 'duration' | 'score'>('date'); + let sortOrder = $state<'desc' | 'asc'>('desc'); + let resultFilter = $state<'all' | 'win' | 'loss' | 'tie'>('all'); + // Reset pagination when data changes (new filters applied) $effect(() => { matches = data.matches; @@ -33,6 +38,44 @@ nextPageTime = data.nextPageTime; }); + // Computed filtered and sorted matches + const displayMatches = $derived(() => { + let filtered = [...matches]; + + // Apply result filter + if (resultFilter !== 'all') { + filtered = filtered.filter((match) => { + const teamAWon = match.score_team_a > match.score_team_b; + const teamBWon = match.score_team_b > match.score_team_a; + const tie = match.score_team_a === match.score_team_b; + + if (resultFilter === 'win') return teamAWon; + if (resultFilter === 'loss') return teamBWon; + if (resultFilter === 'tie') return tie; + return true; + }); + } + + // Apply sorting + filtered.sort((a, b) => { + let comparison = 0; + + if (sortBy === 'date') { + comparison = new Date(a.date).getTime() - new Date(b.date).getTime(); + } else if (sortBy === 'duration') { + comparison = a.duration - b.duration; + } else if (sortBy === 'score') { + const aScoreDiff = Math.abs(a.score_team_a - a.score_team_b); + const bScoreDiff = Math.abs(b.score_team_a - b.score_team_b); + comparison = aScoreDiff - bScoreDiff; + } + + return sortOrder === 'desc' ? -comparison : comparison; + }); + + return filtered; + }); + const handleSearch = () => { const params = new URLSearchParams(); if (searchQuery) params.set('search', searchQuery); @@ -120,18 +163,83 @@ {#if showFilters} -
-

Filter by Map

-
- {#each commonMaps as mapName} - + +
+

Filter by Map

+
+
+ + +
+

Filter by Result

+
+ + + + +
+
+ + +
+

Sort By

+
+ + +
{/if} @@ -155,10 +263,19 @@ {/if} + + {#if matches.length > 0 && resultFilter !== 'all'} +
+ + Showing {displayMatches().length} of {matches.length} matches + +
+ {/if} + - {#if matches.length > 0} + {#if displayMatches().length > 0}
- {#each matches as match} + {#each displayMatches() as match} {/each}
@@ -180,9 +297,28 @@
{:else if matches.length > 0}
- All matches loaded ({matches.length} total) + + All matches loaded ({matches.length} total{resultFilter !== 'all' + ? `, ${displayMatches().length} shown` + : ''}) +
{/if} + {:else if matches.length > 0 && displayMatches().length === 0} + +
+ +

No Matches Found

+

+ No matches match your current filters. Try adjusting your filter settings. +

+
+ +
+
+
{:else}