1+ 'use client' ;
2+
3+ import Image from "next/image" ;
4+ import { Search , LibraryBigIcon , Code , Layers } from "lucide-react" ;
5+ import { Badge } from "@/components/ui/badge" ;
6+ import { Card } from "@/components/ui/card" ;
7+ import { CardContent } from "@/components/ui/card" ;
8+ import { ContextItem , RepoContextItem , SearchContextItem } from "@/features/chat/components/chatBox/contextSelector" ;
9+ import { DemoExamples , DemoSearchExample , DemoSearchContextExample , DemoSearchContext } from "@/types" ;
10+ import { cn , getCodeHostIcon } from "@/lib/utils" ;
11+ import { RepositoryQuery , SearchContextQuery } from "@/lib/types" ;
12+
13+ interface AskSourcebotDemoCardsProps {
14+ demoExamples : DemoExamples ;
15+ selectedItems : ContextItem [ ] ;
16+ setSelectedItems : ( items : ContextItem [ ] ) => void ;
17+ searchContexts : SearchContextQuery [ ] ;
18+ repos : RepositoryQuery [ ] ;
19+ }
20+
21+ export const AskSourcebotDemoCards = ( {
22+ demoExamples,
23+ selectedItems,
24+ setSelectedItems,
25+ searchContexts,
26+ repos,
27+ } : AskSourcebotDemoCardsProps ) => {
28+ const handleExampleClick = ( example : DemoSearchExample ) => {
29+ if ( example . url ) {
30+ window . open ( example . url , '_blank' ) ;
31+ }
32+ }
33+
34+ const getContextIcon = ( context : DemoSearchContext , size : number = 20 ) => {
35+ const sizeClass = size === 12 ? "h-3 w-3" : "h-5 w-5" ;
36+
37+ if ( context . type === "set" ) {
38+ return < LibraryBigIcon className = { cn ( sizeClass , "text-muted-foreground" ) } /> ;
39+ }
40+
41+ if ( context . codeHostType ) {
42+ const codeHostIcon = getCodeHostIcon ( context . codeHostType ) ;
43+ if ( codeHostIcon ) {
44+ return (
45+ < Image
46+ src = { codeHostIcon . src }
47+ alt = { `${ context . codeHostType } icon` }
48+ width = { size }
49+ height = { size }
50+ className = { cn ( sizeClass , codeHostIcon . className ) }
51+ />
52+ ) ;
53+ }
54+ }
55+
56+ return < Code className = { cn ( sizeClass , "text-muted-foreground" ) } /> ;
57+ }
58+
59+ const handleContextClick = ( demoSearchContexts : DemoSearchContext [ ] , contextExample : DemoSearchContextExample ) => {
60+ const context = demoSearchContexts . find ( ( context ) => context . id === contextExample . searchContext )
61+ if ( ! context ) {
62+ console . error ( `Search context ${ contextExample . searchContext } not found on handleContextClick` ) ;
63+ return ;
64+ }
65+
66+ if ( context . type === "set" ) {
67+ const searchContext = searchContexts . find ( ( item ) => item . name === context . value ) ;
68+ if ( ! searchContext ) {
69+ console . error ( `Search context ${ context . value } not found on handleContextClick` ) ;
70+ return ;
71+ }
72+
73+ const isSelected = selectedItems . some (
74+ ( selected ) => selected . type === 'context' && selected . value === context . value
75+ ) ;
76+ const newSelectedItems = isSelected
77+ ? selectedItems . filter (
78+ ( selected ) => ! ( selected . type === 'context' && selected . value === context . value )
79+ )
80+ : [ ...selectedItems , { type : 'context' , value : context . value , name : context . displayName , repoCount : searchContext . repoNames . length } as SearchContextItem ] ;
81+
82+ setSelectedItems ( newSelectedItems ) ;
83+ } else {
84+ const repo = repos . find ( ( repo ) => repo . repoName === context . value ) ;
85+ if ( ! repo ) {
86+ console . error ( `Repo ${ context . value } not found on handleContextClick` ) ;
87+ return ;
88+ }
89+
90+ const isSelected = selectedItems . some (
91+ ( selected ) => selected . type === 'repo' && selected . value === context . value
92+ ) ;
93+ const newSelectedItems = isSelected
94+ ? selectedItems . filter (
95+ ( selected ) => ! ( selected . type === 'repo' && selected . value === context . value )
96+ )
97+ : [ ...selectedItems , { type : 'repo' , value : context . value , name : context . displayName , codeHostType : repo . codeHostType } as RepoContextItem ] ;
98+
99+ setSelectedItems ( newSelectedItems ) ;
100+ }
101+ }
102+
103+ return (
104+ < div className = "w-full mt-8 space-y-12 px-4 max-w-[1200px]" >
105+ { /* Search Context Row */ }
106+ < div className = "space-y-4" >
107+ < div className = "text-center mb-8" >
108+ < div className = "flex items-center justify-center gap-2 mb-2" >
109+ < Layers className = "h-5 w-5 text-muted-foreground" />
110+ < h3 className = "text-lg font-semibold" > Search Context</ h3 >
111+ </ div >
112+ < p className = "text-sm text-muted-foreground" > Select the context you want to ask questions about</ p >
113+ </ div >
114+ < div className = "flex flex-wrap justify-center gap-3" >
115+ { demoExamples . searchContextExamples . map ( ( contextExample ) => {
116+ const context = demoExamples . searchContexts . find ( ( context ) => context . id === contextExample . searchContext )
117+ if ( ! context ) {
118+ console . error ( `Search context ${ contextExample . searchContext } not found on handleContextClick` ) ;
119+ return ;
120+ }
121+
122+ const isSelected = selectedItems . some (
123+ ( selected ) => ( selected . type === 'context' && selected . value === context . value ) ||
124+ ( selected . type === 'repo' && selected . value === context . value )
125+ ) ;
126+
127+ const searchContext = searchContexts . find ( ( item ) => item . name === context . value ) ;
128+ const numRepos = searchContext ? searchContext . repoNames . length : undefined ;
129+ return (
130+ < Card
131+ key = { context . value }
132+ className = { `cursor-pointer transition-all duration-200 hover:shadow-md hover:scale-105 group w-full max-w-[280px] ${ isSelected ? "border-primary bg-primary/5 shadow-sm" : "hover:border-primary/50"
133+ } `}
134+ onClick = { ( ) => handleContextClick ( demoExamples . searchContexts , contextExample ) }
135+ >
136+ < CardContent className = "p-4" >
137+ < div className = "flex items-start gap-3" >
138+ < div
139+ className = { `flex-shrink-0 p-2 rounded-lg transition-transform group-hover:scale-105` }
140+ >
141+ { getContextIcon ( context ) }
142+ </ div >
143+ < div className = "flex-1 min-w-0" >
144+ < div className = "flex items-center gap-2 mb-1" >
145+ < h4
146+ className = { `font-medium text-sm transition-colors ${ isSelected ? "text-primary" : "group-hover:text-primary"
147+ } `}
148+ >
149+ { context . displayName }
150+ </ h4 >
151+ { numRepos && (
152+ < Badge className = "text-[10px] px-1.5 py-0.5 h-4" >
153+ { numRepos } repos
154+ </ Badge >
155+ ) }
156+ </ div >
157+ < p className = "text-xs text-muted-foreground" > { contextExample . description } </ p >
158+ </ div >
159+ </ div >
160+ </ CardContent >
161+ </ Card >
162+ )
163+ } ) }
164+ </ div >
165+ </ div >
166+
167+ { /* Example Searches Row */ }
168+ < div className = "space-y-4" >
169+ < div className = "text-center mb-8" >
170+ < div className = "flex items-center justify-center gap-2 mb-2" >
171+ < Search className = "h-5 w-5 text-muted-foreground" />
172+ < h3 className = "text-lg font-semibold" > Community Ask Results</ h3 >
173+ </ div >
174+ < p className = "text-sm text-muted-foreground" > Check out these featured ask results from the community</ p >
175+ </ div >
176+ < div className = "flex flex-wrap justify-center gap-3" >
177+ { demoExamples . searchExamples . map ( ( example ) => {
178+ const searchContexts = demoExamples . searchContexts . filter ( ( context ) => example . searchContext . includes ( context . id ) )
179+ return (
180+ < Card
181+ key = { example . url }
182+ className = "cursor-pointer transition-all duration-200 hover:shadow-md hover:scale-105 hover:border-primary/50 group w-full max-w-[350px]"
183+ onClick = { ( ) => handleExampleClick ( example ) }
184+ >
185+ < CardContent className = "p-4" >
186+ < div className = "space-y-3" >
187+ < div className = "flex items-center justify-between" >
188+ { searchContexts . map ( ( context ) => (
189+ < Badge key = { context . value } variant = "secondary" className = "text-[10px] px-1.5 py-0.5 h-4 flex items-center gap-1" >
190+ { getContextIcon ( context , 12 ) }
191+ { context . displayName }
192+ </ Badge >
193+ ) ) }
194+ </ div >
195+ < div className = "space-y-1" >
196+ < h4 className = "font-semibold text-sm group-hover:text-primary transition-colors line-clamp-2" >
197+ { example . title }
198+ </ h4 >
199+ < p className = "text-xs text-muted-foreground line-clamp-3 leading-relaxed" >
200+ { example . description }
201+ </ p >
202+ </ div >
203+ </ div >
204+ </ CardContent >
205+ </ Card >
206+ ) } ) }
207+ </ div >
208+ </ div >
209+ </ div >
210+ ) ;
211+ } ;
0 commit comments