"use client" import { Suspense, useState, useEffect } from "react" import { useRouter, useSearchParams } from "next/navigation" import { motion, AnimatePresence } from "motion/react" import { ExternalLinkIcon, Gamepad2Icon, MessageSquareIcon, SettingsIcon, TrendingUpIcon, DatabaseIcon, SparklesIcon, SlidersHorizontalIcon, XIcon, } from "lucide-react" import { FaSteam } from "react-icons/fa" import Image from "next/image" import Link from "next/link" import { useSession } from "@/lib/auth-client" import { WindowsIcon, MacIcon, LinuxIcon } from "@/app/components/PlatformIcons" import { AntiCheatBadge } from "@/components/anti-cheat-badge" import { PlayabilityBadge } from "@/components/playability-badge" import { SavedFilters } from "@/components/saved-filters" interface UnifiedResult { kind: "local" | "steam" id?: string appId: number | null title: string image: string | null developer: string | null publisher: string | null description: string | null genres: string[] | null source: string counts: { benchmarks: number; presets: number; comments: number } | null platformSupport: { isSupported: boolean protonStatus: string antiCheatRelevant: boolean antiCheatName: string | null antiCheatStatus: string } | null metascore?: string | null price?: { currency: string; initial: number; final: number } | null platforms?: { windows: boolean; mac: boolean; linux: boolean } | null controllerSupport?: string | null isRawPerformer?: boolean isPoorPerformance?: boolean bestFps?: number | null estimatedBatteryMin?: number | null latestVersion?: string | null tinyImage?: string | null // Badges & review fields playabilityStatus?: "great" | "playable" | "needs_tweaks" | "unplayable" | "unknown" | null steamReviewScore?: number | null steamReviewSentiment?: string | null antiCheatRelevant?: boolean antiCheatStatus?: string | null antiCheatName?: string | null } const DEVICE_OPTIONS = [ { slug: "steam-deck-oled", name: "Steam Deck OLED" }, { slug: "steam-deck-lcd", name: 'Steam Deck LCD' }, { slug: "rog-ally", name: "ROG Ally" }, ] function SearchContent() { const searchParams = useSearchParams() const router = useRouter() const { data: session } = useSession() const query = searchParams.get("q") || "" const [results, setResults] = useState([]) const [loading, setLoading] = useState(false) const [error, setError] = useState(null) const isValidQuery = query && query.length >= 2 // Filter state const [showFilters, setShowFilters] = useState(false) const [selectedDevice, setSelectedDevice] = useState("") 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 hasActiveFilters = selectedDevice || minFps || maxFps || fsrSupport || protonNative !== "any" || antiCheatStatus !== "any" || playabilityStatus || steamReviewMin || isFree || hasMultiplayer const activeFilterCount = (selectedDevice ? 1 : 0) + (minFps ? 1 : 0) + (maxFps ? 1 : 0) + (fsrSupport ? 1 : 0) + (protonNative !== "any" ? 1 : 0) + (antiCheatStatus !== "any" ? 1 : 0) + (playabilityStatus ? 1 : 0) + (steamReviewMin ? 1 : 0) + (isFree ? 1 : 0) + (hasMultiplayer ? 1 : 0) // Handle direct navigation / browser back-forward useEffect(() => { if (!isValidQuery) return let cancelled = false async function fetchResults() { setLoading(true) setError(null) try { const params = new URLSearchParams() params.set("q", query) if (selectedDevice) params.set("device", selectedDevice) 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") const res = await fetch(`/api/search/unified?${params.toString()}`) if (!res.ok) throw new Error(await res.text()) const data = await res.json() if (!cancelled) setResults(data.results || []) } catch (err) { if (!cancelled) { setError("Failed to fetch search results") console.error(err) } } finally { if (!cancelled) setLoading(false) } } fetchResults() return () => { cancelled = true } }, [isValidQuery, query, selectedDevice, minFps, maxFps, fsrSupport, protonNative, antiCheatStatus, playabilityStatus, steamReviewMin, isFree, hasMultiplayer]) function handleClick(result: UnifiedResult) { const path = result.appId ? `/game/${result.appId}?sync=1` : `/game/${result.id}?sync=1` router.push(path) } function clearAllFilters() { setSelectedDevice("") setMinFps("") setMaxFps("") setFsrSupport(false) setProtonNative("any") setAntiCheatStatus("any") setPlayabilityStatus("") setSteamReviewMin("") setIsFree(false) setHasMultiplayer(false) } return (
{query ? `Results for "${query}"` : "Search using game name or AppID"} {query ? `${results.length} result${results.length !== 1 ? "s" : ""} found` : "Enter a game name or AppID to find benchmarks, settings, and reviews."} {/* Filter toggle button */}
{hasActiveFilters && ( )}
{/* Filter panel */} {showFilters && (
{/* Device filter */}
Device
{DEVICE_OPTIONS.map((device) => ( ))}
{/* Performance Filters */}

Performance

setMinFps(e.target.value)} className="w-24 rounded-md border border-zinc-700 bg-zinc-800 px-2 py-2.5 text-sm min-h-[44px]" /> setMaxFps(e.target.value)} className="w-24 rounded-md border border-zinc-700 bg-zinc-800 px-2 py-2.5 text-sm min-h-[44px]" />
{/* 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 as string) || "") setMaxFps((filters.maxFps as string) || "") setFsrSupport((filters.fsrSupport as boolean) || false) setProtonNative((filters.protonNative as string) || "any") setAntiCheatStatus((filters.antiCheatStatus as string) || "any") setPlayabilityStatus((filters.playabilityStatus as string) || "") setSteamReviewMin((filters.steamReviewMin as string) || "") setIsFree((filters.isFree as boolean) || false) setHasMultiplayer((filters.hasMultiplayer as boolean) || false) if (filters.device) setSelectedDevice(filters.device as string) else setSelectedDevice("") }} />
)}
{!isValidQuery && (

Start typing to search for games

)} {isValidQuery && loading && (

Searching for "{query}"...

)} {isValidQuery && !loading && error && (

{error}

)} {isValidQuery && !loading && !error && results.length === 0 && (

No results found for "{query}"

)} {isValidQuery && !loading && !error && results.length > 0 && ( {results.map((result, idx) => ( ))} )} {session?.user && (

Can't find your game?{" "} Add it manually

)}
) } function SearchResultCard({ result, onClick, index, }: { result: UnifiedResult onClick: (r: UnifiedResult) => void index: number }) { const isLocal = result.kind === "local" const counts = result.counts const hasData = isLocal && counts && (counts.benchmarks > 0 || counts.presets > 0 || counts.comments > 0) return ( {/* Full-card click target */}
onClick(result)} />
{/* Cover */} {/* Main Content */}
{/* Row 1: Title + metascore/price row */}

