From c6614a1e682784d5f70c11f2a99c356da0a8e9f8 Mon Sep 17 00:00:00 2001 From: dhrumit parmar Date: Mon, 8 Apr 2024 10:37:44 +0530 Subject: [PATCH 01/12] Initial commit --- app/api/leaderboard/functions.ts | 50 +++++++++++++++++++ app/feed/GithubFeed.tsx | 83 ++++++++++++++++++++++++++++++++ app/feed/page.tsx | 44 ++++++++++++++--- 3 files changed, 169 insertions(+), 8 deletions(-) create mode 100644 app/feed/GithubFeed.tsx diff --git a/app/api/leaderboard/functions.ts b/app/api/leaderboard/functions.ts index bd67f363..3e96b2dc 100644 --- a/app/api/leaderboard/functions.ts +++ b/app/api/leaderboard/functions.ts @@ -113,3 +113,53 @@ export default async function fetchGitHubReleases( ) .slice(0, sliceLimit); } + +export async function fetchAllReposName() { + const response = await octokit.graphql({ + query: ` + query GetTop10ActiveRepos($org: String!) { + organization(login: $org) { + repositories(first: 20, orderBy: { field: UPDATED_AT, direction: DESC }) { + nodes { + name + } + } + } + } + `, + org: env.NEXT_PUBLIC_GITHUB_ORG, + }); + + const repos = response.organization.repositories.nodes.map( + (repo) => repo.name, + ); + // console.log(repos); + return ["All", ...repos.flat()]; +} + +// export async function fetchAllBranchName() { +// const response = await octokit.graphql({ +// query: ` +// query GetTop10ActiveBranches($org: String!) { +// organization(login: $org) { +// repositories(first: 20, orderBy: { field: UPDATED_AT, direction: DESC }) { +// nodes { +// refs(first: 10, refPrefix: "refs/heads/") { +// nodes { +// name +// } +// } +// } +// } +// } +// } +// `, +// org: env.NEXT_PUBLIC_GITHUB_ORG, +// }); + +// const branches = response.organization.repositories.nodes.flatMap( +// (repo) => repo.refs.nodes.map((branch) => branch.name), +// ); +// console.log(branches); +// return branches; +// } diff --git a/app/feed/GithubFeed.tsx b/app/feed/GithubFeed.tsx new file mode 100644 index 00000000..4acfa6e6 --- /dev/null +++ b/app/feed/GithubFeed.tsx @@ -0,0 +1,83 @@ +"use client"; +import GitHubEvent from "@/components/gh_events/GitHubEvent"; +import { useState, useEffect } from "react"; + +const GithubFeed = (props: any) => { + const { events } = props; + const { filterEvetns } = props; + const [e, setEvents] = useState(events); + const [repo, setRepo] = useState("All"); + const [eventType, setEventType] = useState("All"); + + const filterEvents = () => { + console.log("Hey I am here"); + let filteredEvents = events; + console.log(eventType); + if (repo !== "All" && eventType !== "All") { + filteredEvents = events.filter( + (event: any) => + event.repo.name.split("/").pop() === repo && event.type === eventType, + ); + } else if (eventType !== "All") { + filteredEvents = events.filter((event: any) => event.type === eventType); + } else if (repo !== "All") { + filteredEvents = events.filter( + (event: any) => event.repo.name.split("/").pop() === repo, + ); + } + setEvents(filteredEvents); + }; + + return ( +
+
+

Feed

+
    + {e.length === 0 && ( +
    + No Activity Found +
    + )} + {e.map((event: any) => ( + + ))} +
+
+
+ Filter Activity +
+
    + {filterEvetns.map((filter, index) => ( +
  • + {filter.title} + {filter.options && ( + + )} +
  • + ))} +
+ +
+
+
+ ); +}; + +export default GithubFeed; diff --git a/app/feed/page.tsx b/app/feed/page.tsx index 6b5b458b..827ee3c8 100644 --- a/app/feed/page.tsx +++ b/app/feed/page.tsx @@ -3,6 +3,11 @@ import { IGitHubEvent } from "@/lib/gh_events"; import GitHubEvent from "@/components/gh_events/GitHubEvent"; import { env } from "@/env.mjs"; import octokit from "@/lib/octokit"; +import { + // fetchAllBranchName, + fetchAllReposName, +} from "../api/leaderboard/functions"; +import GithubFeed from "./GithubFeed"; const GITHUB_ORG: string = env.NEXT_PUBLIC_GITHUB_ORG; @@ -15,6 +20,24 @@ type Props = { }; export default async function FeedPage({ searchParams }: Props) { + const filterEvetns = [ + { title: "Repository", options: await fetchAllReposName() }, + { + title: "Events", + options: [ + "All", + "PullRequestReviewCommentEvent", + "PullRequestReviewEvent", + "MemberEvent", + "IssuesEvent", + "IssueCommentEvent", + "PullRequestEvent", + "PushEvent", + "ForkEvent", + "ReleaseEvent", + ], + }, + ]; const events = await octokit.paginate( "GET /orgs/{org}/events", { @@ -30,14 +53,19 @@ export default async function FeedPage({ searchParams }: Props) { return ; } return ( -
-

Feed

-
    - {events.map((e) => ( - - ))} -
-
+ <> + {/* //
*/} + {/* //
*/} + {/* //

Feed

*/} + + {/*
    + {events.map((e) => ( + + ))} +
*/} + {/*
*/} + {/*
*/} + ); } From 8daf2647cc4a353f7d80f5f33dbf360932646f33 Mon Sep 17 00:00:00 2001 From: dhrumit parmar Date: Mon, 8 Apr 2024 16:56:31 +0530 Subject: [PATCH 02/12] Event and repository based filter added --- app/api/leaderboard/functions.ts | 35 +++------------------ app/feed/GithubFeed.tsx | 54 ++++++++++++++++++++------------ 2 files changed, 38 insertions(+), 51 deletions(-) diff --git a/app/api/leaderboard/functions.ts b/app/api/leaderboard/functions.ts index 3e96b2dc..cd380409 100644 --- a/app/api/leaderboard/functions.ts +++ b/app/api/leaderboard/functions.ts @@ -130,36 +130,9 @@ export async function fetchAllReposName() { org: env.NEXT_PUBLIC_GITHUB_ORG, }); - const repos = response.organization.repositories.nodes.map( - (repo) => repo.name, - ); - // console.log(repos); + const repos = ( + response as ReleasesResponse + ).organization.repositories.nodes.map((repo) => repo.name); + return ["All", ...repos.flat()]; } - -// export async function fetchAllBranchName() { -// const response = await octokit.graphql({ -// query: ` -// query GetTop10ActiveBranches($org: String!) { -// organization(login: $org) { -// repositories(first: 20, orderBy: { field: UPDATED_AT, direction: DESC }) { -// nodes { -// refs(first: 10, refPrefix: "refs/heads/") { -// nodes { -// name -// } -// } -// } -// } -// } -// } -// `, -// org: env.NEXT_PUBLIC_GITHUB_ORG, -// }); - -// const branches = response.organization.repositories.nodes.flatMap( -// (repo) => repo.refs.nodes.map((branch) => branch.name), -// ); -// console.log(branches); -// return branches; -// } diff --git a/app/feed/GithubFeed.tsx b/app/feed/GithubFeed.tsx index 4acfa6e6..f82b6790 100644 --- a/app/feed/GithubFeed.tsx +++ b/app/feed/GithubFeed.tsx @@ -1,28 +1,38 @@ "use client"; import GitHubEvent from "@/components/gh_events/GitHubEvent"; -import { useState, useEffect } from "react"; +import { IGitHubEvent } from "@/lib/gh_events"; +import { useState } from "react"; -const GithubFeed = (props: any) => { - const { events } = props; - const { filterEvetns } = props; - const [e, setEvents] = useState(events); +interface Filter { + title: string; + options: string[]; +} +interface Props { + events: IGitHubEvent[]; + filterEvetns: Filter[]; +} + +const GithubFeed = (props: Props) => { + const { events, filterEvetns } = props; + const [e, setEvents] = useState(events); const [repo, setRepo] = useState("All"); const [eventType, setEventType] = useState("All"); const filterEvents = () => { - console.log("Hey I am here"); - let filteredEvents = events; - console.log(eventType); + let filteredEvents = e; if (repo !== "All" && eventType !== "All") { - filteredEvents = events.filter( - (event: any) => - event.repo.name.split("/").pop() === repo && event.type === eventType, + filteredEvents = e.filter( + (events: IGitHubEvent) => + events.repo.name.split("/").pop() === repo && + events.type === eventType, ); } else if (eventType !== "All") { - filteredEvents = events.filter((event: any) => event.type === eventType); - } else if (repo !== "All") { filteredEvents = events.filter( - (event: any) => event.repo.name.split("/").pop() === repo, + (events: IGitHubEvent) => events.type === eventType, + ); + } else if (repo !== "All") { + filteredEvents = e.filter( + (events: IGitHubEvent) => events.repo.name.split("/").pop() === repo, ); } setEvents(filteredEvents); @@ -38,20 +48,21 @@ const GithubFeed = (props: any) => { No Activity Found )} - {e.map((event: any) => ( + {e.map((event: IGitHubEvent) => ( ))}
- Filter Activity + Filter Activity
-
    +
      {filterEvetns.map((filter, index) => ( -
    • - {filter.title} +
    • + {filter.title} {filter.options && ( )}
    • ))}
    + -
