From 666a22988aa0bd112203d73921952bfb58d85fb5 Mon Sep 17 00:00:00 2001 From: bgagent Date: Mon, 10 Aug 2026 14:45:49 +0000 Subject: [PATCH] refactor(soccer-app): extract team query logic into reusable utility Co-Authored-By: Claude Opus 4.8 Task-Id: 01KZP23QXZP7SGBY0ZRGZF4EKY Prompt-Version: 1c9c10e027a2 --- soccer-app/src/App.tsx | 25 ++------------- soccer-app/src/utils/teamQuery.ts | 52 +++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 23 deletions(-) create mode 100644 soccer-app/src/utils/teamQuery.ts diff --git a/soccer-app/src/App.tsx b/soccer-app/src/App.tsx index 6d8ad0b..383cbef 100644 --- a/soccer-app/src/App.tsx +++ b/soccer-app/src/App.tsx @@ -2,10 +2,9 @@ import { useState } from "react"; import { teams } from "./data/teams"; import { TeamCard } from "./components/TeamCard"; import { TeamDetail } from "./components/TeamDetail"; +import { queryTeams, type SortKey } from "./utils/teamQuery"; import "./App.css"; -type SortKey = "points" | "goals" | "name" | "league"; - function App() { const [selectedTeamId, setSelectedTeamId] = useState(teams[0].id); const [searchQuery, setSearchQuery] = useState(""); @@ -13,27 +12,7 @@ function App() { const selectedTeam = teams.find((t) => t.id === selectedTeamId) ?? teams[0]; - const filteredTeams = teams - .filter( - (team) => - team.name.toLowerCase().includes(searchQuery.toLowerCase()) || - team.league.toLowerCase().includes(searchQuery.toLowerCase()) || - team.country.toLowerCase().includes(searchQuery.toLowerCase()) - ) - .sort((a, b) => { - switch (sortKey) { - case "points": - return b.stats.points - a.stats.points; - case "goals": - return b.stats.goalsFor - a.stats.goalsFor; - case "name": - return a.name.localeCompare(b.name); - case "league": - return a.league.localeCompare(b.league); - default: - return 0; - } - }); + const filteredTeams = queryTeams(teams, searchQuery, sortKey); return (
diff --git a/soccer-app/src/utils/teamQuery.ts b/soccer-app/src/utils/teamQuery.ts new file mode 100644 index 0000000..46fb25f --- /dev/null +++ b/soccer-app/src/utils/teamQuery.ts @@ -0,0 +1,52 @@ +import type { Team } from "../data/teams"; + +export type SortKey = "points" | "goals" | "name" | "league"; + +/** + * Returns true if the team matches the given search query. + * Matching is case-insensitive and checks the team name, league and country. + */ +export function matchesSearch(team: Team, searchQuery: string): boolean { + const query = searchQuery.toLowerCase(); + return ( + team.name.toLowerCase().includes(query) || + team.league.toLowerCase().includes(query) || + team.country.toLowerCase().includes(query) + ); +} + +/** + * Comparator for sorting teams by the given sort key. + * - points: descending by league points + * - goals: descending by goals scored + * - name: ascending alphabetical by team name + * - league: ascending alphabetical by league name + */ +export function compareTeams(a: Team, b: Team, sortKey: SortKey): number { + switch (sortKey) { + case "points": + return b.stats.points - a.stats.points; + case "goals": + return b.stats.goalsFor - a.stats.goalsFor; + case "name": + return a.name.localeCompare(b.name); + case "league": + return a.league.localeCompare(b.league); + default: + return 0; + } +} + +/** + * Filters teams by the search query and sorts them by the given sort key. + * Does not mutate the input array. + */ +export function queryTeams( + teams: Team[], + searchQuery: string, + sortKey: SortKey +): Team[] { + return teams + .filter((team) => matchesSearch(team, searchQuery)) + .sort((a, b) => compareTeams(a, b, sortKey)); +}