diff --git a/apps/admin/src/generated/graphql.ts b/apps/admin/src/generated/graphql.ts index aaac34c..43a0a44 100644 --- a/apps/admin/src/generated/graphql.ts +++ b/apps/admin/src/generated/graphql.ts @@ -471,6 +471,8 @@ export type QueryPromoArgs = { }; export type QueryQueryProductsArgs = { + limit?: InputMaybe; + offset?: InputMaybe; query: Scalars["String"]; }; diff --git a/apps/api/src/resolvers/product.ts b/apps/api/src/resolvers/product.ts index 68c38c3..abd516b 100644 --- a/apps/api/src/resolvers/product.ts +++ b/apps/api/src/resolvers/product.ts @@ -16,6 +16,15 @@ class ProductSummary { count!: number; } +@ObjectType() +class PaginatedProducts { + @Field(() => [Product]) + products!: Product[]; + + @Field(() => Boolean) + hasMore!: boolean; +} + @Resolver(Product) export class ProductResolver { @Query(() => ProductSummary, { nullable: true }) @@ -54,8 +63,14 @@ export class ProductResolver { .getMany(); } - @Query(() => [Product], { nullable: true }) - async queryProducts(@Arg("query") query: string): Promise { + @Query(() => PaginatedProducts, { nullable: true }) + async queryProducts( + @Arg("query") query: string, + @Arg("limit", { nullable: true }) limit?: number, + @Arg("offset", { nullable: true }) offset?: number + ): Promise { + const realLimit = Math.min(30, limit ?? 30); + const realLimitPlusOne = realLimit + 1; try { const variants = JSON.parse(query); @@ -84,7 +99,7 @@ export class ProductResolver { }); }); - return await Product.find({ + const products = await Product.find({ relations: { inventories: true, images: true, @@ -104,10 +119,16 @@ export class ProductResolver { isPublished: true, }, }, + take: realLimitPlusOne, + skip: offset ?? 0, }); + + return { + products: products.slice(0, realLimit), + hasMore: products.length === realLimitPlusOne, + }; } catch (error) { - console.error(error); - return await Product.find({ + const products = await Product.find({ relations: { inventories: true, images: true, @@ -123,6 +144,11 @@ export class ProductResolver { }, }, }); + + return { + products: products.slice(0, realLimit), + hasMore: products.length === realLimitPlusOne, + }; } } diff --git a/apps/storefront/src/generated/graphql.ts b/apps/storefront/src/generated/graphql.ts index dbb8fcf..fb45337 100644 --- a/apps/storefront/src/generated/graphql.ts +++ b/apps/storefront/src/generated/graphql.ts @@ -295,6 +295,12 @@ export type OrderItem = { updated_at: Scalars["String"]; }; +export type PaginatedProducts = { + __typename?: "PaginatedProducts"; + hasMore: Scalars["Boolean"]; + products: Array; +}; + export type PaymentDetail = { __typename?: "PaymentDetail"; amount: Scalars["Float"]; @@ -445,7 +451,7 @@ export type Query = { products?: Maybe>; productsSummary?: Maybe; promo?: Maybe; - queryProducts?: Maybe>; + queryProducts?: Maybe; reviewByUserAndProduct?: Maybe; reviewSummary?: Maybe; reviews?: Maybe>; @@ -471,6 +477,8 @@ export type QueryPromoArgs = { }; export type QueryQueryProductsArgs = { + limit?: InputMaybe; + offset?: InputMaybe; query: Scalars["String"]; }; @@ -2262,21 +2270,27 @@ export type ProductsQuery = { export type QueryProductsQueryVariables = Exact<{ query: Scalars["String"]; + limit?: InputMaybe; + offset?: InputMaybe; }>; export type QueryProductsQuery = { __typename?: "Query"; - queryProducts?: Array<{ - __typename?: "Product"; - id: number; - identifier: string; - name: string; - images: Array<{ __typename?: "ProductImage"; imageURL: string }>; - inventories?: Array<{ - __typename?: "ProductInventory"; - price: number; - }> | null; - }> | null; + queryProducts?: { + __typename?: "PaginatedProducts"; + hasMore: boolean; + products: Array<{ + __typename?: "Product"; + id: number; + identifier: string; + name: string; + images: Array<{ __typename?: "ProductImage"; imageURL: string }>; + inventories?: Array<{ + __typename?: "ProductInventory"; + price: number; + }> | null; + }>; + } | null; }; export type SearchProductsQueryVariables = Exact<{ @@ -4471,16 +4485,19 @@ export type ProductsQueryResult = Apollo.QueryResult< ProductsQueryVariables >; export const QueryProductsDocument = gql` - query QueryProducts($query: String!) { - queryProducts(query: $query) { - id - identifier - name - images { - imageURL - } - inventories { - price + query QueryProducts($query: String!, $limit: Float, $offset: Float) { + queryProducts(query: $query, limit: $limit, offset: $offset) { + hasMore + products { + id + identifier + name + images { + imageURL + } + inventories { + price + } } } } @@ -4499,6 +4516,8 @@ export const QueryProductsDocument = gql` * const { data, loading, error } = useQueryProductsQuery({ * variables: { * query: // value for 'query' + * limit: // value for 'limit' + * offset: // value for 'offset' * }, * }); */ diff --git a/apps/storefront/src/graphql/query/product/queryProducts.graphql b/apps/storefront/src/graphql/query/product/queryProducts.graphql index 27dfeda..d35158e 100644 --- a/apps/storefront/src/graphql/query/product/queryProducts.graphql +++ b/apps/storefront/src/graphql/query/product/queryProducts.graphql @@ -1,13 +1,16 @@ -query QueryProducts($query: String!) { - queryProducts(query: $query) { - id - identifier - name - images { - imageURL - } - inventories { - price +query QueryProducts($query: String!, $limit: Float, $offset: Float) { + queryProducts(query: $query, limit: $limit, offset: $offset) { + hasMore + products { + id + identifier + name + images { + imageURL + } + inventories { + price + } } } } diff --git a/apps/storefront/src/pages/_app.tsx b/apps/storefront/src/pages/_app.tsx index 5ba8b02..10a4be2 100644 --- a/apps/storefront/src/pages/_app.tsx +++ b/apps/storefront/src/pages/_app.tsx @@ -12,7 +12,30 @@ const App = ({ Component, pageProps }: AppProps) => { const client = new ApolloClient({ uri: process.env.NEXT_PUBLIC_API_URL, - cache: new InMemoryCache(), + cache: new InMemoryCache({ + // ! Pagination is working, but filtering stops working after this + // typePolicies: { + // Query: { + // fields: { + // queryProducts: { + // keyArgs: [], + // merge( + // existing: PaginatedProducts | undefined, + // incomming: PaginatedProducts + // ): PaginatedProducts { + // return { + // ...incomming, + // products: [ + // ...(existing?.products || []), + // ...incomming.products, + // ], + // }; + // }, + // }, + // }, + // }, + // }, + }), credentials: "include", }); diff --git a/apps/storefront/src/pages/products/index.tsx b/apps/storefront/src/pages/products/index.tsx index 9770d0f..1c20b06 100644 --- a/apps/storefront/src/pages/products/index.tsx +++ b/apps/storefront/src/pages/products/index.tsx @@ -19,6 +19,8 @@ import { Box, HStack, SimpleGrid, Spinner } from "@chakra-ui/react"; import { useState } from "react"; const ProductFilterPage = () => { + // const ref = useRef(null); + // const isInView = useInView(ref); const { data, loading, error } = useVariantsQuery(); const [selectedVariant, setSelectedVariant] = useState< @@ -32,9 +34,30 @@ const ProductFilterPage = () => { } = useQueryProductsQuery({ variables: { query: JSON.stringify(selectedVariant), + limit: 12, + offset: 0, }, }); + // useEffect(() => { + // if (isInView && products?.queryProducts?.hasMore) { + // fetchMore({ + // variables: { + // query: JSON.stringify(selectedVariant), + // limit: 6, + // offset: products?.queryProducts?.products?.length, + // }, + // }); + // } + // // eslint-disable-next-line react-hooks/exhaustive-deps + // }, [ + // fetchMore, + // isInView, + // pLoading, + // products?.queryProducts?.hasMore, + // selectedVariant, + // ]); + if (error) return ( { dump={pError.stack} /> ) : ( - products?.queryProducts?.map((product) => ( + products?.queryProducts?.products.map((product) => ( )) )} + {/* {products?.queryProducts?.hasMore && ( + + )} */} + {/* + {!pLoading && products?.queryProducts?.hasMore && } + */}