From c9a93229183b964d11c15339f38c3cfe63665ec4 Mon Sep 17 00:00:00 2001 From: yuriassuncx Date: Fri, 1 Nov 2024 13:56:43 -0300 Subject: [PATCH] feat: add productCategory and productListingPage loader --- .../intelligentSearch/productListingPage.ts | 4 +- .../loaders/product/productCategory.ts | 38 ++++++++ .../loaders/product/productListingPage.ts | 92 +++++++++++++++++++ woocommerce/manifest.gen.ts | 16 ++-- woocommerce/utils/utils.ts | 8 ++ 5 files changed, 151 insertions(+), 7 deletions(-) create mode 100644 woocommerce/loaders/product/productCategory.ts create mode 100644 woocommerce/loaders/product/productListingPage.ts create mode 100644 woocommerce/utils/utils.ts diff --git a/vtex/loaders/intelligentSearch/productListingPage.ts b/vtex/loaders/intelligentSearch/productListingPage.ts index b392cfd3e..60b9126b5 100644 --- a/vtex/loaders/intelligentSearch/productListingPage.ts +++ b/vtex/loaders/intelligentSearch/productListingPage.ts @@ -269,7 +269,9 @@ const loader = async ( ? filtersFromPathname(pageTypes) : baseSelectedFacets; const selected = withDefaultFacets(selectedFacets, ctx); - const fselected = props.priceFacets ? selected : selected.filter((f) => f.key !== "price"); + const fselected = props.priceFacets + ? selected + : selected.filter((f) => f.key !== "price"); const isInSeachFormat = Boolean(selected.length) || Boolean(args.query); const pathQuery = queryFromPathname(isInSeachFormat, pageTypes, url.pathname); const searchArgs = { ...args, query: args.query || pathQuery }; diff --git a/woocommerce/loaders/product/productCategory.ts b/woocommerce/loaders/product/productCategory.ts new file mode 100644 index 000000000..d1d6a3ec6 --- /dev/null +++ b/woocommerce/loaders/product/productCategory.ts @@ -0,0 +1,38 @@ +import { RequestURLParam } from "../../../website/functions/requestToParam.ts"; +import { AppContext } from "../../mod.ts"; +import { Category } from "../../utils/types.ts"; + +export interface Props { + slug?: RequestURLParam; +} + +/** + * @title WooCommerce Integration + * @description Product Category loader + */ +async function loader( + props: Props, + req: Request, + ctx: AppContext, +): Promise { + const { slug } = props; + const { api } = ctx; + + const urlPathname = new URL(req.url).pathname; + + const pathname = (slug || urlPathname).split("/").filter(Boolean).pop(); + + if (!pathname) return null; + + const categories = await api["GET /wc/v3/products/categories"]({ + slug, + }).then((res) => res.json()); + + const category = categories.find((item) => item.slug === pathname); + + if (!category) return null; + + return category; +} + +export default loader; diff --git a/woocommerce/loaders/product/productListingPage.ts b/woocommerce/loaders/product/productListingPage.ts new file mode 100644 index 000000000..c91a5ccd3 --- /dev/null +++ b/woocommerce/loaders/product/productListingPage.ts @@ -0,0 +1,92 @@ +import type { ProductListingPage } from "../../../commerce/types.ts"; +import { AppContext } from "../../mod.ts"; +import { toProduct } from "../../utils/transform.ts"; +import { Order, OrderBy, Status, StockStatus } from "../../utils/types.ts"; +import { WOOCOMMERCE_SORT_OPTIONS } from "../../utils/utils.ts"; + +export interface Props { + /** + * @description overrides the query term at url + */ + query?: string; + /** + * @default 1 + */ + page: number; + /** + * @title Per Page + * @default 12 + * @description Maximum number of items to be returned in result set. Default is 12. + */ + per_page: number; + order?: Order; + order_by?: OrderBy; + status?: Status; + stock_status?: StockStatus; +} + +/** + * @title WooCommerce Integration + * @description Product Listing Page loader + */ +async function loader( + props: Props, + req: Request, + ctx: AppContext, +): Promise { + const url = new URL(req.url); + const pathname = url.pathname.split("/").filter(Boolean).pop(); + + const { page = 1, per_page = 12, query } = props; + const { api } = ctx; + + const category = await ctx.invoke.woocommerce.loaders.product.productCategory( + { + slug: pathname, + }, + ); + + const products = await api["GET /wc/v3/products"]({ + ...props, + page, + per_page, + category: !query ? category?.id?.toString() : undefined, + search: query, + }).then((res) => res.json()); + + if (!products) return null; + + const totalPages = Math.ceil((category?.count ?? 0) / props.per_page); + const notHasNextPage = totalPages == page; + + return { + "@type": "ProductListingPage", + products: products.map((product) => toProduct(product)), + sortOptions: WOOCOMMERCE_SORT_OPTIONS, + filters: [], + pageInfo: { + previousPage: page == 1 ? undefined : Number(page + 1).toString(), + currentPage: page, + nextPage: notHasNextPage ? undefined : Number(page + 1).toString(), + }, + breadcrumb: { + "@type": "BreadcrumbList", + itemListElement: [ + { + "@type": "ListItem" as const, + name: category?.name, + position: 1, + item: new URL(category?.slug ?? "").href, + }, + ], + numberOfItems: category?.count ?? 0, + }, + seo: { + title: query || category?.name || pathname?.replaceAll("-", " ") || "", + description: category?.description ?? "", + canonical: pathname || req.url, + }, + }; +} + +export default loader; diff --git a/woocommerce/manifest.gen.ts b/woocommerce/manifest.gen.ts index e6462f273..5d5d2b83d 100644 --- a/woocommerce/manifest.gen.ts +++ b/woocommerce/manifest.gen.ts @@ -2,15 +2,19 @@ // This file SHOULD be checked into source version control. // This file is automatically updated during development when running `dev.ts`. -import * as $$$0 from "./loaders/product/productDetailsPage.ts"; -import * as $$$1 from "./loaders/product/productList.ts"; -import * as $$$2 from "./loaders/proxy.ts"; +import * as $$$0 from "./loaders/product/productCategory.ts"; +import * as $$$1 from "./loaders/product/productDetailsPage.ts"; +import * as $$$2 from "./loaders/product/productList.ts"; +import * as $$$3 from "./loaders/product/productListingPage.ts"; +import * as $$$4 from "./loaders/proxy.ts"; const manifest = { "loaders": { - "woocommerce/loaders/product/productDetailsPage.ts": $$$0, - "woocommerce/loaders/product/productList.ts": $$$1, - "woocommerce/loaders/proxy.ts": $$$2, + "woocommerce/loaders/product/productCategory.ts": $$$0, + "woocommerce/loaders/product/productDetailsPage.ts": $$$1, + "woocommerce/loaders/product/productList.ts": $$$2, + "woocommerce/loaders/product/productListingPage.ts": $$$3, + "woocommerce/loaders/proxy.ts": $$$4, }, "name": "woocommerce", "baseUrl": import.meta.url, diff --git a/woocommerce/utils/utils.ts b/woocommerce/utils/utils.ts new file mode 100644 index 000000000..8d509a616 --- /dev/null +++ b/woocommerce/utils/utils.ts @@ -0,0 +1,8 @@ +import { SortOption } from "../../commerce/types.ts"; + +export const WOOCOMMERCE_SORT_OPTIONS: SortOption[] = [ + { value: "date", label: "Data" }, + { value: "price", label: "Preço" }, + { value: "popularity", label: "Popularidade" }, + { value: "rating", label: "Média de Classificação" }, +];