@@ -2,13 +2,14 @@ import Link from "next/link"
22import { Suspense } from "react"
33import { Button } from "@/components/ui/button"
44import { Input } from "@/components/ui/input"
5- import { Card , CardContent } from "@/components/ui/card"
5+ import { Card , CardContent , CardHeader , CardTitle } from "@/components/ui/card"
66import { Separator } from "@/components/ui/separator"
7- import { Search } from "lucide-react"
8- import { marketQueries } from "@/lib/db/queries"
7+ import { Badge } from "@/components/ui/badge"
8+ import { Search , TrendingUp , Calendar , Tag } from "lucide-react"
9+ import { searchQueries , marketQueries } from "@/lib/db/queries"
910import MarketDetailsCard from "@/components/market-details-card"
1011import { generateMarketURL } from "@/lib/utils"
11- import { SearchBox } from "@/components/search-box "
12+ import { SearchInput } from "@/components/search-input "
1213
1314interface SearchPageProps {
1415 searchParams : Promise < { q ?: string ; cursor ?: string ; sort ?: string ; status ?: string } >
@@ -17,54 +18,159 @@ interface SearchPageProps {
1718export default async function SearchPage ( { searchParams } : SearchPageProps ) {
1819 const { q, cursor, sort = 'trending' , status = 'active' } = await searchParams
1920 const query = ( q || "" ) . trim ( )
20- const { items, nextCursor } = query
21- ? await marketQueries . searchMarkets ( query , {
21+
22+ // Use unified search for all entity types
23+ const results = query
24+ ? await searchQueries . searchAll ( query , {
25+ includeMarkets : true ,
26+ includeEvents : true ,
27+ includeTags : true ,
2228 limit : 20 ,
23- sort : ( sort as any ) ?? 'trending' ,
24- status : ( status as any ) ?? 'active' ,
25- cursorId : cursor ?? null ,
29+ marketOptions : {
30+ sort : ( sort as any ) ?? 'trending' ,
31+ status : ( status as any ) ?? 'active' ,
32+ cursorId : cursor ?? null ,
33+ limit : 20
34+ }
2635 } )
27- : { items : [ ] , nextCursor : null }
36+ : { markets : [ ] , events : [ ] , tags : [ ] , totalResults : 0 , suggestions : [ ] }
2837
2938 const marketsWithUrl = await Promise . all (
30- items . map ( async ( m ) => ( { market : m , url : await generateMarketURL ( m . id ) } ) )
39+ results . markets . map ( async ( m ) => ( { market : m , url : await generateMarketURL ( m . id ) } ) )
3140 )
3241
3342 return (
3443 < div className = "min-h-screen bg-background" >
3544 < div className = "container mx-auto px-4 py-6" >
3645 < div className = "mb-6" >
3746 < h1 className = "text-2xl font-bold text-foreground mb-2" >
38- { `Showing ${ marketsWithUrl . length } result${ marketsWithUrl . length === 1 ? "" : "s" } ${ query ? ` for ${ query } ` : "" } ` }
47+ { query ? (
48+ `${ results . totalResults } result${ results . totalResults === 1 ? "" : "s" } for "${ query } "`
49+ ) : (
50+ "Search BetterAI"
51+ ) }
3952 </ h1 >
4053 </ div >
4154
4255 < div className = "flex gap-6" >
43- < div className = "flex-1 space-y-4" >
44- { marketsWithUrl . map ( ( { market, url } ) => (
45- < MarketDetailsCard
46- key = { market . id }
47- market = { market }
48- event = { market . event }
49- externalMarketUrl = { url }
50- latestPrediction = { market . predictions ?. [ 0 ] ?? null }
51- className = "hover:bg-muted/30 transition-colors"
52- href = { `/market/${ market . id } ` }
53- />
54- ) ) }
55- { nextCursor && (
56- < div className = "pt-2" >
57- < Button variant = "outline" asChild className = "w-full" >
58- < Link href = { `/search?${ new URLSearchParams ( { q : query , sort : String ( sort ) , status : String ( status ) , cursor : nextCursor } ) . toString ( ) } ` } >
59- Load more
60- </ Link >
61- </ Button >
56+ < div className = "flex-1 space-y-6" >
57+ { /* Markets Section */ }
58+ { marketsWithUrl . length > 0 && (
59+ < div >
60+ < h2 className = "text-lg font-semibold mb-4 flex items-center gap-2" >
61+ < TrendingUp className = "h-5 w-5 text-blue-500" />
62+ Markets ({ marketsWithUrl . length } )
63+ </ h2 >
64+ < div className = "space-y-4" >
65+ { marketsWithUrl . map ( ( { market, url } ) => (
66+ < MarketDetailsCard
67+ key = { market . id }
68+ market = { market }
69+ event = { market . event }
70+ externalMarketUrl = { url }
71+ latestPrediction = { market . predictions ?. [ 0 ] ?? null }
72+ className = "hover:bg-muted/30 transition-colors"
73+ href = { `/market/${ market . id } ` }
74+ />
75+ ) ) }
76+ </ div >
6277 </ div >
6378 ) }
79+
80+ { /* Events Section */ }
81+ { results . events . length > 0 && (
82+ < div >
83+ < h2 className = "text-lg font-semibold mb-4 flex items-center gap-2" >
84+ < Calendar className = "h-5 w-5 text-green-500" />
85+ Events ({ results . events . length } )
86+ </ h2 >
87+ < div className = "grid gap-4" >
88+ { results . events . map ( ( event ) => (
89+ < Card key = { event . id } className = "hover:bg-muted/30 transition-colors" >
90+ < CardContent className = "p-4" >
91+ < Link
92+ href = { `/event/${ event . slug } ` }
93+ className = "block hover:text-primary transition-colors"
94+ >
95+ < h3 className = "font-medium" > { event . title } </ h3 >
96+ { event . description && (
97+ < p className = "text-sm text-muted-foreground mt-1 line-clamp-2" >
98+ { event . description }
99+ </ p >
100+ ) }
101+ </ Link >
102+ </ CardContent >
103+ </ Card >
104+ ) ) }
105+ </ div >
106+ </ div >
107+ ) }
108+
109+ { /* Tags Section */ }
110+ { results . tags . length > 0 && (
111+ < div >
112+ < h2 className = "text-lg font-semibold mb-4 flex items-center gap-2" >
113+ < Tag className = "h-5 w-5 text-purple-500" />
114+ Tags ({ results . tags . length } )
115+ </ h2 >
116+ < div className = "flex flex-wrap gap-2" >
117+ { results . tags . map ( ( tag ) => (
118+ < Link
119+ key = { tag . id }
120+ href = { `/search?q=${ encodeURIComponent ( tag . label ) } ` }
121+ >
122+ < Badge
123+ variant = "outline"
124+ className = "hover:bg-muted transition-colors cursor-pointer"
125+ >
126+ { tag . label }
127+ { tag . eventCount && tag . eventCount > 0 && (
128+ < span className = "ml-1 text-xs text-muted-foreground" >
129+ ({ tag . eventCount } )
130+ </ span >
131+ ) }
132+ </ Badge >
133+ </ Link >
134+ ) ) }
135+ </ div >
136+ </ div >
137+ ) }
138+
139+ { /* No Results */ }
140+ { query && results . totalResults === 0 && (
141+ < Card >
142+ < CardContent className = "p-6 text-center" >
143+ < p className = "text-muted-foreground mb-4" >
144+ No results found for "{ query } "
145+ </ p >
146+ { results . suggestions && results . suggestions . length > 0 && (
147+ < div >
148+ < p className = "text-sm text-muted-foreground mb-2" > Try searching for:</ p >
149+ < div className = "flex flex-wrap gap-2 justify-center" >
150+ { results . suggestions . map ( ( suggestion , index ) => (
151+ < Link key = { index } href = { `/search?q=${ encodeURIComponent ( suggestion ) } ` } >
152+ < Badge variant = "secondary" className = "hover:bg-muted cursor-pointer" >
153+ { suggestion }
154+ </ Badge >
155+ </ Link >
156+ ) ) }
157+ </ div >
158+ </ div >
159+ ) }
160+ </ CardContent >
161+ </ Card >
162+ ) }
163+
164+ { /* Empty State */ }
64165 { ! query && (
65166 < Card >
66167 < CardContent className = "p-6 text-muted-foreground" >
67- Enter a search term to find markets by question, description, event title/description, or tag label.
168+ Enter a search term to find markets, events, and tags. You can search by:
169+ < ul className = "list-disc list-inside mt-2 space-y-1" >
170+ < li > Market questions and descriptions</ li >
171+ < li > Event titles and descriptions</ li >
172+ < li > Tag labels</ li >
173+ </ ul >
68174 </ CardContent >
69175 </ Card >
70176 ) }
@@ -73,7 +179,7 @@ export default async function SearchPage({ searchParams }: SearchPageProps) {
73179 < div className = "w-80 flex-shrink-0" >
74180 < Card >
75181 < CardContent className = "p-4 space-y-4" >
76- < SearchBox defaultQuery = { query } sort = { String ( sort ) } status = { String ( status ) } />
182+ < SearchInput defaultQuery = { query } sort = { String ( sort ) } status = { String ( status ) } />
77183 < Separator />
78184 { /* Sort options */ }
79185 < div className = "space-y-2" >
0 commit comments