Skip to content

Commit a3b2e5a

Browse files
authored
Merge pull request #122 from DevanshuNEU/feat/111-searchbox-component
feat(frontend): add SearchBox component with glass styling
2 parents 2821145 + c237913 commit a3b2e5a

4 files changed

Lines changed: 251 additions & 92 deletions

File tree

docker-compose.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ services:
2727
environment:
2828
- REDIS_HOST=redis
2929
- REDIS_PORT=6379
30+
- ALLOWED_ORIGINS=http://localhost:3000,http://localhost:5173,http://localhost:5174
3031
- OPENAI_API_KEY=${OPENAI_API_KEY}
3132
- PINECONE_API_KEY=${PINECONE_API_KEY}
3233
- PINECONE_INDEX_NAME=${PINECONE_INDEX_NAME}
Lines changed: 77 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1,100 +1,84 @@
1-
import { useState } from 'react'
2-
import { toast } from 'sonner'
3-
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'
4-
import { oneDark } from 'react-syntax-highlighter/dist/esm/styles/prism'
5-
import type { SearchResult } from '../types'
1+
import { useState } from 'react';
2+
import { toast } from 'sonner';
3+
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
4+
import { oneDark } from 'react-syntax-highlighter/dist/esm/styles/prism';
5+
import { SearchBox } from './search';
6+
import type { SearchResult } from '../types';
67

78
interface SearchPanelProps {
8-
repoId: string
9-
apiUrl: string
10-
apiKey: string
9+
repoId: string;
10+
apiUrl: string;
11+
apiKey: string;
1112
}
1213

1314
export function SearchPanel({ repoId, apiUrl, apiKey }: SearchPanelProps) {
14-
const [query, setQuery] = useState('')
15-
const [results, setResults] = useState<SearchResult[]>([])
16-
const [loading, setLoading] = useState(false)
17-
const [searchTime, setSearchTime] = useState<number | null>(null)
18-
const [cached, setCached] = useState(false)
15+
const [query, setQuery] = useState('');
16+
const [results, setResults] = useState<SearchResult[]>([]);
17+
const [loading, setLoading] = useState(false);
18+
const [searchTime, setSearchTime] = useState<number | null>(null);
19+
const [cached, setCached] = useState(false);
20+
const [hasSearched, setHasSearched] = useState(false);
1921

20-
const handleSearch = async (e: React.FormEvent) => {
21-
e.preventDefault()
22-
if (!query.trim()) return
22+
const handleSearch = async () => {
23+
if (!query.trim()) return;
2324

24-
setLoading(true)
25-
const startTime = Date.now()
25+
setLoading(true);
26+
setHasSearched(true);
27+
const startTime = Date.now();
2628

2729
try {
2830
const response = await fetch(`${apiUrl}/search`, {
2931
method: 'POST',
3032
headers: {
31-
'Authorization': `Bearer ${apiKey}`,
32-
'Content-Type': 'application/json'
33+
Authorization: `Bearer ${apiKey}`,
34+
'Content-Type': 'application/json',
3335
},
3436
body: JSON.stringify({
3537
query,
3638
repo_id: repoId,
37-
max_results: 10
38-
})
39-
})
39+
max_results: 10,
40+
}),
41+
});
4042

41-
const data = await response.json()
42-
setResults(data.results || [])
43-
setSearchTime(Date.now() - startTime)
44-
setCached(data.cached || false)
43+
const data = await response.json();
44+
setResults(data.results || []);
45+
setSearchTime(Date.now() - startTime);
46+
setCached(data.cached || false);
4547
} catch (error) {
46-
console.error('Search error:', error)
48+
console.error('Search error:', error);
4749
toast.error('Search failed', {
48-
description: 'Please check your query and try again'
49-
})
50+
description: 'Please check your query and try again',
51+
});
5052
} finally {
51-
setLoading(false)
53+
setLoading(false);
5254
}
53-
}
55+
};
5456