-
- - ); -}; - -export default GithubFeed; diff --git a/app/feed/page.tsx b/app/feed/page.tsx index 827ee3c8..e3ad9e19 100644 --- a/app/feed/page.tsx +++ b/app/feed/page.tsx @@ -1,13 +1,12 @@ import LoadingText from "@/components/LoadingText"; import { IGitHubEvent } from "@/lib/gh_events"; -import GitHubEvent from "@/components/gh_events/GitHubEvent"; import { env } from "@/env.mjs"; import octokit from "@/lib/octokit"; import { // fetchAllBranchName, fetchAllReposName, } from "../api/leaderboard/functions"; -import GithubFeed from "./GithubFeed"; +import GithubFeed from "../../components/gh_events/GithubFeed"; const GITHUB_ORG: string = env.NEXT_PUBLIC_GITHUB_ORG; diff --git a/components/gh_events/GithubFeed.tsx b/components/gh_events/GithubFeed.tsx new file mode 100644 index 00000000..d44a6dc2 --- /dev/null +++ b/components/gh_events/GithubFeed.tsx @@ -0,0 +1,102 @@ +"use client"; +import GitHubEvent from "@/components/gh_events/GitHubEvent"; +import { IGitHubEvent } from "@/lib/gh_events"; +import { useState } from "react"; + +interface Filter { + title: string; + options: string[]; +} +interface Props { + events: IGitHubEvent[]; + filterEvetns: Filter[]; +} + +const GithubFeed = (props: Props) => { + const { events, filterEvetns } = props; + const [e, setEvents] = useState(events); + const [repo, setRepo] = useState("All"); + const [eventType, setEventType] = useState("All"); + + const filterEvents = () => { + let filteredEvents = events; + if (repo !== "All" && eventType !== "All") { + filteredEvents = e.filter( + (events: IGitHubEvent) => + events.repo.name.split("/").pop() === repo && + events.type === eventType, + ); + } else if (eventType !== "All") { + filteredEvents = events.filter( + (events: IGitHubEvent) => events.type === eventType, + ); + } else if (repo !== "All") { + filteredEvents = e.filter( + (events: IGitHubEvent) => events.repo.name.split("/").pop() === repo, + ); + } + setEvents(filteredEvents); + }; + + return ( +
+ {/* Filter Component */} +
+
+ Filter Activity +
+
    + {filterEvetns.map((filter, index) => ( +
  • + {filter.title} + {filter.options && ( + + )} +
  • + ))} +
+ + +
+
+
+ + {/* Feed Component */} +
+

Feed

+
    + {e.length === 0 && ( +
    + No Activity Found +
    + )} + {e.map((event: IGitHubEvent) => ( + + ))} +
+
+
+ ); +}; + +export default GithubFeed; From 976b41c29f5a253b90bcd99cc32ac94c45d02536 Mon Sep 17 00:00:00 2001 From: dhrumit parmar Date: Mon, 8 Apr 2024 18:04:24 +0530 Subject: [PATCH 04/12] Correct styling --- components/gh_events/GithubFeed.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/components/gh_events/GithubFeed.tsx b/components/gh_events/GithubFeed.tsx index d44a6dc2..31baaa55 100644 --- a/components/gh_events/GithubFeed.tsx +++ b/components/gh_events/GithubFeed.tsx @@ -39,19 +39,19 @@ const GithubFeed = (props: Props) => { }; return ( -
+
{/* Filter Component */} -
+
Filter Activity -
-
    +
    +
      {filterEvetns.map((filter, index) => (
    • {filter.title} {filter.options && ( { - if (filter.title === "Repository") - setRepo(e.target.value); - else if (filter.title === "Events") - setEventType(e.target.value); - }} - > - {filter.options.map((option, optionIndex) => ( - - ))} - - )} -
    • - ))} -
    - - -
    -
