From c0b6c7e7b18ff5ed85649a7416cdf8cda4267295 Mon Sep 17 00:00:00 2001
From: nios-x
Date: Fri, 27 Feb 2026 15:47:09 +0000
Subject: [PATCH] feat/move-main-page-search-bar
---
frontend/src/app/chapters/page.tsx | 1 +
frontend/src/app/committees/page.tsx | 1 +
frontend/src/app/contribute/page.tsx | 1 +
frontend/src/app/members/page.tsx | 1 +
frontend/src/app/mentorship/programs/page.tsx | 1 +
frontend/src/app/my/mentorship/page.tsx | 1 +
frontend/src/app/organizations/page.tsx | 1 +
frontend/src/app/page.tsx | 9 ---
frontend/src/app/projects/page.tsx | 1 +
frontend/src/components/Header.tsx | 64 ++++++++++++++++++-
frontend/src/components/MultiSearch.tsx | 23 +++++--
frontend/src/components/SearchPageLayout.tsx | 21 ++++--
frontend/src/types/search.ts | 3 +
13 files changed, 106 insertions(+), 22 deletions(-)
diff --git a/frontend/src/app/chapters/page.tsx b/frontend/src/app/chapters/page.tsx
index 621a8d9a6d..5e40d40197 100644
--- a/frontend/src/app/chapters/page.tsx
+++ b/frontend/src/app/chapters/page.tsx
@@ -84,6 +84,7 @@ const ChaptersPage = () => {
isLoaded={isLoaded}
onPageChange={handlePageChange}
onSearch={handleSearch}
+ scopeLabel="Search within chapters"
searchPlaceholder="Search for chapters..."
searchQuery={searchQuery}
totalPages={totalPages}
diff --git a/frontend/src/app/committees/page.tsx b/frontend/src/app/committees/page.tsx
index bd52479962..0f17b5851a 100644
--- a/frontend/src/app/committees/page.tsx
+++ b/frontend/src/app/committees/page.tsx
@@ -59,6 +59,7 @@ const CommitteesPage = () => {
isLoaded={isLoaded}
onPageChange={handlePageChange}
onSearch={handleSearch}
+ scopeLabel="Search within committees"
searchPlaceholder="Search for committees..."
searchQuery={searchQuery}
totalPages={totalPages}
diff --git a/frontend/src/app/contribute/page.tsx b/frontend/src/app/contribute/page.tsx
index 24317b1bfd..a6eb29ee45 100644
--- a/frontend/src/app/contribute/page.tsx
+++ b/frontend/src/app/contribute/page.tsx
@@ -77,6 +77,7 @@ const ContributePage = () => {
isLoaded={isLoaded}
onPageChange={handlePageChange}
onSearch={handleSearch}
+ scopeLabel="Search within issues"
searchPlaceholder="Search for issues..."
searchQuery={searchQuery}
totalPages={totalPages}
diff --git a/frontend/src/app/members/page.tsx b/frontend/src/app/members/page.tsx
index f024ab9a47..9bfc8beaac 100644
--- a/frontend/src/app/members/page.tsx
+++ b/frontend/src/app/members/page.tsx
@@ -59,6 +59,7 @@ const UsersPage = () => {
isLoaded={isLoaded}
onPageChange={handlePageChange}
onSearch={handleSearch}
+ scopeLabel="Search within members"
searchPlaceholder="Search for members..."
searchQuery={searchQuery}
totalPages={totalPages}
diff --git a/frontend/src/app/mentorship/programs/page.tsx b/frontend/src/app/mentorship/programs/page.tsx
index a987299769..4b2eaaa480 100644
--- a/frontend/src/app/mentorship/programs/page.tsx
+++ b/frontend/src/app/mentorship/programs/page.tsx
@@ -41,6 +41,7 @@ const ProgramsPage = () => {
isLoaded={isLoaded}
onPageChange={handlePageChange}
onSearch={handleSearch}
+ scopeLabel="Search within programs"
searchPlaceholder="Search for programs..."
searchQuery={searchQuery}
totalPages={totalPages}
diff --git a/frontend/src/app/my/mentorship/page.tsx b/frontend/src/app/my/mentorship/page.tsx
index c62134b310..0c54a7ba31 100644
--- a/frontend/src/app/my/mentorship/page.tsx
+++ b/frontend/src/app/my/mentorship/page.tsx
@@ -126,6 +126,7 @@ const MyMentorshipPage: React.FC = () => {
setPage(1)
}}
searchQuery={searchQuery}
+ scopeLabel="Search within your programs"
searchPlaceholder="Search your programs"
indexName="my-programs"
>
diff --git a/frontend/src/app/organizations/page.tsx b/frontend/src/app/organizations/page.tsx
index bb567387ad..a8b7f6af6d 100644
--- a/frontend/src/app/organizations/page.tsx
+++ b/frontend/src/app/organizations/page.tsx
@@ -59,6 +59,7 @@ const OrganizationPage = () => {
isLoaded={isLoaded}
onPageChange={handlePageChange}
onSearch={handleSearch}
+ scopeLabel="Search within organizations"
searchPlaceholder="Search for organizations..."
searchQuery={searchQuery}
totalPages={totalPages}
diff --git a/frontend/src/app/page.tsx b/frontend/src/app/page.tsx
index 859d1fb2b5..d4d28f701a 100644
--- a/frontend/src/app/page.tsx
+++ b/frontend/src/app/page.tsx
@@ -40,7 +40,6 @@ import LoadingSpinner from 'components/LoadingSpinner'
import MovingLogos from 'components/LogoCarousel'
import Milestones from 'components/Milestones'
import DialogComp from 'components/Modal'
-import MultiSearchBar from 'components/MultiSearch'
import RecentIssues from 'components/RecentIssues'
import RecentPullRequests from 'components/RecentPullRequests'
import RecentReleases from 'components/RecentReleases'
@@ -146,14 +145,6 @@ export default function Home() {
Your gateway to OWASP. Discover, engage, and help shape the future!
-
-
-
{
isLoaded={isLoaded}
onPageChange={handlePageChange}
onSearch={handleSearch}
+ scopeLabel="Search within projects"
searchPlaceholder="Search for projects..."
searchQuery={searchQuery}
sortChildren={
diff --git a/frontend/src/components/Header.tsx b/frontend/src/components/Header.tsx
index cfbf5b7e33..86c74dc53f 100644
--- a/frontend/src/components/Header.tsx
+++ b/frontend/src/components/Header.tsx
@@ -11,9 +11,11 @@ import {
FaStar as FaSolidStar,
FaBars,
FaTimes,
+ FaSearch,
} from 'react-icons/fa'
import { desktopViewMinWidth, headerLinks } from 'utils/constants'
import { cn } from 'utils/utility'
+import MultiSearchBar from 'components/MultiSearch'
import ModeToggle from 'components/ModeToggle'
import NavButton from 'components/NavButton'
import NavDropdown from 'components/NavDropDown'
@@ -22,18 +24,28 @@ import UserMenu from 'components/UserMenu'
export default function Header({ isGitHubAuthEnabled }: { readonly isGitHubAuthEnabled: boolean }) {
const pathname = usePathname()
const [mobileMenuOpen, setMobileMenuOpen] = useState(false)
- const toggleMobileMenu = () => setMobileMenuOpen(!mobileMenuOpen)
+ const [mobileSearchOpen, setMobileSearchOpen] = useState(false)
+ const toggleMobileMenu = () => {
+ setMobileMenuOpen((prev) => !prev)
+ setMobileSearchOpen(false)
+ }
+ const toggleMobileSearch = () => {
+ setMobileSearchOpen((prev) => !prev)
+ setMobileMenuOpen(false)
+ }
useEffect(() => {
const handleResize = () => {
if (globalThis.innerWidth >= desktopViewMinWidth) {
setMobileMenuOpen(false)
+ setMobileSearchOpen(false)
}
}
const handleOutsideClick = (event: Event) => {
const navbar = document.getElementById('navbar-sticky')
const sidebar = document.querySelector('.fixed.inset-y-0')
+ const mobileSearch = document.getElementById('global-search-mobile')
if (
mobileMenuOpen &&
navbar &&
@@ -43,6 +55,15 @@ export default function Header({ isGitHubAuthEnabled }: { readonly isGitHubAuthE
) {
setMobileMenuOpen(false)
}
+ if (
+ mobileSearchOpen &&
+ mobileSearch &&
+ !mobileSearch.contains(event.target as Node) &&
+ navbar &&
+ !navbar.contains(event.target as Node)
+ ) {
+ setMobileSearchOpen(false)
+ }
}
globalThis.addEventListener('resize', handleResize)
@@ -60,7 +81,10 @@ export default function Header({ isGitHubAuthEnabled }: { readonly isGitHubAuthE
{/* Logo */}
setMobileMenuOpen(false)}
+ onClick={() => {
+ setMobileMenuOpen(false)
+ setMobileSearchOpen(false)
+ }}
className="rounded-lg focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white"
>
@@ -108,7 +132,26 @@ export default function Header({ isGitHubAuthEnabled }: { readonly isGitHubAuthE
})}
+
+
+
+ {mobileSearchOpen && (
+
+
+
+ )}
setMobileMenuOpen(false)}
+ onClick={() => {
+ setMobileMenuOpen(false)
+ setMobileSearchOpen(false)
+ }}
className="rounded-lg focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white"
>
diff --git a/frontend/src/components/MultiSearch.tsx b/frontend/src/components/MultiSearch.tsx
index efa3c69fc2..5a82ce22b3 100644
--- a/frontend/src/components/MultiSearch.tsx
+++ b/frontend/src/components/MultiSearch.tsx
@@ -13,6 +13,7 @@ import type { Organization } from 'types/organization'
import type { Project } from 'types/project'
import type { MultiSearchBarProps, Suggestion } from 'types/search'
import type { User } from 'types/user'
+import { cn } from 'utils/utility'
type SearchHit = Chapter | Event | Organization | Project | User
@@ -22,6 +23,9 @@ const MultiSearchBar: React.FC
= ({
indexes,
initialValue = '',
eventData,
+ autoFocus = false,
+ containerClassName,
+ inputClassName,
}) => {
const [searchQuery, setSearchQuery] = useState(initialValue)
const [suggestions, setSuggestions] = useState([])
@@ -115,6 +119,10 @@ const MultiSearchBar: React.FC = ({
useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
+ const isInputFocused = document.activeElement === inputRef.current
+ if (!isInputFocused && !showSuggestions) {
+ return
+ }
if (event.key === 'Escape') {
setShowSuggestions(false)
inputRef.current?.blur()
@@ -158,8 +166,6 @@ const MultiSearchBar: React.FC = ({
}, [searchQuery, suggestions, highlightedIndex, handleSuggestionClick])
useEffect(() => {
- inputRef.current?.focus()
-
const handleClickOutside = (event: MouseEvent) => {
if (searchBarRef.current && !searchBarRef.current.contains(event.target as Node)) {
setShowSuggestions(false)
@@ -173,6 +179,12 @@ const MultiSearchBar: React.FC = ({
}
}, [])
+ useEffect(() => {
+ if (autoFocus) {
+ inputRef.current?.focus()
+ }
+ }, [autoFocus])
+
const handleSearchChange = (e: React.ChangeEvent) => {
const newQuery = e.target.value
setSearchQuery(newQuery)
@@ -221,7 +233,7 @@ const MultiSearchBar: React.FC = ({
}
return (
-
+
{isLoaded ? (
<>
@@ -236,7 +248,10 @@ const MultiSearchBar: React.FC
= ({
onChange={handleSearchChange}
onFocus={handleFocusSearch}
placeholder={placeholder}
- className="h-12 w-full rounded-lg border-1 border-gray-300 bg-white pr-10 pl-10 text-lg text-black focus:ring-1 focus:ring-blue-500 focus:outline-hidden dark:border-gray-600 dark:bg-gray-800 dark:text-white dark:focus:ring-blue-300"
+ className={cn(
+ 'h-12 w-full rounded-lg border-1 border-gray-300 bg-white pr-10 pl-10 text-lg text-black focus:ring-1 focus:ring-blue-500 focus:outline-hidden dark:border-gray-600 dark:bg-gray-800 dark:text-white dark:focus:ring-blue-300',
+ inputClassName
+ )}
/>
{searchQuery && (