diff --git a/node_modules/.package-lock.json b/node_modules/.package-lock.json index d4c0342..b270bfd 100644 --- a/node_modules/.package-lock.json +++ b/node_modules/.package-lock.json @@ -570,6 +570,32 @@ "dev": true, "license": "MIT" }, + "node_modules/@tanstack/query-core": { + "version": "5.102.8", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.102.8.tgz", + "integrity": "sha512-ZNjkJ33CqvPNec/6lZBnHqLc3EVGPZ9ySLhYahU9TcuRFdmwXewuj0c4hwSWcGHqEUwcSrKeZ+oGcvPBqXcQcg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.102.8", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.102.8.tgz", + "integrity": "sha512-TYBea4OuXWD7MhaSHq069TWbFe7rcwWN6kzT7JF0OKi1K6c1gTv2IzD6A6ExJsCMozdkqBWeuIUZmu4KQg0O5A==", + "license": "MIT", + "dependencies": { + "@tanstack/query-core": "5.102.8" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18 || ^19" + } + }, "node_modules/@types/esrecurse": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", diff --git a/node_modules/.vite/deps/_metadata.json b/node_modules/.vite/deps/_metadata.json index 9da5a4c..4a2b13f 100644 --- a/node_modules/.vite/deps/_metadata.json +++ b/node_modules/.vite/deps/_metadata.json @@ -1,43 +1,49 @@ { - "hash": "cbab395f", + "hash": "dc2b6b92", "configHash": "e7f202bd", - "lockfileHash": "4ccba3ff", - "browserHash": "da41ee4c", + "lockfileHash": "558c6ded", + "browserHash": "dec6a747", "optimized": { + "@tanstack/react-query": { + "src": "../../@tanstack/react-query/build/modern/index.js", + "file": "@tanstack_react-query.js", + "fileHash": "9b4be17a", + "needsInterop": false + }, "react-dom": { "src": "../../react-dom/index.js", "file": "react-dom.js", - "fileHash": "390c96fc", + "fileHash": "1c0d4d92", "needsInterop": true }, "react-dom/client": { "src": "../../react-dom/client.js", "file": "react-dom_client.js", - "fileHash": "fd29f39a", + "fileHash": "13bb0152", "needsInterop": true }, "react": { "src": "../../react/index.js", "file": "react.js", - "fileHash": "3bb5d513", + "fileHash": "4f799b01", "needsInterop": true }, "react/jsx-dev-runtime": { "src": "../../react/jsx-dev-runtime.js", "file": "react_jsx-dev-runtime.js", - "fileHash": "ff3b1fc9", + "fileHash": "e387570b", "needsInterop": true }, "react/jsx-runtime": { "src": "../../react/jsx-runtime.js", "file": "react_jsx-runtime.js", - "fileHash": "cacf0771", + "fileHash": "7ac592c3", "needsInterop": true } }, "chunks": { - "rolldown-runtime-BvCyGRYZ": { - "file": "rolldown-runtime-BvCyGRYZ.js", + "rolldown-runtime-BPOCksWG": { + "file": "rolldown-runtime-BPOCksWG.js", "isDynamicEntry": false } } diff --git a/src/App.css b/src/App.css index 9fd856e..9e60301 100644 --- a/src/App.css +++ b/src/App.css @@ -124,3 +124,18 @@ body { padding: 0.5rem; margin-bottom: 0.5rem; } + +.favorite-button { + color: orange; + background-color: #15151e; + padding: 0.5rem 1rem; + border-radius: 0.375rem; + cursor: pointer; + border: none; + font-size: 1.7rem; +} +.favorite-button:hover { + color: white; +} + + diff --git a/src/App.tsx b/src/App.tsx index c332979..fdc70b9 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -15,6 +15,25 @@ const sessionDate = new Intl.DateTimeFormat("en-GB", { type SortOrder = "none" | "asc" | "desc"; function App() { + const [favorites, setFavorites] = useState(() => + JSON.parse(localStorage.getItem("favorites") ?? "[]"), + ); + const [showFavorites, setShowFavorites] = useState(false); + + useEffect(() => { + localStorage.setItem("favorites", JSON.stringify(favorites)); + }, [favorites]); + + function toggleFavorite(driverNumber: number) { + setFavorites((current) => { + if (current.includes(driverNumber)) { + return current.filter((number) => number !== driverNumber); + } + + return [...current, driverNumber]; + }); + } + const [search, setSearch] = useState( () => sessionStorage.getItem("search") ?? "", ); @@ -101,7 +120,9 @@ function App() { .toLowerCase() .includes(search.toLowerCase()); const matchesTeam = team === "all" || driver.team_name === team; - return matchesSearch && matchesTeam; + const matchesFavorites = + !showFavorites || favorites.includes(driver.driver_number); + return matchesSearch && matchesTeam && matchesFavorites; }) .sort((a, b) => { if (sortOrder === "asc") { @@ -145,6 +166,8 @@ function App() { return ( <> ( toggleFavorite(driver.driver_number)} teamStanding={teamStandings.find( (team) => team.team_name === driver.team_name, )} diff --git a/src/components/DriverCard.tsx b/src/components/DriverCard.tsx index f8a4e14..9a3104b 100644 --- a/src/components/DriverCard.tsx +++ b/src/components/DriverCard.tsx @@ -16,12 +16,30 @@ interface DriverCardProps { teamStanding?: TeamStandings; teamStandingsPending: boolean; teamStandingsError: boolean; + isFavorite: boolean; + onToggleFavorite: () => void; } -export default function DriverCard({ driver, teamStanding, teamStandingsPending, teamStandingsError }: DriverCardProps) { +export default function DriverCard({ + driver, + teamStanding, + teamStandingsPending, + teamStandingsError, + isFavorite, + onToggleFavorite, +}: DriverCardProps) { return (
+

{driver.number}

{driver.name}

{driver.name} @@ -31,8 +49,8 @@ export default function DriverCard({ driver, teamStanding, teamStandingsPending, Team points: {teamStandingsPending ? 'Loading...' : teamStandingsError - ? 'Could not load points' - : teamStanding?.points_current ?? 'Unavailable'} + ? 'Could not load points' + : teamStanding?.points_current ?? 'Unavailable'}

) diff --git a/src/components/SearchAndFilter.tsx b/src/components/SearchAndFilter.tsx index c6e8461..2b5789a 100644 --- a/src/components/SearchAndFilter.tsx +++ b/src/components/SearchAndFilter.tsx @@ -1,4 +1,6 @@ interface SearchAndFilterProps { + showFavorites: boolean; + onShowFavoritesChange: (value: boolean) => void; search: string; onSearchChange: (value: string) => void; team: string; @@ -9,6 +11,8 @@ interface SearchAndFilterProps { } function SearchAndFilter({ + showFavorites, + onShowFavoritesChange, search, onSearchChange, team, @@ -43,6 +47,14 @@ function SearchAndFilter({ + + ); }