Skip to content

Commit

Permalink
feature/add catalog query filter values and corresponding ui logic (#110
Browse files Browse the repository at this point in the history
)

* add query filter values and corresponding ui logic

* simplify infinite scroll logic

* fix query filters
  • Loading branch information
dogfrogfog authored Jul 13, 2024
1 parent 57a5bb1 commit a989afa
Show file tree
Hide file tree
Showing 14 changed files with 361 additions and 136 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"react": "19.0.0-rc-6d3110b4d9-20240531",
"react-dom": "19.0.0-rc-6d3110b4d9-20240531",
"react-hook-form": "^7.52.0",
"react-intersection-observer": "^9.13.0",
"react-photo-view": "^1.2.4",
"zod": "^3.23.8"
},
Expand Down
18 changes: 18 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 17 additions & 5 deletions src/app/catalog/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,33 @@ import Filters from "@/components/Filters";
import CoreLayout from "@/components/layouts/CoreLayout";
import { CATALOG_LIMIT } from "@/constants";
import { serviceClient } from "@/lib/api";
import { getValidatedGetProductsPagedParams } from "@/lib/utils/queryFilters";

export default async function Page() {
interface CatalogPageProps {
searchParams: {
category?: string;
gender?: string;
order?: string;
sort?: string;
size?: string;
};
}

export default async function CatalogPage({ searchParams }: CatalogPageProps) {
const response = await serviceClient.GetProductsPaged({
limit: CATALOG_LIMIT,
offset: 0,
sortFactors: undefined,
orderFactor: undefined,
filterConditions: undefined,
...getValidatedGetProductsPagedParams(searchParams),
});

return (
<CoreLayout>
<div>
<Filters />
<CatalogSection firstPageItems={response.products || []} />
<CatalogSection
total={response.total || 0}
firstPageItems={response.products || []}
/>
</div>
</CoreLayout>
);
Expand Down
56 changes: 56 additions & 0 deletions src/components/Filters/Category.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
"use client";

import { useHeroContext } from "../contexts/HeroContext";
import GenericPopover from "../ui/Popover";
import useFilterQueryParams from "./useFilterQueryParams";
import FilterOptionButtons from "./FilterOptionButtons";

function Trigger({ defaultValue }: { defaultValue: string }) {
return <div>{defaultValue}</div>;
}

export default function Category() {
const { dictionary } = useHeroContext();
const {
defaultValue: defaultCategory,
handleFilterChange: handleCategoryChange,
} = useFilterQueryParams("category");
const {
defaultValue: defaultGender,
handleFilterChange: handleGenderChange,
} = useFilterQueryParams("gender");

return (
<GenericPopover
contentProps={{
side: "bottom",
align: "start",
}}
title="categories"
openElement={
<Trigger
defaultValue={`${defaultCategory || "all categories"} / ${defaultGender || "all genders"}`}
/>
}
>
<div className="mb-8">
<FilterOptionButtons
defaultOptionText="all"
defaultValue={defaultCategory || ""}
handleFilterChange={handleCategoryChange}
values={dictionary?.categories || []}
/>
</div>

<div className="mb-8">
<div className="mb-4">gender</div>
<FilterOptionButtons
defaultValue={defaultGender || ""}
handleFilterChange={handleGenderChange}
values={dictionary?.genders || []}
defaultOptionText="all"
/>
</div>
</GenericPopover>
);
}
51 changes: 51 additions & 0 deletions src/components/Filters/FilterOptionButtons.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"use client";

import {
common_Category,
common_OrderFactors,
common_Size,
common_SortFactors,
common_Genders,
} from "@/api/proto-http/frontend";
import { cn } from "@/lib/utils";

export default function FilterOptionButtons({
handleFilterChange,
defaultValue,
defaultOptionText,
values,
}: {
handleFilterChange: (term?: string) => void;
defaultValue: string;
defaultOptionText?: string;
values:
| common_Category[]
| common_OrderFactors[]
| common_SortFactors[]
| common_Size[]
| common_Genders[];
}) {
return (
<>
{defaultOptionText && (
<button
onClick={() => handleFilterChange()}
className={cn("block", { underline: !defaultValue })}
>
{defaultOptionText}
</button>
)}
{values.map((factor) => (
<button
onClick={() => handleFilterChange(factor.id + "")}
className={cn("block", {
underline: factor.id + "" === defaultValue,
})}
key={factor.id}
>
{factor.name}
</button>
))}
</>
);
}
37 changes: 37 additions & 0 deletions src/components/Filters/Order.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"use client";

import { useHeroContext } from "../contexts/HeroContext";
import GenericPopover from "../ui/Popover";
import useFilterQueryParams from "./useFilterQueryParams";
import FilterOptionButtons from "./FilterOptionButtons";

function Trigger({ defaultValue }: { defaultValue: string }) {
return (
<div>
order <span className="underline">{defaultValue}</span>
</div>
);
}

export default function Order() {
const { dictionary } = useHeroContext();
const { defaultValue, handleFilterChange } = useFilterQueryParams("order");

return (
<GenericPopover
contentProps={{
side: "bottom",
align: "end",
}}
title="order_by"
openElement={<Trigger defaultValue={defaultValue || ""} />}
>
<FilterOptionButtons
defaultOptionText="none"
defaultValue={defaultValue || ""}
handleFilterChange={handleFilterChange}
values={dictionary?.orderFactors || []}
/>
</GenericPopover>
);
}
36 changes: 36 additions & 0 deletions src/components/Filters/Size.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
"use client";
import { useHeroContext } from "../contexts/HeroContext";
import GenericPopover from "../ui/Popover";
import useFilterQueryParams from "./useFilterQueryParams";
import FilterOptionButtons from "./FilterOptionButtons";

function Trigger({ defaultValue }: { defaultValue: string }) {
return (
<div>
size <span className="underline">{defaultValue}</span>
</div>
);
}

export default function Size() {
const { dictionary } = useHeroContext();
const { defaultValue, handleFilterChange } = useFilterQueryParams("size");

return (
<GenericPopover
contentProps={{
side: "bottom",
align: "end",
}}
title="size"
openElement={<Trigger defaultValue={defaultValue || ""} />}
>
<FilterOptionButtons
defaultValue={defaultValue || ""}
handleFilterChange={handleFilterChange}
values={dictionary?.sizes || []}
defaultOptionText="all sizes"
/>
</GenericPopover>
);
}
37 changes: 37 additions & 0 deletions src/components/Filters/Sort.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"use client";

import GenericPopover from "@/components/ui/Popover";
import { useHeroContext } from "../contexts/HeroContext";
import useFilterQueryParams from "./useFilterQueryParams";
import FilterOptionButtons from "./FilterOptionButtons";

function Trigger({ defaultValue }: { defaultValue: string }) {
return (
<div>
sort_by <span className="underline">{defaultValue}</span>
</div>
);
}

export default function Sort() {
const { dictionary } = useHeroContext();
const { defaultValue, handleFilterChange } = useFilterQueryParams("sort");

return (
<GenericPopover
contentProps={{
side: "bottom",
align: "end",
}}
title="order_by"
openElement={<Trigger defaultValue={defaultValue || ""} />}
>
<FilterOptionButtons
defaultValue={defaultValue || ""}
handleFilterChange={handleFilterChange}
values={dictionary?.sortFactors || []}
defaultOptionText="none"
/>
</GenericPopover>
);
}
Loading

0 comments on commit a989afa

Please sign in to comment.