diff --git a/app/games/games-page-client.tsx b/app/games/games-page-client.tsx index 3465de5..6ff1001 100644 --- a/app/games/games-page-client.tsx +++ b/app/games/games-page-client.tsx @@ -11,6 +11,9 @@ import { XIcon, Loader2Icon, } from "lucide-react" +import { AntiCheatBadge } from "@/components/anti-cheat-badge" +import { PlayabilityBadge } from "@/components/playability-badge" +import { SavedFilters } from "@/components/saved-filters" interface GamesListItem { id: string @@ -21,8 +24,13 @@ interface GamesListItem { headerImage: string | null genres: string[] | null source: string + steamReviewScore: number | null + playabilityStatus: "great" | "playable" | "needs_tweaks" | "unplayable" | "unknown" | null + onlineMultiplayerStatus: "none" | "supported" | "unknown" | null benchmarkCount: number deckStatus: string | null + antiCheatRelevant: boolean + antiCheatStatus: "none" | "supported" | "unsupported" | "unknown" | null } interface DeviceOption { @@ -30,7 +38,7 @@ interface DeviceOption { name: string } -type SortOption = "recent" | "name" | "benchmarks" +type SortOption = "recent" | "name" | "benchmarks" | "performance" | "popularity" | "release_date" | "steam_reviews" type SortDirection = "asc" | "desc" const DECK_STATUS_CONFIG: Record = { @@ -42,8 +50,12 @@ const DECK_STATUS_CONFIG: Record = const SORT_OPTIONS: { value: SortOption; label: string }[] = [ { value: "recent", label: "Recently Added" }, - { value: "name", label: "Name A\u2013Z" }, + { value: "name", label: "Name A–Z" }, { value: "benchmarks", label: "Most Benchmarks" }, + { value: "performance", label: "Best Performance" }, + { value: "popularity", label: "Most Popular" }, + { value: "release_date", label: "Release Date" }, + { value: "steam_reviews", label: "Steam Reviews" }, ] export function GamesPageClient({ @@ -67,6 +79,15 @@ export function GamesPageClient({ const [loading, setLoading] = useState(false) const [error, setError] = useState(null) const [showFilters, setShowFilters] = useState(false) + const [minFps, setMinFps] = useState("") + const [maxFps, setMaxFps] = useState("") + const [fsrSupport, setFsrSupport] = useState(false) + const [protonNative, setProtonNative] = useState("any") + const [antiCheatStatus, setAntiCheatStatus] = useState("any") + const [playabilityStatus, setPlayabilityStatus] = useState("") + const [steamReviewMin, setSteamReviewMin] = useState("") + const [isFree, setIsFree] = useState(false) + const [hasMultiplayer, setHasMultiplayer] = useState(false) const observerRef = useRef(null) const sentinelRef = useRef(null) @@ -82,9 +103,22 @@ export function GamesPageClient({ if (search) params.set("search", search) if (selectedDevice) params.set("device", selectedDevice) if (selectedGenres.length === 1) params.set("genre", selectedGenres[0]) + if (minFps) params.set("minFps", minFps) + if (maxFps) params.set("maxFps", maxFps) + if (fsrSupport) params.set("fsrSupport", "true") + if (protonNative !== "any") params.set("protonNative", protonNative) + if (antiCheatStatus !== "any") params.set("antiCheatStatus", antiCheatStatus) + if (playabilityStatus) params.set("playabilityStatus", playabilityStatus) + if (steamReviewMin) params.set("steamReviewScore", steamReviewMin) + if (isFree) params.set("isFree", "true") + if (hasMultiplayer) params.set("hasMultiplayer", "true") return `/api/games/listing?${params.toString()}` }, - [sort, sortDirection, search, selectedDevice, selectedGenres], + [ + sort, sortDirection, search, selectedDevice, selectedGenres, + minFps, maxFps, fsrSupport, protonNative, antiCheatStatus, + playabilityStatus, steamReviewMin, isFree, hasMultiplayer, + ], ) // Load more function for infinite scroll @@ -230,15 +264,46 @@ export function GamesPageClient({ @@ -308,12 +373,160 @@ export function GamesPageClient({ + {/* Performance Filters */} +
+

Performance

+
+ setMinFps(e.target.value)} + className="w-24 rounded-md border border-zinc-700 bg-zinc-800 px-2 py-1 text-sm" + /> + setMaxFps(e.target.value)} + className="w-24 rounded-md border border-zinc-700 bg-zinc-800 px-2 py-1 text-sm" + /> +
+ +
+ + {/* Compatibility Filters */} +
+

Compatibility

+ + + +
+ + {/* Other Filters */} +
+

Other

+ + + setSteamReviewMin(e.target.value)} + className="w-full rounded-md border border-zinc-700 bg-zinc-800 px-2 py-1 text-sm" + /> +
+ + {/* Saved Filters */} + { + setMinFps(filters.minFps || "") + setMaxFps(filters.maxFps || "") + setFsrSupport(filters.fsrSupport || false) + setProtonNative(filters.protonNative || "any") + setAntiCheatStatus(filters.antiCheatStatus || "any") + setPlayabilityStatus(filters.playabilityStatus || "") + setSteamReviewMin(filters.steamReviewMin || "") + setIsFree(filters.isFree || false) + setHasMultiplayer(filters.hasMultiplayer || false) + if (filters.genre) setSelectedGenres([filters.genre]) + else setSelectedGenres([]) + if (filters.device) setSelectedDevice(filters.device) + else setSelectedDevice("") + if (filters.sortBy) setSort(filters.sortBy as SortOption) + }} + /> + {/* Clear filters */} - {(selectedDevice || selectedGenres.length > 0) && ( + {(selectedDevice || + selectedGenres.length > 0 || + minFps || + maxFps || + fsrSupport || + protonNative !== "any" || + antiCheatStatus !== "any" || + playabilityStatus || + steamReviewMin || + isFree || + hasMultiplayer) && (