migrants-nt-web/src/components/home/SearchResults.tsx

159 lines
5.4 KiB
TypeScript

"use client";
import { useNavigate } from "react-router-dom";
import { motion, AnimatePresence } from "framer-motion";
import type { SearchResult } from "@/types/search";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Skeleton } from "@/components/ui/skeleton";
import AnimatedImage from "@/components/ui/animated-image";
interface SearchResultsProps {
results: SearchResult[];
isLoading: boolean;
hasSearched?: boolean;
}
export default function SearchResults({
results,
isLoading,
hasSearched = false,
}: SearchResultsProps) {
const navigate = useNavigate();
// Define animation variants
const container = {
hidden: { opacity: 0 },
show: {
opacity: 1,
transition: {
staggerChildren: 0.1,
},
},
};
const item = {
hidden: { opacity: 0, y: 20 },
show: { opacity: 1, y: 0, transition: { duration: 0.5 } },
};
if (isLoading) {
return (
<div>
<h3 className="text-2xl font-semibold mb-6 font-serif">
Search Results
</h3>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{[...Array(6)].map((_, i) => (
<Card
key={i}
className="overflow-hidden border border-gray-200 shadow-none rounded-md bg-white"
>
<div className="relative h-48 w-full">
<Skeleton className="h-full w-full" />
</div>
<CardHeader>
<Skeleton className="h-6 w-3/4 mb-2" />
<Skeleton className="h-4 w-1/2" />
</CardHeader>
<CardContent>
<Skeleton className="h-4 w-full mb-2" />
<Skeleton className="h-4 w-full mb-2" />
<Skeleton className="h-4 w-3/4" />
</CardContent>
</Card>
))}
</div>
</div>
);
}
if (results.length === 0 && hasSearched) {
return (
<motion.div
className="text-center py-12 bg-gray-50 rounded-lg border border-gray-200"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
>
<h3 className="text-2xl font-semibold mb-4 font-serif">
No Results Found
</h3>
<p className="text-gray-500">
Try adjusting your search criteria to find more records.
</p>
</motion.div>
);
}
if (results.length === 0) {
return null;
}
return (
<div>
<h3 className="text-2xl font-semibold mb-6 font-serif">
Search Results ({results.length})
</h3>
<AnimatePresence>
<motion.div
className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"
variants={container}
initial="hidden"
animate="show"
>
{results.map((person) => (
<motion.div key={person.person_id || person.id_card_no} variants={item} onClick={() => navigate(`/migrants/${person.person_id}`)}>
<div className="block h-full cursor-pointer">
<Card className="overflow-hidden hover:shadow-lg transition-shadow h-full border border-gray-200 group">
<div className="relative h-48 w-full overflow-hidden">
<AnimatedImage
src={
"/placeholder.svg?height=300&width=300"
}
alt=""
fill
/>
<div className="absolute inset-0 bg-gradient-to-t from-black/70 via-transparent to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300" />
<div className="absolute bottom-0 left-0 right-0 p-3 transform translate-y-full group-hover:translate-y-0 transition-transform duration-300">
<div className="flex space-x-1">
<div className="h-6 w-2 bg-green-600" />
<div className="h-6 w-2 bg-white" />
<div className="h-6 w-2 bg-red-600" />
</div>
</div>
</div>
<CardHeader>
<CardTitle className="font-serif">
{person.full_name}
</CardTitle>
<p className="text-sm text-gray-500">
{person.migration?.date_of_arrival_nt ?
`Arrived ${new Date(person.migration.date_of_arrival_nt).getFullYear()}` : 'Date unknown'}
</p>
</CardHeader>
<CardContent>
<div className="space-y-2">
<p>
<span className="font-medium">From:</span>{" "}
{person.place_of_birth || 'Unknown'}, Italy
</p>
<p>
<span className="font-medium">Settled in:</span>{" "}
{person.residence?.town_or_city || 'Unknown'}, NT
</p>
<p>
<span className="font-medium">Occupation:</span>{" "}
{person.occupation || 'Unknown'}
</p>
</div>
</CardContent>
</Card>
</div>
</motion.div>
))}
</motion.div>
</AnimatePresence>
</div>
);
}