Spaces:
Sleeping
Sleeping
File size: 4,929 Bytes
81d1e4c 7a6a641 81d1e4c | 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 | import { useState } from "react";
import { useTranslation } from "react-i18next";
import { useQuery } from "@tanstack/react-query";
import { projectsService } from "@/services/projects.service";
import { ProjectCard } from "@/components/cards/ProjectCard";
import { ProjectModal } from "@/components/modals/ProjectModal";
import { FadeIn } from "@/components/animations/FadeIn";
import { Skeleton } from "@/components/common/Skeleton";
import type { Project } from "@/types";
import { Search } from "lucide-react";
export function ProjectsSection() {
const { t } = useTranslation();
const [search, setSearch] = useState("");
const [category, setCategory] = useState("");
const [selectedProject, setSelectedProject] = useState<Project | null>(null);
const { data, isLoading } = useQuery({
queryKey: ["projects", search, category],
queryFn: () => projectsService.getAll({ search, category }),
});
const projects: Project[] = data?.data?.data || [];
const categories = [...new Set(projects.map((p) => p.categoryEn))];
return (
<section id="projects" className="py-20">
<div className="max-w-7xl mx-auto px-4">
<FadeIn>
<h2 className="text-3xl font-bold text-center text-slate-900 dark:text-white mb-8">{t("projects.title")}</h2>
</FadeIn>
<FadeIn>
<div className="flex flex-col sm:flex-row gap-4 justify-center mb-8">
<div className="relative max-w-md w-full mx-auto sm:mx-0">
<Search className="absolute left-3 top-3 w-4 h-4 text-slate-400" />
<input
type="text"
placeholder={t("projects.search")}
value={search}
onChange={(e) => setSearch(e.target.value)}
className="w-full pl-10 pr-4 py-2 rounded-lg border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-800 text-slate-900 dark:text-white focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none"
/>
</div>
<div className="flex gap-2 justify-center flex-wrap">
<button
onClick={() => setCategory("")}
className={`px-4 py-2 rounded-lg text-sm font-medium transition-colors ${
!category ? "bg-blue-600 text-white" : "bg-slate-100 dark:bg-slate-800 text-slate-600 dark:text-slate-300 hover:bg-slate-200 dark:hover:bg-slate-700"
}`}
>
{t("projects.all")}
</button>
{categories.map((cat) => (
<button
key={cat}
onClick={() => setCategory(cat)}
className={`px-4 py-2 rounded-lg text-sm font-medium transition-colors ${
category === cat ? "bg-blue-600 text-white" : "bg-slate-100 dark:bg-slate-800 text-slate-600 dark:text-slate-300 hover:bg-slate-200 dark:hover:bg-slate-700"
}`}
>
{cat}
</button>
))}
</div>
</div>
</FadeIn>
{isLoading ? (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{Array.from({ length: 6 }).map((_, i) => (
<div key={i} className="bg-white dark:bg-slate-900 rounded-2xl border border-slate-200 dark:border-slate-800 overflow-hidden">
<div className="relative h-56 bg-slate-200 dark:bg-slate-800 animate-pulse">
<div className="absolute top-4 right-4 w-12 h-12 rounded-full bg-slate-300 dark:bg-slate-700" />
</div>
<div className="p-5 space-y-3">
<div className="h-5 w-20 bg-slate-200 dark:bg-slate-800 rounded-full" />
<div className="h-5 w-3/4 bg-slate-200 dark:bg-slate-800 rounded" />
<div className="space-y-2">
<div className="h-3 w-full bg-slate-200 dark:bg-slate-800 rounded" />
<div className="h-3 w-2/3 bg-slate-200 dark:bg-slate-800 rounded" />
</div>
<div className="flex gap-2 pt-1">
<div className="h-6 w-16 bg-slate-200 dark:bg-slate-800 rounded" />
<div className="h-6 w-16 bg-slate-200 dark:bg-slate-800 rounded" />
</div>
</div>
</div>
))}
</div>
) : (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{projects.map((project, i) => (
<FadeIn key={project.id} delay={i * 0.1}>
<ProjectCard project={project} onClick={() => setSelectedProject(project)} />
</FadeIn>
))}
</div>
)}
<ProjectModal project={selectedProject} open={!!selectedProject} onClose={() => setSelectedProject(null)} />
</div>
</section>
);
} |