fazeel007's picture
initial commit
7c012de
import { useState } from "react";
import { Button } from "@/components/ui/button";
import { SortAsc, Filter, Circle } from "lucide-react";
import ResultCard from "./result-card";
import { type SearchResponse, type DocumentWithContext } from "@shared/schema";
interface SearchResultsProps {
results?: SearchResponse;
expandedResults: Set<number>;
savedDocuments?: Set<number>;
onToggleExpanded: (resultId: number) => void;
onAddCitation: (documentId: number, citationText: string, section?: string, pageNumber?: number) => void;
onSaveDocument?: (documentId: number) => void;
isLoading?: boolean;
error?: Error | null;
}
export default function SearchResults({
results,
expandedResults,
savedDocuments,
onToggleExpanded,
onAddCitation,
onSaveDocument,
isLoading,
error
}: SearchResultsProps) {
const [sortBy, setSortBy] = useState<"relevance" | "date" | "title">("relevance");
if (error) {
return null; // Error is handled in parent component
}
if (isLoading) {
return (
<div className="space-y-4">
{[...Array(3)].map((_, i) => (
<div key={i} className="bg-white rounded-xl shadow-sm border border-slate-200 p-6 animate-pulse">
<div className="flex items-start justify-between mb-4">
<div className="flex-1 space-y-2">
<div className="flex items-center gap-3 mb-2">
<div className="w-4 h-4 bg-slate-200 rounded"></div>
<div className="w-20 h-4 bg-slate-200 rounded"></div>
<div className="w-16 h-6 bg-slate-200 rounded-full"></div>
</div>
<div className="w-3/4 h-6 bg-slate-200 rounded"></div>
<div className="w-1/2 h-4 bg-slate-200 rounded"></div>
</div>
<div className="w-8 h-8 bg-slate-200 rounded"></div>
</div>
<div className="bg-slate-50 rounded-lg p-4">
<div className="space-y-2">
<div className="w-full h-4 bg-slate-200 rounded"></div>
<div className="w-5/6 h-4 bg-slate-200 rounded"></div>
<div className="w-4/6 h-4 bg-slate-200 rounded"></div>
</div>
</div>
</div>
))}
</div>
);
}
const sortedResults = results?.results ? [...results.results].sort((a, b) => {
switch (sortBy) {
case "relevance":
return b.relevanceScore - a.relevanceScore;
case "date":
return new Date(b.createdAt || 0).getTime() - new Date(a.createdAt || 0).getTime();
case "title":
return a.title.localeCompare(b.title);
default:
return 0;
}
}) : [];
if (!results) {
return null;
}
return (
<div>
{/* Results Statistics */}
<div className="flex items-center justify-between mb-6">
<div className="flex items-center gap-4">
<span className="text-sm text-slate-600">
<span className="font-medium">{results?.totalCount || 0}</span> results found in{" "}
<span className="font-medium">{results?.searchTime?.toFixed(2) || '0.00'}</span> seconds
</span>
<div className="flex items-center gap-2">
<Circle className="w-2 h-2 fill-emerald-500 text-emerald-500" />
<span className="text-sm text-slate-600">Vector index active</span>
</div>
</div>
<div className="flex items-center gap-2">
<Button
variant="ghost"
size="sm"
onClick={() => {
const nextSort = sortBy === "relevance" ? "date" : sortBy === "date" ? "title" : "relevance";
setSortBy(nextSort);
}}
className="text-slate-400 hover:text-slate-600"
>
<SortAsc className="w-4 h-4" />
</Button>
<Button
variant="ghost"
size="sm"
className="text-slate-400 hover:text-slate-600"
>
<Filter className="w-4 h-4" />
</Button>
</div>
</div>
{/* Search Results */}
<div className="space-y-4">
{sortedResults.map((result) => (
<ResultCard
key={result.id}
document={result}
isExpanded={expandedResults.has(result.id)}
isSaved={savedDocuments?.has(result.id) || false}
onToggleExpanded={() => onToggleExpanded(result.id)}
onAddCitation={onAddCitation}
onSaveDocument={onSaveDocument}
/>
))}
</div>
{/* Load More */}
{results?.results && results.results.length > 0 && results.totalCount > results.results.length && (
<div className="mt-8 text-center">
<Button
variant="outline"
className="px-6 py-3"
>
Load More Results
</Button>
</div>
)}
</div>
);
}