Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix/filters #25

Merged
merged 9 commits into from
Jan 7, 2024
84 changes: 14 additions & 70 deletions app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,19 @@
import ContributionsTable from "@/components/contributions-table/table";
import Filter from "@/components/filter";
import { title, subtitle } from "@/components/primitives";
import Search from "@/components/search";
import Toolbar from "@/components/filters/toolbar";
import { title } from "@/components/primitives";
import { queryDatabase } from "@/lib/notion";
import { SearchParams } from "@/types/filters";
import {
SEARCH_OPTIONS,
LANGUAGES_OPTIONS,
INTERESTS_OPTIONS,
REPOSITORIES_BY_INTERESTS,
} from "@/data/filters";
import { queryDatabase, createFilter } from "@/lib/notion";
import { transformNotionDataToContributions } from "@/utils/contribution";
import RemoveFilters from "@/components/removeFilters";
processNotionFilters,
transformNotionDataToContributions,
} from "@/utils/notion";

export default async function Home({
searchParams,
}: {
searchParams?: { [key: string]: string | string[] | undefined };
}) {
const params = searchParams as { [key: string]: string };
const languagesFilterIsSelected = !!params && !!params.languages;
const interestsFilterIsSelected = !!params && !!params.interests;
const hasSearch = !!params && !!params.search;
const showRemoveAllFilters =
[languagesFilterIsSelected, interestsFilterIsSelected, hasSearch].filter(
Boolean,
).length >= 2;
const filter = await createFilter(params.languages, params.search, REPOSITORIES_BY_INTERESTS[params.interests])
interface IHomeProps {
searchParams: SearchParams;
}

export default async function Home({ searchParams }: IHomeProps) {
const filter = processNotionFilters(searchParams);
const data = await queryDatabase({
page_size: 10,
filter,
Expand All @@ -44,51 +31,8 @@ export default async function Home({
<h1 className={title()}>Find Collaborations,</h1>
<h1 className={title()}>Collect Kudos</h1>
</section>
<div className="flex flex-col items-center gap-4 py-8 md:py-10">
<div className="inline-block max-w-lg text-center justify-center">
<Search
placeholder="Search"
emoji="🔍"
items={SEARCH_OPTIONS}
selectedValue={params.search}
/>
{/* TODO:
1. make it controlled
2. set the selected value in the params
3. use custom function
4. reset the other values */}
<h2 className={subtitle({ class: "mt-4" })}>Banner</h2>
</div>
</div>
<div className="grid grid-cols-2 gap-4">
<div className="flex justify-around items-stretch gap-4">
<Filter
placeholder="Languages"
emoji={"💪"}
items={LANGUAGES_OPTIONS}
selectedValue={params.languages}
/>
<Filter
placeholder="Interests"
emoji={"🪄"}
items={INTERESTS_OPTIONS}
selectedValue={params.interests}
/>
{languagesFilterIsSelected && (
<RemoveFilters value={params.languages} param="Languages" />
)}
{interestsFilterIsSelected && (
<RemoveFilters value={params.interests} param="Interests" />
)}
{hasSearch && <RemoveFilters value={params.search} param="Search" />}

{showRemoveAllFilters && <RemoveFilters value={"All filters"} />}
</div>
<div className="flex justify-end">
<div>
<h2 className={subtitle({ class: "mt-4" })}>Sort</h2>
</div>
</div>
<div className="flex flex-col gap-4">
<Toolbar searchParams={searchParams} />
<div>
<ContributionsTable
items={items}
Expand Down
2 changes: 1 addition & 1 deletion app/table/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { getGoodFirstIssues } from "@/lib/notion";
import ContributionsTable from "@/components/contributions-table/table";
import { transformNotionDataToContributions } from "@/utils/contribution";
import { transformNotionDataToContributions } from "@/utils/notion";

export default async function Page() {
const data = await getGoodFirstIssues({ page_size: 10 });
Expand Down
77 changes: 0 additions & 77 deletions components/filter.tsx

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,17 @@ import { useRouter } from "next/navigation";
import { Chip } from "@nextui-org/chip";
import { createUrl } from "@/utils/url";

interface IRemoveFilters {
interface IClearFilters {
param?: string;
value: string;
onClear: () => void;
}

export const RemoveFilters = ({ param, value }: IRemoveFilters) => {
export const ClearFilters = ({ param, value, onClear }: IClearFilters) => {
const router = useRouter();
const pathname = usePathname();
const searchParams = useSearchParams();
const removeSearchParams = () => {
const clearSearchParams = () => {
if (!!param) {
const optionNameLowerCase = param.toLowerCase();
const optionSearchParams = new URLSearchParams(searchParams.toString());
Expand All @@ -24,14 +25,15 @@ export const RemoveFilters = ({ param, value }: IRemoveFilters) => {
} else {
router.replace(pathname);
}
onClear();
};
return (
<div className="flex gap-4">
<Chip variant="solid" onClose={removeSearchParams}>
<Chip variant="solid" onClose={clearSearchParams}>
{value}
</Chip>
</div>
);
};

export default RemoveFilters;
export default ClearFilters;
68 changes: 68 additions & 0 deletions components/filters/select-filter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
"use client";
import React from "react";
import { usePathname, useSearchParams } from "next/navigation";
import { useRouter } from "next/navigation";
import { Select, SelectItem } from "@nextui-org/select";
import { FilterOption } from "@/types/filters";
import { createUrl } from "@/utils/url";
import Emoji from "../emoji";

interface ISelectFilterProps {
placeholder: string;
mainEmoji: string;
options: FilterOption[];
selectedKey?: string;
onSelect: (value: string) => void;
}
export const SelectFilter = ({
placeholder,
mainEmoji,
options,
selectedKey,
onSelect,
}: ISelectFilterProps) => {
const router = useRouter();
const pathname = usePathname();
const searchParams = useSearchParams();

const handleSelectionChange = (selection: unknown) => {
const selectedValue = Array.from(selection as Iterable<string>)[0];
onSelect(selectedValue);
};

return (
<Select
aria-label={`Select Filter ${placeholder}`}
color="default"
variant="faded"
size="sm"
placeholder={placeholder}
selectionMode="single"
startContent={<Emoji emoji={mainEmoji} className="text-sm" />}
selectedKeys={selectedKey ? new Set([selectedKey]) : new Set()}
onSelectionChange={handleSelectionChange}
>
{options.map(({ emoji, label, value }) => {
const optionNameLowerCase = placeholder.toLowerCase();
const optionSearchParams = new URLSearchParams(searchParams.toString());
optionSearchParams.set(optionNameLowerCase, value);
const optionUrl = createUrl(pathname, optionSearchParams);

return (
<SelectItem
key={value}
value={value}
startContent={<Emoji emoji={emoji} />}
onClick={() => {
router.replace(optionUrl, { scroll: false });
}}
>
{label}
</SelectItem>
);
})}
</Select>
);
};

export default SelectFilter;
47 changes: 47 additions & 0 deletions components/filters/toolbar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
"use client";
import { LANGUAGES_OPTIONS, INTERESTS_OPTIONS } from "@/data/filters";
import { useFilters } from "@/hooks/useFilters";
import { SearchParams } from "@/types/filters";
import ClearFilters from "./clear-filters";
import SelectFilter from "./select-filter";

interface IToolbarProps {
searchParams: SearchParams;
}

const Toolbar = ({ searchParams }: IToolbarProps) => {
const { filters, updateFilter, clearAllFilters } = useFilters({
initialParams: searchParams,
});

const handleSelect = (paramKey: string) => (value: string) => {
updateFilter(paramKey, value);
};

const hasFilters = Object.keys(filters).length > 0;

return (
<div className="flex justify-around items-stretch gap-4">
<SelectFilter
placeholder="Languages"
mainEmoji="🌐"
options={LANGUAGES_OPTIONS}
selectedKey={filters.languages}
onSelect={handleSelect("languages")}
/>
<SelectFilter
placeholder="Interests"
mainEmoji="🪄"
options={INTERESTS_OPTIONS}
selectedKey={filters.interests}
onSelect={handleSelect("interests")}
/>

{hasFilters && (
<ClearFilters onClear={clearAllFilters} value="All filters" />
)}
</div>
);
};

export default Toolbar;
10 changes: 5 additions & 5 deletions components/search.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use client";
import React, { FC, useEffect } from "react";
import { Autocomplete, AutocompleteItem } from "@nextui-org/autocomplete";
import { FilterItem } from "@/types/filters";
import { FilterOption } from "@/types/filters";
import Emoji from "./emoji";
import { useRouter } from "next/navigation";
import { usePathname, useSearchParams } from "next/navigation";
Expand All @@ -10,8 +10,8 @@ import { createUrl } from "@/utils/url";
interface SearchProps {
placeholder: string;
emoji: string;
items: FilterItem[];
selectedValue: string;
items: FilterOption[];
selectedValue?: string;
}

const Search: FC<SearchProps> = ({
Expand Down Expand Up @@ -55,9 +55,9 @@ const Search: FC<SearchProps> = ({
className="max-w-md"
size="lg"
selectedKey={value}
onSelectionChange={setValue}
// onSelectionChange={setValue}
>
{(item: FilterItem) => {
{(item: FilterOption) => {
return (
<AutocompleteItem key={item.value} textValue={item.label}>
<Emoji emoji={item.emoji} className="text-xl"></Emoji>
Expand Down
2 changes: 1 addition & 1 deletion hooks/useContributions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
QueryKey,
} from "@tanstack/react-query";
import { queryDatabase } from "@/lib/notion";
import { transformNotionDataToContributions } from "@/utils/contribution";
import { transformNotionDataToContributions } from "@/utils/notion";
import { PaginatedContributions } from "@/types/contribution";
import { KudosQueryParameters } from "@/lib/notion/types";

Expand Down
Loading