{result.title}

{!isLocal && ( Steam )} {isLocal && result.source !== "steam" && ( {result.source} )} {result.isRawPerformer && ( โšก RAW PERFORMER )} {result.isPoorPerformance && ( โš  POOR PERFORMANCE )} {result.estimatedBatteryMin != null && ( ๐Ÿ”‹ ~{Math.round(result.estimatedBatteryMin / 60)}h )}
{(result.developer || result.publisher) && (

{result.developer} {result.developer && result.publisher ? " ยท " : ""} {result.publisher}

)}
{/* Metascore + Price row */}
{result.metascore ? ( {result.metascore} ) : null}
{/* Row 2: Description */} {result.description && (

{result.description}

)} {/* Row 3: Genre tags + Platform icons + Controller */}
{result.genres && result.genres.length > 0 && (
{result.genres.slice(0, 3).map((genre) => ( {genre} ))} {result.genres.length > 3 && ( +{result.genres.length - 3} )}
)} {/* Platform icons โ€” always show all 3, color if present */}
{result.controllerSupport && ( Controller )}
{/* Row 4: Stats row */}
{hasData ? ( <> {counts!.benchmarks > 0 && ( )} {counts!.presets > 0 && ( )} {counts!.comments > 0 && ( )} ) : isLocal ? ( No data yet โ€” be the first to contribute ) : ( )}
{/* Row 5: Anti-cheat info */} {result.platformSupport?.antiCheatRelevant && (
Anti-cheat: {result.platformSupport.antiCheatName || "Unknown"} {" "}โ€” {result.platformSupport.antiCheatStatus}
)}
{/* Right Panel โ€” Desktop Only */}
{/* Deck Status */} {/* Best FPS */} = 60 ? "text-green-400" : undefined} /> {/* Battery Estimate */} {result.estimatedBatteryMin != null && ( )} {/* Version */} {/* Playability badge */} {result.playabilityStatus && ( )} {/* Anti-cheat badge */} {result.antiCheatRelevant && result.antiCheatStatus === "unsupported" && ( )} {/* Steam review score */} {result.steamReviewScore != null && ( {result.steamReviewScore}% Positive )}
) } function DataField({ label, value, bar, color, }: { label: string value: string bar?: boolean color?: string }) { return (
{label}
{bar && (
)} {value}
) } function protonLabel(status: string): string { const map: Record = { native: "Native", proton: "Proton", unsupported: "Unsupported", unknown: "Unknown", } return map[status] || status } function protonColor(status: string): string { const map: Record = { native: "text-green-400", proton: "text-blue-400", unsupported: "text-red-400", unknown: "text-text/25", } return map[status] || "text-text/25" } function GameCover({ image, tinyImage, title }: { image: string | null; tinyImage?: string | null; title: string }) { const [src, setSrc] = useState(image) const [fallbackStage, setFallbackStage] = useState(0) const handleError = () => { if (fallbackStage === 0 && tinyImage) { setFallbackStage(1) setSrc(tinyImage) } else { setFallbackStage(2) setSrc(null) } } if (src) { return ( {title} ) } return (
) } function PriceTag({ price, }: { price?: { currency: string; initial: number; final: number } | null }) { if (!price) { return null } // Price is 0 and initial is 0 โ†’ legitimately free-to-play if (price.initial === 0 && price.final === 0) { return ( Free ) } // Price is 0 but initial > 0 โ†’ promotional free (free weekend etc.) if (price.final === 0 && price.initial > 0) { return ( Free* ) } // Normal paid game const isDiscounted = price.final < price.initial const fmt = new Intl.NumberFormat("en-US", { style: "currency", currency: price.currency, }) return ( {isDiscounted && ( {fmt.format(price.initial / 100)} )} {fmt.format(price.final / 100)} ) } function StatBadge({ icon: Icon, count, label, }: { icon: React.ElementType count: number label: string }) { return ( {count} {label} ) } export default function SearchPage() { return ( ) }