From fb61d222dc67c2a4f374bed3897b7527f422ad67 Mon Sep 17 00:00:00 2001 From: Diksha Mohite Date: Sat, 20 Jun 2026 16:49:23 +0530 Subject: [PATCH] feat: add search bar for quick subject navigation (#269) --- app/components/subjects.tsx | 91 +++++++++++++++++++++++++++++++++++-- 1 file changed, 87 insertions(+), 4 deletions(-) diff --git a/app/components/subjects.tsx b/app/components/subjects.tsx index 588c63a..0e6d046 100644 --- a/app/components/subjects.tsx +++ b/app/components/subjects.tsx @@ -1,5 +1,8 @@ "use client"; import Link from "next/link"; +import { Search } from "lucide-react"; +import { useMemo, useState } from "react"; + const subjects = { "Semester-1": [ @@ -131,7 +134,41 @@ const subjectCodes: Record = { // Available subjects const available = ["ep", "c", "em1", "em2", "oops", "dsc", "coa", "os", "ml", "dops", "cd", "cle", "ec", "dbms", "bme", "cns", "vlsi", "mb"]; + +const matchesSearch = (subject: string, searchTerm: string) => { + if (!searchTerm) return true; + + const query = searchTerm.toLowerCase().trim(); + + const words = subject + .toLowerCase() + .split(/[\s&()-]+/) + .filter(Boolean); + + const abbreviation = words.map(word => word[0]).join(""); + + const searchableText = [ + subject.toLowerCase(), + ...words, + abbreviation, + subjectCodes[subject], + ] + .filter(Boolean) + .join(" "); + + return searchableText.includes(query); +}; + export default function SubjectsSection() { + const [searchTerm, setSearchTerm] = useState(""); + const noResults = useMemo( + () => + Object.values(subjects) + .flat() + .filter((subj) => matchesSearch(subj, searchTerm)) + .length === 0, + [searchTerm] +); return (

Explore Subjects divided Semester-wise for your convenience.

+
+
+ + + setSearchTerm(e.target.value)} + className=" + w-full + pl-12 + pr-4 + py-3 + rounded-full + border-2 + border-[#9a7b4f] + bg-[#f8f8f8] + text-black + shadow-md + focus:outline-none + focus:ring-2 + focus:ring-[#d2b48c] + transition-all duration-200 + " + /> +
+
+ {noResults && searchTerm && ( +
+ No results found. Try another keyword. +
+ )} + {Object.entries(subjects).map(([semester, list]) => { + + const filteredList = list.filter((subj) => + matchesSearch(subj, searchTerm) + ); + + if (filteredList.length === 0) return null; const semCode = semester.toLowerCase().replace("semester-", "sem"); return (

{semester}

- {list.map((subj) => { + {filteredList.map((subj) => { const code = subjectCodes[subj]; const href = `/${semCode}/${code}/ch0`; const isAvailable = available.includes(code); const baseClass = - "relative bg-[#d2b48c] text-[#2b1b0e] flex items-center justify-center font-medium py-4 px-6 shadow-md"; + "relative bg-[#d2b48c] text-[#2b1b0e] flex items-center justify-center font-medium py-4 px-6 shadow-md transition-all duration-200"; return isAvailable ? ( - {subj} + {subj} Coming Soon @@ -192,4 +275,4 @@ export default function SubjectsSection() {

); -} +} \ No newline at end of file