Spaces:
Build error
Build error
Vector Search UI & Skeletal Code to send request to backend
Browse files- frontend/app/components/search-section.tsx +34 -0
- frontend/app/components/ui/search/search-input.tsx +44 -0
- frontend/app/components/ui/search/search-results.tsx +31 -0
- frontend/app/components/ui/search/search-types.tsx +6 -0
- frontend/app/components/ui/search/useSearch.tsx +53 -0
- frontend/app/search/page.tsx +2 -2
frontend/app/components/search-section.tsx
ADDED
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
// SearchSection.tsx
|
2 |
+
|
3 |
+
import React, { useState, ChangeEvent, FormEvent } from "react";
|
4 |
+
import useSearch from "@/app/components/ui/search/useSearch";
|
5 |
+
import SearchResults from "@/app/components/ui/search/search-results";
|
6 |
+
import SearchInput from "@/app/components/ui/search/search-input";
|
7 |
+
|
8 |
+
const SearchSection: React.FC = () => {
|
9 |
+
const [query, setQuery] = useState("");
|
10 |
+
const { searchResults, isLoading, handleSearch } = useSearch();
|
11 |
+
|
12 |
+
const handleInputChange = (e: ChangeEvent<HTMLInputElement>) => {
|
13 |
+
setQuery(e.target.value);
|
14 |
+
};
|
15 |
+
|
16 |
+
const handleSearchSubmit = (e: FormEvent) => {
|
17 |
+
e.preventDefault();
|
18 |
+
handleSearch(query);
|
19 |
+
};
|
20 |
+
|
21 |
+
return (
|
22 |
+
<div className="space-y-4 max-w-5xl w-full">
|
23 |
+
<SearchInput
|
24 |
+
query={query}
|
25 |
+
isLoading={isLoading}
|
26 |
+
onInputChange={handleInputChange}
|
27 |
+
onSearchSubmit={handleSearchSubmit}
|
28 |
+
/>
|
29 |
+
<SearchResults results={searchResults} isLoading={isLoading} />
|
30 |
+
</div>
|
31 |
+
);
|
32 |
+
};
|
33 |
+
|
34 |
+
export default SearchSection;
|
frontend/app/components/ui/search/search-input.tsx
ADDED
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
// SearchInput.tsx
|
2 |
+
|
3 |
+
import React, { ChangeEvent, FormEvent } from "react";
|
4 |
+
import { Button } from "../button";
|
5 |
+
import { Input } from "../input";
|
6 |
+
import { Search } from "lucide-react";
|
7 |
+
|
8 |
+
interface SearchInputProps {
|
9 |
+
query: string;
|
10 |
+
isLoading: boolean;
|
11 |
+
onInputChange: (e: ChangeEvent<HTMLInputElement>) => void;
|
12 |
+
onSearchSubmit: (e: FormEvent) => void;
|
13 |
+
}
|
14 |
+
|
15 |
+
const SearchInput: React.FC<SearchInputProps> = ({
|
16 |
+
query,
|
17 |
+
isLoading,
|
18 |
+
onInputChange,
|
19 |
+
onSearchSubmit,
|
20 |
+
}) => {
|
21 |
+
return (
|
22 |
+
<form
|
23 |
+
onSubmit={onSearchSubmit}
|
24 |
+
className="flex w-full items-start justify-between gap-4 rounded-xl bg-white dark:bg-zinc-700/30 p-4 shadow-xl"
|
25 |
+
>
|
26 |
+
<Input
|
27 |
+
autoFocus
|
28 |
+
name="query"
|
29 |
+
placeholder="Enter a text to search..."
|
30 |
+
value={query}
|
31 |
+
onChange={onInputChange}
|
32 |
+
className="flex-1 bg-white dark:bg-zinc-500/30"
|
33 |
+
/>
|
34 |
+
<Button type="submit" disabled={isLoading} className="hidden md:flex items-center transition duration-300 ease-in-out transform hover:scale-110">
|
35 |
+
Search
|
36 |
+
</Button>
|
37 |
+
<Button type="submit" disabled={isLoading} className="md:hidden"> {/* Hide on larger screens */}
|
38 |
+
<Search className="h-5 w-5" />
|
39 |
+
</Button>
|
40 |
+
</form>
|
41 |
+
);
|
42 |
+
};
|
43 |
+
|
44 |
+
export default SearchInput;
|
frontend/app/components/ui/search/search-results.tsx
ADDED
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import React from "react";
|
2 |
+
import { SearchResult } from "./search-types";
|
3 |
+
import { IconSpinner } from "../icons";
|
4 |
+
|
5 |
+
interface SearchResultsProps {
|
6 |
+
results: SearchResult[];
|
7 |
+
isLoading: boolean;
|
8 |
+
}
|
9 |
+
|
10 |
+
const SearchResults: React.FC<SearchResultsProps> = ({ results, isLoading }) => {
|
11 |
+
return (
|
12 |
+
<div className="flex w-full items-start justify-between gap-4 rounded-xl bg-white dark:bg-zinc-700/30 p-4 shadow-xl">
|
13 |
+
{isLoading ? (
|
14 |
+
<div className="flex items-center">
|
15 |
+
<IconSpinner className="mr-2 animate-spin" />
|
16 |
+
<p>Loading...</p>
|
17 |
+
</div>
|
18 |
+
) : null}
|
19 |
+
{!isLoading && results.length === 0 && <p>No results found.</p>}
|
20 |
+
{!isLoading && results.length > 0 && (
|
21 |
+
<ul>
|
22 |
+
{results.map((result) => (
|
23 |
+
<li key={result.id}>{result.title}</li>
|
24 |
+
))}
|
25 |
+
</ul>
|
26 |
+
)}
|
27 |
+
</div>
|
28 |
+
);
|
29 |
+
};
|
30 |
+
|
31 |
+
export default SearchResults;
|
frontend/app/components/ui/search/search-types.tsx
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
// SearchTypes.tsx
|
2 |
+
export interface SearchResult {
|
3 |
+
id: number;
|
4 |
+
title: string;
|
5 |
+
// Add more properties as needed
|
6 |
+
}
|
frontend/app/components/ui/search/useSearch.tsx
ADDED
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { useState, useEffect } from "react";
|
2 |
+
|
3 |
+
interface SearchResult {
|
4 |
+
id: number;
|
5 |
+
title: string;
|
6 |
+
// Add more properties as needed
|
7 |
+
}
|
8 |
+
|
9 |
+
interface UseSearchResult {
|
10 |
+
searchResults: SearchResult[];
|
11 |
+
isLoading: boolean;
|
12 |
+
handleSearch: (query: string) => Promise<void>;
|
13 |
+
}
|
14 |
+
|
15 |
+
const useSearch = (): UseSearchResult => {
|
16 |
+
const [searchResults, setSearchResults] = useState<SearchResult[]>([]);
|
17 |
+
const [isLoading, setIsLoading] = useState(false);
|
18 |
+
|
19 |
+
const handleSearch = async (query: string): Promise<void> => {
|
20 |
+
setIsLoading(true);
|
21 |
+
|
22 |
+
// Perform your search logic here
|
23 |
+
// Replace the following with your actual search API or function
|
24 |
+
try {
|
25 |
+
const response = await fetch(`/api/search?query=${query}`);
|
26 |
+
const data = await response.json();
|
27 |
+
setSearchResults(data);
|
28 |
+
} catch (error) {
|
29 |
+
console.error("Error during search:", error);
|
30 |
+
setSearchResults([]);
|
31 |
+
}
|
32 |
+
|
33 |
+
setIsLoading(false);
|
34 |
+
};
|
35 |
+
|
36 |
+
useEffect(() => {
|
37 |
+
// Initial load logic if needed
|
38 |
+
// ...
|
39 |
+
|
40 |
+
// Cleanup logic if needed
|
41 |
+
return () => {
|
42 |
+
// ...
|
43 |
+
};
|
44 |
+
}, []); // Dependency array depends on your use case
|
45 |
+
|
46 |
+
return {
|
47 |
+
searchResults,
|
48 |
+
isLoading,
|
49 |
+
handleSearch,
|
50 |
+
};
|
51 |
+
};
|
52 |
+
|
53 |
+
export default useSearch;
|
frontend/app/search/page.tsx
CHANGED
@@ -1,14 +1,14 @@
|
|
1 |
"use client";
|
2 |
|
3 |
import Header from "@/app/components/header";
|
4 |
-
import
|
5 |
|
6 |
export default function Search() {
|
7 |
|
8 |
return (
|
9 |
<main id='main-container' className="flex min-h-screen flex-col items-center gap-10 background-gradient dark:background-gradient-dark md:pt-10 pt-24 px-10">
|
10 |
<Header />
|
11 |
-
<
|
12 |
</main>
|
13 |
);
|
14 |
}
|
|
|
1 |
"use client";
|
2 |
|
3 |
import Header from "@/app/components/header";
|
4 |
+
import SearchSection from "@/app/components/search-section";
|
5 |
|
6 |
export default function Search() {
|
7 |
|
8 |
return (
|
9 |
<main id='main-container' className="flex min-h-screen flex-col items-center gap-10 background-gradient dark:background-gradient-dark md:pt-10 pt-24 px-10">
|
10 |
<Header />
|
11 |
+
<SearchSection />
|
12 |
</main>
|
13 |
);
|
14 |
}
|