File size: 4,937 Bytes
7c012de
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
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>
  );
}