Skip to content

Commit

Permalink
Filter groups/admins via query params (#29)
Browse files Browse the repository at this point in the history
* feat(explorer): filter content using query params

Content can now be filtered using either the params groupid or admin

re #25

* feat(explorer): filters groups using query parameters

Groups and groups belonging to a specified admin can now be filtered via query parameters.
Additionally, the query parameters are reflected within the input element.

re #25
  • Loading branch information
tb38r authored Jan 30, 2025
1 parent d1cb7e9 commit 91ae57e
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 9 deletions.
28 changes: 21 additions & 7 deletions apps/explorer/src/app/[network]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

import { GroupResponse, SemaphoreSubgraph } from "@semaphore-protocol/data"
import { SupportedNetwork } from "@semaphore-protocol/utils"
import { usePathname } from "next/navigation"
import { useCallback, useEffect, useState } from "react"
import { usePathname, useSearchParams } from "next/navigation"
import { useCallback, useEffect, useState, useMemo } from "react"
import SearchBar from "@/components/SearchBar"

export default function Network() {
Expand All @@ -12,9 +12,13 @@ export default function Network() {

const [allGroups, setAllGroups] = useState<GroupResponse[]>([])
const [filteredGroups, setFilteredGroups] = useState<GroupResponse[]>([])

const [loading, setLoading] = useState(false)

const searchParams = useSearchParams()
const adminParam = useMemo(() => new URLSearchParams(searchParams).get("admin"), [searchParams.toString()])
const groupIdParam = useMemo(() => new URLSearchParams(searchParams).get("groupid"), [searchParams.toString()])
const queryParam = adminParam || groupIdParam

useEffect(() => {
const fetchData = async () => {
setLoading(true)
Expand All @@ -40,24 +44,34 @@ export default function Network() {
let groups: GroupResponse[]
if (groupIdOrAdmin.startsWith("0x")) {
groupIdOrAdmin = groupIdOrAdmin.toLowerCase()
groups = allGroups.filter((group) => (!groupIdOrAdmin ? true : group.admin?.includes(groupIdOrAdmin)))
groups = allGroups.filter((group) => group.admin?.includes(groupIdOrAdmin))
} else {
groups = allGroups.filter((group) => (!groupIdOrAdmin ? true : group.id.includes(groupIdOrAdmin)))
groups = allGroups.filter(
(group) => group.id.includes(groupIdOrAdmin) || group.admin === groupIdOrAdmin
)
}

setFilteredGroups(groups)
},
[allGroups]
)

useEffect(() => {
filterGroups(adminParam || groupIdParam || "")
}, [adminParam, groupIdParam, filterGroups])

return loading ? (
<div className="flex justify-center items-center h-screen">
<div className="loader" />
</div>
) : (
allGroups && (
<div className="mx-auto max-w-7xl px-4 lg:px-8 pt-20">
<SearchBar className="mb-6" placeholder="Group ID, Admin" onChange={filterGroups} />
<SearchBar
className="mb-6"
placeholder="Group ID, Admin"
onChange={filterGroups}
queryParam={queryParam}
/>

<div className="flex justify-center flex-col pb-10 font-[family-name:var(--font-geist-sans)]">
<ul className="divide-y divide-gray-300 min-w-xl">
Expand Down
19 changes: 17 additions & 2 deletions apps/explorer/src/components/SearchBar.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,34 @@
"use client"

import { FaSearch } from "react-icons/fa"
import { useState, useEffect, ChangeEvent } from "react"

export default function SearchBar({ placeholder, onChange, className, queryParam }: any) {
const [searchQuery, setSearchQuery] = useState("")
useEffect(() => {
if (queryParam) {
setSearchQuery(queryParam)
}
}, [queryParam])

const handleInputChange = (event: ChangeEvent<HTMLInputElement>) => {
const { value } = event.target
setSearchQuery(value)
onChange(value)
}

export default function SearchBar({ placeholder, onChange, className }: any) {
return (
<div className={`relative mt-2 rounded-md shadow-sm ${className}`}>
<div className="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
<FaSearch className="text-gray-600" />
</div>
<input
onChange={(event) => onChange(event.target.value)}
onChange={handleInputChange}
name="search-bar"
type="text"
placeholder={placeholder}
className="block w-full rounded-md border-0 py-1.5 pl-9 pr-20 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:outline-none text-sm sm:leading-6"
value={searchQuery}
/>
</div>
)
Expand Down

0 comments on commit 91ae57e

Please sign in to comment.