test: add CI/CD workflow
This commit is contained in:
parent
66d44c0774
commit
ce97671da3
28 changed files with 11684 additions and 1199 deletions
15
ui/components/BusinessList.tsx
Normal file
15
ui/components/BusinessList.tsx
Normal file
|
@ -0,0 +1,15 @@
|
|||
const BusinessList = ({ businesses }: { businesses: BusinessData[] }) => {
|
||||
console.log('Rendering BusinessList with:', businesses);
|
||||
|
||||
if (!businesses.length) {
|
||||
return <div>No businesses found</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{businesses.map(business => (
|
||||
<BusinessCard key={business.id} business={business} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
148
ui/components/BusinessResults.tsx
Normal file
148
ui/components/BusinessResults.tsx
Normal file
|
@ -0,0 +1,148 @@
|
|||
import React, { useState } from 'react';
|
||||
import { Business } from '../../types/business';
|
||||
|
||||
interface Props {
|
||||
businesses: Business[];
|
||||
onExport: (format: 'csv' | 'json') => void;
|
||||
onSearch: (query: string) => void;
|
||||
}
|
||||
|
||||
export const BusinessResults: React.FC<Props> = ({ businesses, onExport, onSearch }) => {
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [progress, setProgress] = useState({ status: '', percent: 0 });
|
||||
const [searchResults, setSearchResults] = useState<Business[]>([]);
|
||||
|
||||
const handleSearchResponse = (data: any) => {
|
||||
console.log('Received search response:', data);
|
||||
|
||||
if (data.type === 'error') {
|
||||
setError(data.error);
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.type === 'progress') {
|
||||
setProgress({ status: data.status, percent: data.progress });
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.type === 'results') {
|
||||
console.log('Setting results:', data.results);
|
||||
setSearchResults(data.results);
|
||||
onSearch(data.results); // Pass results up to parent
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSearch = async (query: string) => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
setProgress({ status: 'Starting search...', percent: 0 });
|
||||
|
||||
try {
|
||||
const response = await fetch(
|
||||
`http://localhost:3000/api/search?q=${encodeURIComponent(query)}`,
|
||||
{
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Cache-Control': 'no-cache'
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const reader = response.body?.getReader();
|
||||
if (!reader) throw new Error('No response body');
|
||||
|
||||
const decoder = new TextDecoder();
|
||||
let buffer = '';
|
||||
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
|
||||
buffer += decoder.decode(value, { stream: true });
|
||||
const lines = buffer.split('\n');
|
||||
buffer = lines.pop() || '';
|
||||
|
||||
for (const line of lines) {
|
||||
try {
|
||||
if (line.trim()) {
|
||||
const data = JSON.parse(line);
|
||||
handleSearchResponse(data);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error parsing JSON:', e, 'Line:', line);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Search error:', error);
|
||||
setError('Failed to fetch results');
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="business-results">
|
||||
<div className="search-controls">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search businesses..."
|
||||
onKeyPress={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
handleSearch(e.currentTarget.value);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{loading && (
|
||||
<div className="progress">
|
||||
{progress.status} ({progress.percent}%)
|
||||
</div>
|
||||
)}
|
||||
{error && (
|
||||
<div className="error">
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="export-controls">
|
||||
<button onClick={() => onExport('csv')}>Export CSV</button>
|
||||
<button onClick={() => onExport('json')}>Export JSON</button>
|
||||
</div>
|
||||
|
||||
<table className="business-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Business Name</th>
|
||||
<th>Contact</th>
|
||||
<th>Address</th>
|
||||
<th>Rating</th>
|
||||
<th>Website</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{(searchResults.length ? searchResults : businesses).map(business => (
|
||||
<tr key={business.id}>
|
||||
<td>{business.name}</td>
|
||||
<td>
|
||||
{business.phone}<br/>
|
||||
{business.email}
|
||||
</td>
|
||||
<td>{business.address}</td>
|
||||
<td>{business.rating}/5</td>
|
||||
<td>
|
||||
{business.website && (
|
||||
<a href={business.website} target="_blank" rel="noopener noreferrer">
|
||||
Visit Website
|
||||
</a>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue