added category filter bar; clear filter button

This commit is contained in:
2025-03-15 15:43:56 +01:00
parent 4ad0357133
commit 1ccbcddae3
5 changed files with 169 additions and 40 deletions
@@ -0,0 +1,99 @@
"use client";
import { cn, debounce } from "@/lib/utils";
import React from "react";
import { Input } from "../ui/input";
import { SearchIcon, XIcon } from "lucide-react";
import CategorySelect from "../category/category-select";
import SortFilterSelect from "../sort-filter-select";
import { Button } from "../ui/button";
export type CategoryFilter = {
query?: string;
sort?: string;
};
const defaultFilter: CategoryFilter = {
query: "",
sort: undefined,
};
export default function CategoryFilterBar({
className,
onFilterUpdate,
}: {
className?: string;
onFilterUpdate: (filter: CategoryFilter) => void;
}) {
const [filter, setFilter] = React.useState<CategoryFilter>(defaultFilter);
const onFilterChange = React.useCallback(
(newFilter: Partial<CategoryFilter>, notify: boolean = true) => {
const f = { ...filter, ...newFilter };
setFilter(f);
if (notify) onFilterUpdate(f);
},
[filter, onFilterUpdate], // Ensure dependencies are listed
);
const debouncedSearch = React.useMemo(
() =>
debounce((query: string) => {
onFilterChange({ query });
}, 300),
[],
);
const hasFilter = filter.query?.length || filter.sort?.length;
return (
<>
<div
className={cn(
"flex w-full flex-col-reverse items-center justify-center gap-2 rounded-md border bg-background p-2 lg:flex-row lg:gap-4",
className,
)}
>
<div className="relative w-full">
<SearchIcon className="absolute left-2 top-1/2 size-4 -translate-y-1/2 text-muted-foreground" />
<Input
className="border-0 p-0 pl-8 shadow-none focus-visible:ring-0"
value={filter.query}
onChange={(e) => {
onFilterChange({ query: e.currentTarget.value }, false);
debouncedSearch(e.currentTarget.value);
}}
placeholder="Kategorie Suchen..."
/>
</div>
<div className="flex w-full flex-col items-center justify-end gap-2 sm:flex-row">
<Button
disabled={!hasFilter}
variant={"ghost"}
className={cn(
"text-xs text-muted-foreground",
hasFilter
? "flex lg:opacity-100"
: "hidden lg:opacity-0 lg:disabled:opacity-0",
)}
onClick={() => onFilterChange(defaultFilter)}
>
<span>Filter entfernen</span>
<XIcon className="size-4" />
</Button>
<SortFilterSelect
initialValue={filter.sort}
onSelect={(currentValue) => {
onFilterChange({
sort: currentValue?.length ? currentValue : undefined,
});
}}
className="w-full lg:max-w-64"
buttonProps={{
size: "sm",
}}
/>
</div>
</div>
</>
);
}
@@ -7,16 +7,14 @@ import CategoryCard from "../category-card";
import { useInfiniteItemsObserver } from "@/lib/hooks/infinite-items-observer-hook";
import { Skeleton } from "@/components/ui/skeleton";
import { CATEGORY_GRID_CLASS } from "./category-grid";
// import ArticleFilterBar, { ArticleFilter } from "../article-filter-bar";
import CategoryFilterBar, { CategoryFilter } from "../category-filter-bar";
export default function InfiniteCategoryGrid() {
// const [filter, setFilter] = React.useState<ArticleFilter | undefined>(
// undefined,
// );
const [filter, setFilter] = React.useState<CategoryFilter>({});
const { data, fetchNextPage, hasNextPage, isFetchingNextPage, isLoading } =
api.category.getByCursor.useInfiniteQuery(
{
// filter,
filter,
},
{
getNextPageParam: (lastPage) => lastPage.nextCursor,
@@ -39,7 +37,7 @@ export default function InfiniteCategoryGrid() {
return (
<div className="relative space-y-4">
{/* <ArticleFilterBar onFilterUpdate={setFilter} /> */}
<CategoryFilterBar onFilterUpdate={setFilter} />
<menu className={`${CATEGORY_GRID_CLASS} overflow-auto`}>
{data?.pages?.length
? allItems.map((category, idx) => (