-
- - {/* Feed Component */} -
-

Feed

-
    - {e.length === 0 && ( -
    - No Activity Found -
    - )} - {e.map((event: IGitHubEvent) => ( - - ))} -
-
-
- ); -}; - -export default GithubFeed; diff --git a/components/gh_events/GithubFeedFilter.tsx b/components/gh_events/GithubFeedFilter.tsx new file mode 100644 index 00000000..9d118804 --- /dev/null +++ b/components/gh_events/GithubFeedFilter.tsx @@ -0,0 +1,59 @@ +"use client"; +import { FilterOption } from "@/lib/types"; +import { usePathname, useRouter, useSearchParams } from "next/navigation"; + +const GithubFeed = (props: { filterEvents: FilterOption[] }) => { + const { filterEvents } = props; + const router = useRouter(); + const pathname = usePathname(); + const searchParams = useSearchParams(); + + const updateSearchParam = (key: string, value: string) => { + const params = new URLSearchParams(searchParams.toString()); + + value !== "All" ? params.set(key, value) : params.delete(key); + + router.push(pathname + "?" + params.toString()); + }; + + return ( +
+ {/* Filter Component */} +
+
+ Filter Activity +
+
    + {filterEvents.map((filter, index) => ( +
  • + {filter.title} + {filter.options && ( + + )} +
  • + ))} +
+
+
+
+
+ ); +}; + +export default GithubFeed; diff --git a/lib/types.ts b/lib/types.ts index a9959c99..c898f0cc 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -153,7 +153,11 @@ export type ContributorSocials = { linkedin: string; slack: string; }; - +export type FilterOption = { + title: string; + options: string[]; + selectedOption?: string; // Add selectedOption property +}; export type PageProps = { searchParams: { search?: string; From 2387ddfde72450a1b722fd3c53d65394fa39db8e Mon Sep 17 00:00:00 2001 From: dhrumit parmar Date: Thu, 18 Apr 2024 19:21:27 +0530 Subject: [PATCH 06/12] Use ocotokit.graphql.paginate --- app/api/leaderboard/functions.ts | 53 +++++++++++++++++++------------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/app/api/leaderboard/functions.ts b/app/api/leaderboard/functions.ts index 3621a2ad..888d3871 100644 --- a/app/api/leaderboard/functions.ts +++ b/app/api/leaderboard/functions.ts @@ -118,42 +118,51 @@ export default async function fetchGitHubReleases( interface RepositoriesResponse { organization: { repositories: { + nodes: { + name: string; + }[]; pageInfo: { hasNextPage: boolean; endCursor: string | null; }; - nodes: Repository[]; }; }; } + export async function fetchAllReposName() { - let repos: string[] = []; - let afterCursor: string | null = null; + const allRepositoryNames: string[] = []; + let cursor = null; do { - const response: RepositoriesResponse = await octokit.graphql({ - query: ` - query GetTop10ActiveRepos($org: String!, $after: String) { - organization(login: $org) { - repositories(first: 20, after: $after, orderBy: { field: UPDATED_AT, direction: DESC }) { - pageInfo { - hasNextPage - endCursor - } - nodes { - name - } + const result: RepositoriesResponse = await octokit.graphql.paginate( + ` + query paginate($cursor: String, $organization: String!) { + organization(login: $organization) { + repositories(first: 10, after: $cursor, orderBy: { field: STARGAZERS, direction: DESC }) { + nodes { + name + } + pageInfo { + hasNextPage + endCursor } } } + } `, - org: env.NEXT_PUBLIC_GITHUB_ORG, - after: afterCursor, + { + organization: "coronasafe", + cursor: cursor, + }, + ); + + const repositories = result.organization.repositories.nodes; + repositories.forEach((repo) => { + allRepositoryNames.push(repo.name); }); - const { nodes, pageInfo } = response.organization.repositories; - repos.push(...nodes.map((repo) => repo.name)); - afterCursor = pageInfo.hasNextPage ? pageInfo.endCursor : null; - } while (afterCursor !== null); - return repos; + cursor = result.organization.repositories.pageInfo.endCursor; + } while (cursor !== null); + + return allRepositoryNames; } From 1436f86321b2c6da47b214d19837df3943ac3ef6 Mon Sep 17 00:00:00 2001 From: dhrumit parmar Date: Thu, 18 Apr 2024 21:31:37 +0530 Subject: [PATCH 07/12] use .paginate --- app/api/leaderboard/functions.ts | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/app/api/leaderboard/functions.ts b/app/api/leaderboard/functions.ts index 888d3871..f1c74ca2 100644 --- a/app/api/leaderboard/functions.ts +++ b/app/api/leaderboard/functions.ts @@ -131,11 +131,8 @@ interface RepositoriesResponse { export async function fetchAllReposName() { const allRepositoryNames: string[] = []; - let cursor = null; - - do { - const result: RepositoriesResponse = await octokit.graphql.paginate( - ` + const result: RepositoriesResponse = await octokit.graphql.paginate( + ` query paginate($cursor: String, $organization: String!) { organization(login: $organization) { repositories(first: 10, after: $cursor, orderBy: { field: STARGAZERS, direction: DESC }) { @@ -150,19 +147,15 @@ export async function fetchAllReposName() { } } `, - { - organization: "coronasafe", - cursor: cursor, - }, - ); + { + organization: "coronasafe", + }, + ); - const repositories = result.organization.repositories.nodes; - repositories.forEach((repo) => { - allRepositoryNames.push(repo.name); - }); - - cursor = result.organization.repositories.pageInfo.endCursor; - } while (cursor !== null); + const repositories = result.organization.repositories.nodes; + repositories.forEach((repo) => { + allRepositoryNames.push(repo.name); + }); return allRepositoryNames; } From 62c6e1fa09a060ab1128860be988ce473ac9203d Mon Sep 17 00:00:00 2001 From: Rithvik Nishad Date: Thu, 18 Apr 2024 21:57:58 +0530 Subject: [PATCH 08/12] Update app/api/leaderboard/functions.ts --- app/api/leaderboard/functions.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/api/leaderboard/functions.ts b/app/api/leaderboard/functions.ts index f1c74ca2..abcfb8d8 100644 --- a/app/api/leaderboard/functions.ts +++ b/app/api/leaderboard/functions.ts @@ -121,10 +121,6 @@ interface RepositoriesResponse { nodes: { name: string; }[]; - pageInfo: { - hasNextPage: boolean; - endCursor: string | null; - }; }; }; } From 1121c15783e7fd820e2ae3f40e037c5d8f928790 Mon Sep 17 00:00:00 2001 From: dhrumit parmar Date: Fri, 19 Apr 2024 09:52:46 +0530 Subject: [PATCH 09/12] refactor filter logic in feed page --- app/api/leaderboard/functions.ts | 8 ++-- app/feed/page.tsx | 55 ++++++++++------------- app/leaderboard/_components/Searchbar.tsx | 1 - components/gh_events/GithubFeedFilter.tsx | 5 ++- 4 files changed, 31 insertions(+), 38 deletions(-) diff --git a/app/api/leaderboard/functions.ts b/app/api/leaderboard/functions.ts index abcfb8d8..179de3f5 100644 --- a/app/api/leaderboard/functions.ts +++ b/app/api/leaderboard/functions.ts @@ -126,7 +126,6 @@ interface RepositoriesResponse { } export async function fetchAllReposName() { - const allRepositoryNames: string[] = []; const result: RepositoriesResponse = await octokit.graphql.paginate( ` query paginate($cursor: String, $organization: String!) { @@ -148,10 +147,9 @@ export async function fetchAllReposName() { }, ); - const repositories = result.organization.repositories.nodes; - repositories.forEach((repo) => { - allRepositoryNames.push(repo.name); - }); + const allRepositoryNames = result.organization.repositories.nodes.map( + (repo) => repo.name, + ); return allRepositoryNames; } diff --git a/app/feed/page.tsx b/app/feed/page.tsx index 2b07bc49..6cbd8e96 100644 --- a/app/feed/page.tsx +++ b/app/feed/page.tsx @@ -14,15 +14,21 @@ const GITHUB_ORG: string = env.NEXT_PUBLIC_GITHUB_ORG; export const revalidate = 600; -export default async function FeedPage({ searchParams }: any) { - const { Repository, Events } = searchParams; +interface FeedPageProps { + searchParams: { + repository: string; + events: string; + }; +} + +export default async function FeedPage({ searchParams }: FeedPageProps) { + const { repository, events } = searchParams; const repositories = await fetchAllReposName(); const filterEvents: FilterOption[] = [ - { title: "Repository", options: ["All", ...repositories] }, + { title: "repository", options: repositories }, { - title: "Events", + title: "events", options: [ - "All", "PullRequestReviewCommentEvent", "PullRequestReviewEvent", "MemberEvent", @@ -35,7 +41,7 @@ export default async function FeedPage({ searchParams }: any) { ], }, ] as const; - let events = await octokit.paginate( + let allEvents = await octokit.paginate( "GET /orgs/{org}/events", { org: GITHUB_ORG, @@ -46,33 +52,20 @@ export default async function FeedPage({ searchParams }: any) { return data.filter(exludeBotEvents).filter(excludeBlacklistedEvents); }, ); - if (!Object.entries(events).length) { + if (!Object.entries(allEvents).length) { return ; } - if (Repository && Events) { - events = events.filter((event) => { - const repoName = event.repo.name.replace( - `${env.NEXT_PUBLIC_GITHUB_ORG}/`, - "", - ); - return ( - repoName === searchParams.Repository && - event.type === searchParams.Events - ); - }); - } else if (Repository) { - events = events.filter((event) => { - const repoName = event.repo.name.replace( - `${env.NEXT_PUBLIC_GITHUB_ORG}/`, - "", - ); - return repoName === searchParams.Repository; - }); - } else if (Events) { - events = events.filter((event) => { - return event.type === searchParams.Events; - }); + if (searchParams.repository) { + allEvents = allEvents.filter((e) => + e.repo.name.includes( + env.NEXT_PUBLIC_GITHUB_ORG + "/" + searchParams.repository, + ), + ); + } + + if (searchParams.events) { + allEvents = allEvents.filter((e) => e.type === searchParams.events); } return ( @@ -82,7 +75,7 @@ export default async function FeedPage({ searchParams }: any) {

Feed

    - {events.map((e) => ( + {allEvents.map((e) => ( ))}
diff --git a/app/leaderboard/_components/Searchbar.tsx b/app/leaderboard/_components/Searchbar.tsx index 05068b74..47c462aa 100644 --- a/app/leaderboard/_components/Searchbar.tsx +++ b/app/leaderboard/_components/Searchbar.tsx @@ -32,7 +32,6 @@ export default function Searchbar({ searchParams }: PageProps) { const updateSearchParam = (key: string, value?: string) => { const current = new URLSearchParams(searchParams as Record); - console.log(current); if (!value) { current.delete(key); } else { diff --git a/components/gh_events/GithubFeedFilter.tsx b/components/gh_events/GithubFeedFilter.tsx index 9d118804..71536907 100644 --- a/components/gh_events/GithubFeedFilter.tsx +++ b/components/gh_events/GithubFeedFilter.tsx @@ -11,7 +11,7 @@ const GithubFeed = (props: { filterEvents: FilterOption[] }) => { const updateSearchParam = (key: string, value: string) => { const params = new URLSearchParams(searchParams.toString()); - value !== "All" ? params.set(key, value) : params.delete(key); + !!value ? params.set(key, value) : params.delete(key); router.push(pathname + "?" + params.toString()); }; @@ -35,6 +35,9 @@ const GithubFeed = (props: { filterEvents: FilterOption[] }) => { updateSearchParam(filter.title, e.target.value); }} > + {filter.options.map((option, optionIndex) => (