5557
return (
5658
<div className="p-6 space-y-6">
57-
{/* Search */}
58-
<div className="bg-[#0a0a0c] border border-white/5 rounded-xl p-5">
59-
<form onSubmit={handleSearch}>
60-
<div className="flex gap-3">
61-
<input
62-
type="text"
63-
value={query}
64-
onChange={(e) => setQuery(e.target.value)}
65-
placeholder="e.g., authentication middleware, React hooks, database queries..."
66-
className="flex-1 px-4 py-3 bg-white/5 border border-white/10 rounded-xl text-white placeholder:text-gray-500 focus:outline-none focus:border-blue-500/50 focus:ring-1 focus:ring-blue-500/20 transition-all"
67-
disabled={loading}
68-
autoFocus
69-
/>
70-
<button
71-
type="submit"
72-
className="px-6 py-3 bg-gradient-to-r from-blue-500 to-blue-600 hover:from-blue-600 hover:to-blue-700 text-white font-medium rounded-xl transition-all disabled:opacity-50"
73-
disabled={loading}
74-
>
75-
{loading ? 'Searching...' : 'Search'}
76-
</button>
77-
</div>
78-
<p className="mt-3 text-xs text-gray-500">
79-
Powered by semantic embeddings - finds code by meaning, not just keywords
80-
</p>
81-
</form>
59+
{/* Search Box */}
60+
<div className="card p-5">
61+
<SearchBox
62+
value={query}
63+
onChange={setQuery}
64+
onSubmit={handleSearch}
65+
loading={loading}
66+
autoFocus
67+
/>
8268

8369
{searchTime !== null && (
84-
<div className="mt-4 pt-4 border-t border-white/5 flex items-center gap-4 text-sm text-gray-400">
70+
<div className="mt-4 pt-4 border-t border-border flex items-center gap-4 text-sm text-text-secondary">
8571
<span>
86-
<span className="font-semibold text-white">{results.length}</span> results
72+
<span className="font-semibold text-text-primary">{results.length}</span> results
8773
</span>
88-
<span className="text-gray-600"></span>
74+
<span className="text-text-muted"></span>
8975
<span>
90-
<span className="font-mono font-semibold text-white">{searchTime}ms</span>
76+
<span className="font-mono font-semibold text-text-primary">{searchTime}ms</span>
9177
</span>
9278
{cached && (
9379
<>
94-
<span className="text-gray-600"></span>
95-
<span className="text-xs bg-green-500/10 text-green-400 border border-green-500/20 px-2 py-0.5 rounded-md">
96-
⚡ Cached
97-
</span>
80+
<span className="text-text-muted"></span>
81+
<span className="badge-success">⚡ Cached</span>
9882
</>
9983
)}
10084
</div>
@@ -104,45 +88,48 @@ export function SearchPanel({ repoId, apiUrl, apiKey }: SearchPanelProps) {
10488
{/* Results */}
10589
<div className="space-y-4">
10690
{results.map((result, idx) => (
107-
<div key={idx} className="bg-[#0a0a0c] border border-white/5 rounded-xl p-5 hover:border-white/10 transition-all group">
91+
<div
92+
key={idx}
93+
className="card p-5 hover:border-border-accent transition-all duration-normal group"
94+
>
10895
{/* Header */}
10996
<div className="flex items-start justify-between mb-4">
11097
<div className="flex-1">
11198
<div className="flex items-center gap-2 mb-1">
112-
<h3 className="font-mono font-semibold text-sm text-white">
99+
<h3 className="font-mono font-semibold text-sm text-text-primary">
113100
{result.name}
114101
</h3>
115-
<span className="px-2 py-0.5 text-[10px] uppercase tracking-wide bg-white/5 text-gray-400 border border-white/10 rounded">
102+
<span className="badge-neutral text-[10px] uppercase tracking-wide">
116103
{result.type.replace('_', ' ')}
117104
</span>
118105
</div>
119-
<p className="text-xs text-gray-500 font-mono">
106+
<p className="text-xs text-text-muted font-mono">
120107
{result.file_path.split('/').slice(-3).join('/')}
121108
</p>
122109
</div>
123-
110+
124111
<div className="flex items-center gap-3">
125112
<div className="text-right">
126-
<div className="text-xs font-mono text-gray-500">Match</div>
127-
<div className="text-sm font-mono font-semibold text-blue-400">
113+
<div className="text-xs font-mono text-text-muted">Match</div>
114+
<div className="text-sm font-mono font-semibold text-accent">
128115
{(result.score * 100).toFixed(0)}%
129116
</div>
130117
</div>
131118
<button
132119
onClick={(e) => {
133-
e.stopPropagation()
134-
navigator.clipboard.writeText(result.code)
135-
toast.success('Code copied!')
120+
e.stopPropagation();
121+
navigator.clipboard.writeText(result.code);
122+
toast.success('Code copied!');
136123
}}
137-
className="px-3 py-1.5 text-sm text-gray-400 hover:text-white bg-white/5 hover:bg-white/10 rounded-lg opacity-0 group-hover:opacity-100 transition-all"
124+
className="btn-ghost px-3 py-1.5 text-sm opacity-0 group-hover:opacity-100"
138125
title="Copy code"
139126
>
140127
Copy
141128
</button>
142129
</div>
143130
</div>
144131

145-
{/* Code with Syntax Highlighting */}
132+
{/* Code */}
146133
<div className="relative rounded-lg overflow-hidden">
147134
<SyntaxHighlighter
148135
language={result.language}
@@ -152,47 +139,45 @@ export function SearchPanel({ repoId, apiUrl, apiKey }: SearchPanelProps) {
152139
borderRadius: '0.5rem',
153140
fontSize: '0.75rem',
154141
lineHeight: '1.5',
155-
background: '#0d0d0f',
142+
background: 'var(--color-bg-secondary)',
156143
}}
157144
showLineNumbers
158145
startingLineNumber={result.line_start}
159146
>
160147
{result.code}
161148
</SyntaxHighlighter>
162-
149+
163150
<div className="absolute top-3 right-3">
164-
<span className="px-2 py-0.5 text-[10px] font-mono uppercase bg-black/50 text-gray-400 backdrop-blur rounded">
151+
<span className="px-2 py-0.5 text-[10px] font-mono uppercase glass text-text-muted rounded">
165152
{result.language}
166153
</span>
167154
</div>
168155
</div>
169156

170-
{/* Metadata */}
171-
<div className="mt-3 flex items-center gap-3 text-xs text-gray-500">
157+
{/* Footer */}
158+
<div className="mt-3 flex items-center gap-3 text-xs text-text-muted">
172159
<span className="font-mono">
173160
Lines {result.line_start}{result.line_end}
174161
</span>
175-
<span className="text-gray-600"></span>
176-
<span className="text-gray-500 truncate">
177-
{result.file_path}
178-
</span>
162+
<span></span>
163+
<span className="truncate">{result.file_path}</span>
179164
</div>
180165
</div>
181166
))}
182167
</div>
183168

184169
{/* Empty State */}
185-
{results.length === 0 && query && !loading && (
186-
<div className="bg-[#0a0a0c] border border-white/5 rounded-xl p-16 text-center">
187-
<div className="w-20 h-20 mx-auto mb-4 rounded-2xl bg-white/5 flex items-center justify-center">
170+
{results.length === 0 && hasSearched && !loading && (
171+
<div className="card p-16 text-center">
172+
<div className="w-20 h-20 mx-auto mb-4 rounded-2xl glass flex items-center justify-center">
188173
<span className="text-4xl">🔍</span>
189174
</div>
190-
<h3 className="text-base font-semibold mb-2 text-white">No results found</h3>
191-
<p className="text-sm text-gray-400">
175+
<h3 className="text-base font-semibold mb-2 text-text-primary">No results found</h3>
176+
<p className="text-sm text-text-secondary">
192177
Try a different query or check if the repository is fully indexed
193178
</p>
194179
</div>
195180
)}
196181
</div>
197-
)
182+
);
198183
}

0 commit comments

Comments
 (0)