feat: filter by product meta field

Signed-off-by: Chloe <pinkcloudvnn@gmail.com>
This commit is contained in:
Chloe
2024-05-06 21:59:17 +07:00
parent 0eba468825
commit 305fe3d458
14 changed files with 241 additions and 28 deletions

View File

@@ -0,0 +1,58 @@
'use client';
import clsx from 'clsx';
import { Filter } from 'lib/shopify/types';
import { createUrl } from 'lib/utils';
import { usePathname, useRouter, useSearchParams } from 'next/navigation';
const Filters = ({ filters }: { filters: Filter[] }) => {
const router = useRouter();
const pathname = usePathname();
const searchParams = useSearchParams();
const handleChange = (e: React.FormEvent<HTMLFormElement>) => {
const formData = new FormData(e.currentTarget);
const newSearchParams = new URLSearchParams(searchParams);
Array.from(formData.keys()).forEach((key) => {
const values = formData.getAll(key);
newSearchParams.delete(key);
values.forEach((value) => newSearchParams.append(key, String(value)));
});
router.replace(createUrl(pathname, newSearchParams), { scroll: false });
};
return (
<form onChange={handleChange} className="space-y-5 divide-y divide-gray-200">
{filters.map(({ label, id, values }) => (
<div key={id} className="flex h-auto max-h-[550px] flex-col gap-y-3 overflow-hidden pt-5">
<div className="block text-sm font-medium text-gray-900">{label}</div>
<div className="flex-grow space-y-3 overflow-auto pb-1 pl-1 pt-2">
{values.map(({ id: valueId, label, count, value }) => (
<label
key={valueId}
htmlFor={valueId}
className={clsx('flex items-center gap-2 text-sm text-gray-600', {
'cursor-not-allowed opacity-50': count === 0
})}
>
<input
id={valueId}
name={id}
defaultChecked={searchParams.getAll(id).includes(String(value))}
type="checkbox"
value={String(value)}
className="h-4 w-4 rounded border-gray-300 text-secondary focus:ring-secondary disabled:cursor-not-allowed disabled:opacity-50"
disabled={count === 0}
/>
<span>{`${label} (${count})`}</span>
</label>
))}
</div>
</div>
))}
</form>
);
};
export default Filters;

View File

@@ -1,7 +1,9 @@
import { getMenu } from 'lib/shopify';
import { Filter } from 'lib/shopify/types';
import Link from 'next/link';
import FiltersList from './filters-list';
const Filters = async ({ collection }: { collection: string }) => {
const Filters = async ({ collection, filters }: { collection: string; filters: Filter[] }) => {
const menu = await getMenu('main-menu');
const subMenu = menu.find((item) => item.path === `/search/${collection}`)?.items || [];
return (
@@ -23,6 +25,8 @@ const Filters = async ({ collection }: { collection: string }) => {
</ul>
</>
) : null}
<h3 className="sr-only">Filters</h3>
<FiltersList filters={filters} />
</div>
);
};

View File

@@ -8,15 +8,17 @@ const SortingItem = ({ item, hover }: { item: SortFilterItem; hover: boolean })
const pathname = usePathname();
const searchParams = useSearchParams();
const active = searchParams.get('sort') === item.slug;
const q = searchParams.get('q');
const href = createUrl(
pathname,
new URLSearchParams({
...(q && { q }),
...(item.slug && item.slug.length && { sort: item.slug })
})
);
const newSearchParams = new URLSearchParams(searchParams);
if (item.slug && item.slug.length) {
newSearchParams.set('sort', item.slug);
} else {
newSearchParams.delete('sort');
}
const href = createUrl(pathname, newSearchParams);
const DynamicTag = active ? 'p' : Link;
return (
<DynamicTag
prefetch={!active ? false : undefined}