feat: implement search functionality with results display, add search input component, and update API for search results

This commit is contained in:
2025-09-07 12:54:39 +02:00
parent 69eb225c1e
commit c9b8bd7b07
7 changed files with 171 additions and 2 deletions

View File

@@ -3,6 +3,7 @@
import Link from "next/link";
import { usePathname } from "next/navigation";
import { cn } from "@/lib/utils";
import { SearchInput } from "./search-input";
export function MainNav() {
const pathname = usePathname();
@@ -17,6 +18,7 @@ export function MainNav() {
>
Feed
</Link>
<SearchInput />
</nav>
);
}

View File

@@ -46,7 +46,7 @@ export function PostThoughtForm() {
toast.success("Your thought has been posted!");
form.reset();
router.refresh(); // This is the key to updating the feed
} catch (err) {
} catch {
toast.error("Failed to post thought. Please try again.");
}
}

View File

@@ -0,0 +1,29 @@
"use client";
import { useRouter } from "next/navigation";
import { Input } from "./ui/input";
import { Search as SearchIcon } from "lucide-react";
export function SearchInput() {
const router = useRouter();
const handleSearch = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
const formData = new FormData(e.currentTarget);
const query = formData.get("q") as string;
if (query) {
router.push(`/search?q=${encodeURIComponent(query)}`);
}
};
return (
<form onSubmit={handleSearch} className="relative w-full max-w-sm">
<SearchIcon className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
<Input
name="q"
placeholder="Search for users or thoughts..."
className="pl-9 md:min-w-[250px]"
/>
</form>
);
}

View File

@@ -0,0 +1,47 @@
import { Me, Thought } from "@/lib/api";
import { ThoughtCard } from "./thought-card";
import { Card, CardContent } from "./ui/card";
interface ThoughtListProps {
thoughts: Thought[];
authorDetails: Map<string, { avatarUrl?: string | null }>;
currentUser: Me | null;
}
export function ThoughtList({
thoughts,
authorDetails,
currentUser,
}: ThoughtListProps) {
if (thoughts.length === 0) {
return (
<p className="text-center text-muted-foreground pt-8">
No thoughts to display.
</p>
);
}
return (
<Card>
<CardContent className="divide-y p-0">
<div className="space-y-6 p-4">
{thoughts.map((thought) => {
const author = {
username: thought.authorUsername,
avatarUrl: null,
...authorDetails.get(thought.authorUsername),
};
return (
<ThoughtCard
key={thought.id}
thought={thought}
author={author}
currentUser={currentUser}
/>
);
})}
</div>
</CardContent>
</Card>
);
}

View File

@@ -10,7 +10,7 @@ function Input({ className, type, ...props }: React.ComponentProps<"input">) {
className={cn(
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive shadow-fa-inner transition-shadow",
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive shadow-fa-inner transition-shadow glass-effect glossy-effect bottom",
className
)}
{...props}