From f8118213a7fcdd0e275a1ee13fd2f151d3af057c Mon Sep 17 00:00:00 2001 From: yuriassuncx Date: Fri, 24 May 2024 10:34:36 -0300 Subject: [PATCH] feat: Show More added to PLP --- components/product/ProductGallery.tsx | 70 +++++++++++++-- components/search/SearchResult.tsx | 112 +++++++++++++++--------- components/wishlist/WishlistGallery.tsx | 12 ++- fresh.gen.ts | 2 + islands/ShowMore.tsx | 57 ++++++++++++ sdk/useShowMore.ts | 11 +++ sections/Product/SearchResult.tsx | 2 +- sections/Product/Wishlist.tsx | 5 +- 8 files changed, 216 insertions(+), 55 deletions(-) create mode 100644 islands/ShowMore.tsx create mode 100644 sdk/useShowMore.ts diff --git a/components/product/ProductGallery.tsx b/components/product/ProductGallery.tsx index 9c4c41c..acf9438 100644 --- a/components/product/ProductGallery.tsx +++ b/components/product/ProductGallery.tsx @@ -1,8 +1,11 @@ -import ProductCard, { - Layout as CardLayout, -} from "$store/components/product/ProductCard.tsx"; -import { usePlatform } from "$store/sdk/usePlatform.tsx"; -import { Product } from "apps/commerce/types.ts"; +import { Head } from "$fresh/runtime.ts"; +import { PageInfo, Product } from "apps/commerce/types.ts"; +import { usePartialSection } from "deco/hooks/usePartialSection.ts"; +import ProductCard from "../../components/product/ProductCard.tsx"; +import { Format } from "../../components/search/SearchResult.tsx"; +import Spinner from "../../components/ui/Spinner.tsx"; +import ShowMore from "../../islands/ShowMore.tsx"; +import { usePlatform } from "../../sdk/usePlatform.tsx"; export interface Columns { mobile?: 1 | 2; @@ -11,11 +14,13 @@ export interface Columns { export interface Props { products: Product[] | null; + pageInfo: PageInfo; offset: number; layout?: { - card?: CardLayout; columns?: Columns; + format?: Format; }; + url: URL; } const MOBILE_COLUMNS = { @@ -30,22 +35,69 @@ const DESKTOP_COLUMNS = { 5: "sm:grid-cols-5", }; -function ProductGallery({ products, layout, offset }: Props) { +function ProductGallery( + { products, pageInfo, layout, offset, url }: Props, +) { const platform = usePlatform(); const mobile = MOBILE_COLUMNS[layout?.columns?.mobile ?? 2]; const desktop = DESKTOP_COLUMNS[layout?.columns?.desktop ?? 4]; + const nextPage = pageInfo.nextPage + ? new URL(pageInfo.nextPage, url.href) + : null; + const partialUrl = nextPage ? new URL(nextPage.href) : null; + if (pageInfo.nextPage && nextPage) { + partialUrl?.searchParams.set("partial", "true"); + } + return ( -
+
+ {layout?.format == "Show More" && ( + + {pageInfo.nextPage && } + {pageInfo.previousPage && ( + + )} + + )} + {products?.map((product, index) => ( ))} + + {(layout && layout?.format === "Show More") && ( + <> + + {partialUrl && ( +
+
+ +
+ +
+ )} +
+ + )}
); } diff --git a/components/search/SearchResult.tsx b/components/search/SearchResult.tsx index 8479ff2..c79e3c6 100644 --- a/components/search/SearchResult.tsx +++ b/components/search/SearchResult.tsx @@ -1,14 +1,15 @@ -import { SendEventOnView } from "$store/components/Analytics.tsx"; -import { Layout as CardLayout } from "$store/components/product/ProductCard.tsx"; -import Filters from "$store/components/search/Filters.tsx"; -import Icon from "$store/components/ui/Icon.tsx"; -import SearchControls from "$store/islands/SearchControls.tsx"; -import { useId } from "$store/sdk/useId.ts"; -import { useOffer } from "$store/sdk/useOffer.ts"; import type { ProductListingPage } from "apps/commerce/types.ts"; import { mapProductToAnalyticsItem } from "apps/commerce/utils/productToAnalyticsItem.ts"; +import { SendEventOnView } from "../../components/Analytics.tsx"; +import Filters from "../../components/search/Filters.tsx"; +import Icon from "../../components/ui/Icon.tsx"; +import SearchControls from "../../islands/SearchControls.tsx"; +import { useId } from "../../sdk/useId.ts"; +import { useOffer } from "../../sdk/useOffer.ts"; import ProductGallery, { Columns } from "../product/ProductGallery.tsx"; +export type Format = "Show More" | "Pagination"; + export interface Layout { /** * @description Use drawer for mobile like behavior on desktop. Aside for rendering the filters alongside the products @@ -18,13 +19,16 @@ export interface Layout { * @description Number of products per line on grid */ columns?: Columns; + /** + * @description Format of the pagination + */ + format?: Format; } export interface Props { /** @title Integration */ page: ProductListingPage | null; layout?: Layout; - cardLayout?: CardLayout; /** @description 0 for ?page=0 as your first page */ startingPage?: 0 | 1; @@ -41,65 +45,80 @@ function NotFound() { function Result({ page, layout, - cardLayout, startingPage = 0, -}: Omit & { page: ProductListingPage }) { + url: _url, +}: Omit & { + page: ProductListingPage; + url: string; +}) { const { products, filters, breadcrumb, pageInfo, sortOptions } = page; - const perPage = pageInfo.recordPerPage || products.length; + const perPage = pageInfo?.recordPerPage || products.length; + const url = new URL(_url); + + const { format = "Show More" } = layout ?? {}; const id = useId(); const zeroIndexedOffsetPage = pageInfo.currentPage - startingPage; const offset = zeroIndexedOffsetPage * perPage; + const isPartial = url.searchParams.get("partial") === "true"; + const isFirstPage = !pageInfo.previousPage; + return ( <>
- + {(isFirstPage || !isPartial) && ( + + )}
{layout?.variant === "aside" && filters.length > 0 && ( )}
-
-
- - - Page {zeroIndexedOffsetPage + 1} - - + {format == "Pagination" && ( +
+
+ + + Página {zeroIndexedOffsetPage + 1} + + +
-
+ )}
, +) { if (!page) { return ; } @@ -132,4 +153,11 @@ function SearchResult({ page, ...props }: Props) { return ; } +export const loader = (props: Props, req: Request) => { + return { + ...props, + url: req.url, + }; +}; + export default SearchResult; diff --git a/components/wishlist/WishlistGallery.tsx b/components/wishlist/WishlistGallery.tsx index 8d9ba0b..a3d660f 100644 --- a/components/wishlist/WishlistGallery.tsx +++ b/components/wishlist/WishlistGallery.tsx @@ -1,10 +1,11 @@ +import { SectionProps } from "deco/mod.ts"; import SearchResult, { Props as SearchResultProps, -} from "$store/components/search/SearchResult.tsx"; +} from "../../components/search/SearchResult.tsx"; export type Props = SearchResultProps; -function WishlistGallery(props: Props) { +function WishlistGallery(props: SectionProps) { const isEmpty = !props.page || props.page.products.length === 0; if (isEmpty) { @@ -24,4 +25,11 @@ function WishlistGallery(props: Props) { return ; } +export const loader = (props: Props, req: Request) => { + return { + ...props, + url: req.url, + }; +}; + export default WishlistGallery; diff --git a/fresh.gen.ts b/fresh.gen.ts index a8a81d6..ec437fb 100644 --- a/fresh.gen.ts +++ b/fresh.gen.ts @@ -25,6 +25,7 @@ import * as $ProductImageZoom from "./islands/ProductImageZoom.tsx"; import * as $QuantitySelector from "./islands/QuantitySelector.tsx"; import * as $SearchControls from "./islands/SearchControls.tsx"; import * as $ShippingSimulation from "./islands/ShippingSimulation.tsx"; +import * as $ShowMore from "./islands/ShowMore.tsx"; import * as $SliderJS from "./islands/SliderJS.tsx"; import * as $WishlistButton_vtex from "./islands/WishlistButton/vtex.tsx"; import * as $WishlistButton_wake from "./islands/WishlistButton/wake.tsx"; @@ -58,6 +59,7 @@ const manifest = { "./islands/QuantitySelector.tsx": $QuantitySelector, "./islands/SearchControls.tsx": $SearchControls, "./islands/ShippingSimulation.tsx": $ShippingSimulation, + "./islands/ShowMore.tsx": $ShowMore, "./islands/SliderJS.tsx": $SliderJS, "./islands/WishlistButton/vtex.tsx": $WishlistButton_vtex, "./islands/WishlistButton/wake.tsx": $WishlistButton_wake, diff --git a/islands/ShowMore.tsx b/islands/ShowMore.tsx new file mode 100644 index 0000000..b6f8b1b --- /dev/null +++ b/islands/ShowMore.tsx @@ -0,0 +1,57 @@ +import { PageInfo } from "apps/commerce/types.ts"; +import type { ComponentChildren } from "preact"; +import { useEffect, useMemo } from "preact/hooks"; +import { useShowMore } from "../sdk/useShowMore.ts"; + +export interface Props { + children: ComponentChildren; + pageInfo: PageInfo; +} + +export default function ShowMore( + { children, pageInfo }: Props, +) { + const { currentPage, loading } = useShowMore(); + + const loadedPage = pageInfo.currentPage; + const isFirstPage = !pageInfo.previousPage; + const isAtPage = useMemo(() => currentPage.value === loadedPage, [ + currentPage.value, + ]); + + useEffect(() => { + if (!isFirstPage) { + loading.value = false; + } + currentPage.value = loadedPage; + }, []); + + return ( +
+ {children} + +
+ ); +} diff --git a/sdk/useShowMore.ts b/sdk/useShowMore.ts new file mode 100644 index 0000000..794770b --- /dev/null +++ b/sdk/useShowMore.ts @@ -0,0 +1,11 @@ +import { signal } from "@preact/signals"; + +const currentPage = signal(1); +const loading = signal(false); + +export const useShowMore = () => { + return { + currentPage, + loading, + }; +}; diff --git a/sections/Product/SearchResult.tsx b/sections/Product/SearchResult.tsx index 9e9bf57..8cb96e8 100644 --- a/sections/Product/SearchResult.tsx +++ b/sections/Product/SearchResult.tsx @@ -1,4 +1,4 @@ -export { default } from "$store/components/search/SearchResult.tsx"; +export { default, loader } from "$store/components/search/SearchResult.tsx"; export function LoadingFallback() { return ( diff --git a/sections/Product/Wishlist.tsx b/sections/Product/Wishlist.tsx index 7bba30b..da021d2 100644 --- a/sections/Product/Wishlist.tsx +++ b/sections/Product/Wishlist.tsx @@ -1 +1,4 @@ -export { default } from "$store/components/wishlist/WishlistGallery.tsx"; +export { + default, + loader, +} from "$store/components/wishlist/WishlistGallery.tsx";