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

@@ -1,4 +1,10 @@
import { HIDDEN_PRODUCT_TAG, SHOPIFY_GRAPHQL_API_ENDPOINT, TAGS } from 'lib/constants';
import {
AVAILABILITY_FILTER_ID,
HIDDEN_PRODUCT_TAG,
PRICE_FILTER_ID,
SHOPIFY_GRAPHQL_API_ENDPOINT,
TAGS
} from 'lib/constants';
import { isShopifyError } from 'lib/type-guards';
import { ensureStartsWith, normalizeUrl, parseMetaFieldValue } from 'lib/utils';
import { revalidateTag } from 'next/cache';
@@ -27,6 +33,7 @@ import {
Cart,
Collection,
Connection,
Filter,
Image,
Menu,
Money,
@@ -41,6 +48,7 @@ import {
ShopifyCollectionProductsOperation,
ShopifyCollectionsOperation,
ShopifyCreateCartOperation,
ShopifyFilter,
ShopifyMenuOperation,
ShopifyPageOperation,
ShopifyPagesOperation,
@@ -168,6 +176,31 @@ const reshapeCollections = (collections: ShopifyCollection[]) => {
return reshapedCollections;
};
const reshapeFilters = (filters: ShopifyFilter[]): Filter[] => {
const reshapedFilters = [];
for (const filter of filters) {
const values = filter.values
.map((valueItem) => {
try {
return {
...valueItem,
...(![AVAILABILITY_FILTER_ID, PRICE_FILTER_ID].includes(filter.id)
? { value: JSON.parse(valueItem.input).productMetafield.value }
: { value: JSON.parse(valueItem.input) })
};
} catch (error) {
return null;
}
})
.filter(Boolean) as Filter['values'];
reshapedFilters.push({ ...filter, values });
}
return reshapedFilters;
};
const reshapeImages = (images: Connection<Image>, productTitle: string) => {
const flattened = removeEdgesAndNodes(images);
@@ -305,28 +338,34 @@ export async function getCollection(handle: string): Promise<Collection | undefi
export async function getCollectionProducts({
collection,
reverse,
sortKey
sortKey,
filters
}: {
collection: string;
reverse?: boolean;
sortKey?: string;
}): Promise<Product[]> {
filters?: Array<object>;
}): Promise<{ products: Product[]; filters: Filter[] }> {
const res = await shopifyFetch<ShopifyCollectionProductsOperation>({
query: getCollectionProductsQuery,
tags: [TAGS.collections, TAGS.products],
variables: {
handle: collection,
reverse,
sortKey: sortKey === 'CREATED_AT' ? 'CREATED' : sortKey
sortKey: sortKey === 'CREATED_AT' ? 'CREATED' : sortKey,
filters
}
});
if (!res.body.data.collection) {
console.log(`No collection found for \`${collection}\``);
return [];
return { products: [], filters: [] };
}
return reshapeProducts(removeEdgesAndNodes(res.body.data.collection.products));
return {
products: reshapeProducts(removeEdgesAndNodes(res.body.data.collection.products)),
filters: reshapeFilters(res.body.data.collection.products.filters)
};
}
export async function getCollections(): Promise<Collection[]> {

View File

@@ -41,14 +41,26 @@ export const getCollectionProductsQuery = /* GraphQL */ `
$handle: String!
$sortKey: ProductCollectionSortKeys
$reverse: Boolean
$filters: [ProductFilter!]
) {
collection(handle: $handle) {
products(sortKey: $sortKey, reverse: $reverse, first: 100) {
products(sortKey: $sortKey, filters: $filters, reverse: $reverse, first: 100) {
edges {
node {
...product
}
}
filters {
id
label
type
values {
id
count
input
label
}
}
}
}
}

View File

@@ -223,13 +223,16 @@ export type ShopifyCollectionOperation = {
export type ShopifyCollectionProductsOperation = {
data: {
collection: {
products: Connection<ShopifyProduct>;
products: Connection<ShopifyProduct> & {
filters: ShopifyFilter[];
};
};
};
variables: {
handle: string;
reverse?: boolean;
sortKey?: string;
filters?: Array<object>;
};
};
@@ -296,3 +299,35 @@ export type CoreChargeOption = {
value: string;
price: Money;
};
export type ShopifyFilter = {
id: string;
label: string;
type: FilterType;
values: {
id: string;
input: string;
count: number;
label: string;
}[];
};
export enum FilterType {
// eslint-disable-next-line no-unused-vars
LIST = 'LIST',
// eslint-disable-next-line no-unused-vars
PRICE_RANGE = 'PRICE_RANGE'
}
export type Filter = {
id: string;
label: string;
type: FilterType;
values: {
id: string;
input: string;
count: number;
label: string;
value: unknown;
}[];
};