diff --git a/src/pages/Contributors.jsx b/src/pages/Contributors.jsx index 99234be9..290ff0e3 100644 --- a/src/pages/Contributors.jsx +++ b/src/pages/Contributors.jsx @@ -3,6 +3,7 @@ import React, { useMemo, useState } from "react"; import { Link } from "react-router-dom"; import { getRepos, getContributorsByRepo, getAllContributors } from "../lib/contributors/data"; import FilterPill from "../components/FilterPill"; +import { Search } from "lucide-react"; /* ---------------- Repo Filter Pill ---------------- */ @@ -89,6 +90,23 @@ function ContributorCard({ c }) { ); } +/* ---------------- Search Bar ---------------- */ + +function SearchBar({ value, onChange, placeholder = "Search contributors..." }) { + return ( +
+ + onChange(e.target.value)} + placeholder={placeholder} + className="w-full rounded-xl border border-white/15 bg-white/5 py-3 pl-10 pr-4 text-white placeholder-white/50 backdrop-blur transition focus:border-emerald-400/50 focus:bg-white/10 focus:outline-none focus:ring-2 focus:ring-emerald-400/20" + /> +
+ ); +} + /* ---------------- Page ---------------- */ export default function Contributors() { @@ -97,15 +115,18 @@ export default function Contributors() { const [activeRepo, setActiveRepo] = useState(repos[0] || "all"); const [activeFilters, setActiveFilters] = useState([]); + const [searchTerm, setSearchTerm] = useState(""); // Extract unique values for each filterable field const availableFilters = useMemo(() => { const roles = [...new Set(allContribs.map((c) => c.role).filter(Boolean))].sort(); const locations = [...new Set(allContribs.map((c) => c.location).filter(Boolean))].sort(); + const names = [...new Set(allContribs.map((c) => c.name).filter(Boolean))].sort(); return { role: roles, location: locations, + name: names, }; }, [allContribs]); @@ -126,14 +147,23 @@ export default function Contributors() { if (key === "location") { return contributor.location === value; } + if (key === "name") { + return (contributor.name || "").toLowerCase() === (value || "").toLowerCase(); + } return true; }); }); } + // Apply search filter (name match) + if (searchTerm && searchTerm.trim()) { + const s = searchTerm.toLowerCase(); + filtered = filtered.filter((c) => (c.name || "").toLowerCase().includes(s)); + } + return filtered; - }, [activeRepo, activeFilters, allContribs]); + }, [activeRepo, activeFilters, allContribs, searchTerm]); return ( @@ -167,6 +197,10 @@ export default function Contributors() { className="flex w-full flex-wrap items-center justify-center gap-2" style={{ marginBottom: "1.5rem" }} > + {/* Search bar (placed above pills on small screens) */} +
+ +
{/* Results counter */} - {(activeFilters.length > 0 || activeRepo !== "all") && ( + {(activeFilters.length > 0 || activeRepo !== "all" || (searchTerm && searchTerm.trim())) && (

Showing {data.length}{" "} @@ -220,6 +254,7 @@ export default function Contributors() { onClick={() => { setActiveFilters([]); setActiveRepo("all"); + setSearchTerm(""); }} className="mt-4 rounded-xl border border-emerald-400/45 bg-emerald-500/15 px-6 py-2 text-emerald-200 backdrop-blur transition hover:bg-emerald-500/20" > @@ -233,4 +268,4 @@ export default function Contributors() { ); -} +} \ No newline at end of file