Skip to content

Commit

Permalink
feat: Show More added to PLP
Browse files Browse the repository at this point in the history
  • Loading branch information
yuriassuncx committed May 24, 2024
1 parent 2ba8ac0 commit f811821
Show file tree
Hide file tree
Showing 8 changed files with 216 additions and 55 deletions.
70 changes: 61 additions & 9 deletions components/product/ProductGallery.tsx
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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 = {
Expand All @@ -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 (
<div class={`grid ${mobile} gap-2 items-center ${desktop} sm:gap-10`}>
<div
class={`grid ${mobile} gap-2 items-center ${desktop} sm:gap-10`}
>
{layout?.format == "Show More" && (
<Head>
{pageInfo.nextPage && <link rel="next" href={pageInfo.nextPage} />}
{pageInfo.previousPage && (
<link rel="prev" href={pageInfo.previousPage} />
)}
</Head>
)}

{products?.map((product, index) => (
<ProductCard
key={`product-card-${product.productID}`}
product={product}
preload={index === 0}
index={offset + index}
layout={layout?.card}
platform={platform}
/>
))}

{(layout && layout?.format === "Show More") && (
<>
<ShowMore
pageInfo={pageInfo}
>
{partialUrl && (
<div>
<div class="mt-2">
<Spinner size={24} />
</div>
<button
id={`show-more-button-${pageInfo.currentPage}`}
class="btn cursor-pointer hidden w-0 h-0 absolute"
{...usePartialSection({
href: partialUrl.href,
mode: "append",
})}
>
Mostrar mais
</button>
</div>
)}
</ShowMore>
</>
)}
</div>
);
}
Expand Down
112 changes: 70 additions & 42 deletions components/search/SearchResult.tsx
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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;
Expand All @@ -41,65 +45,80 @@ function NotFound() {
function Result({
page,
layout,
cardLayout,
startingPage = 0,
}: Omit<Props, "page"> & { page: ProductListingPage }) {
url: _url,
}: Omit<Props, "page"> & {
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 (
<>
<div class="container px-4 sm:py-10">
<SearchControls
sortOptions={sortOptions}
filters={filters}
breadcrumb={breadcrumb}
displayFilter={layout?.variant === "drawer"}
/>
{(isFirstPage || !isPartial) && (
<SearchControls
sortOptions={sortOptions}
filters={filters}
breadcrumb={breadcrumb}
displayFilter={layout?.variant === "drawer"}
/>
)}

<div class="flex flex-row">
{layout?.variant === "aside" && filters.length > 0 && (
<aside class="hidden sm:block w-min min-w-[250px]">
<Filters filters={filters} />
{(isFirstPage && !isPartial) && <Filters filters={filters} />}
</aside>
)}
<div class="flex-grow" id={id}>
<ProductGallery
products={products}
offset={offset}
layout={{ card: cardLayout, columns: layout?.columns }}
layout={{ columns: layout?.columns, format }}
pageInfo={pageInfo}
url={url}
/>
</div>
</div>

<div class="flex justify-center my-4">
<div class="join">
<a
aria-label="previous page link"
rel="prev"
href={pageInfo.previousPage ?? "#"}
class="btn btn-ghost join-item"
>
<Icon id="ChevronLeft" size={24} strokeWidth={2} />
</a>
<span class="btn btn-ghost join-item">
Page {zeroIndexedOffsetPage + 1}
</span>
<a
aria-label="next page link"
rel="next"
href={pageInfo.nextPage ?? "#"}
class="btn btn-ghost join-item"
>
<Icon id="ChevronRight" size={24} strokeWidth={2} />
</a>
{format == "Pagination" && (
<div class="flex justify-center my-4">
<div class="join">
<a
aria-label="previous page link"
rel="prev"
href={pageInfo.previousPage ?? "#"}
class="btn btn-ghost join-item"
>
<Icon id="ChevronLeft" size={24} strokeWidth={2} />
</a>
<span class="btn btn-ghost join-item">
Página {zeroIndexedOffsetPage + 1}
</span>
<a
aria-label="next page link"
rel="next"
href={pageInfo.nextPage ?? "#"}
class="btn btn-ghost join-item"
>
<Icon id="ChevronRight" size={24} strokeWidth={2} />
</a>
</div>
</div>
</div>
)}
</div>
<SendEventOnView
id={id}
Expand All @@ -124,12 +143,21 @@ function Result({
);
}

function SearchResult({ page, ...props }: Props) {
function SearchResult(
{ page, ...props }: ReturnType<typeof loader>,
) {
if (!page) {
return <NotFound />;
}

return <Result {...props} page={page} />;
}

export const loader = (props: Props, req: Request) => {
return {
...props,
url: req.url,
};
};

export default SearchResult;
12 changes: 10 additions & 2 deletions components/wishlist/WishlistGallery.tsx
Original file line number Diff line number Diff line change
@@ -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<typeof loader>) {
const isEmpty = !props.page || props.page.products.length === 0;

if (isEmpty) {
Expand All @@ -24,4 +25,11 @@ function WishlistGallery(props: Props) {
return <SearchResult {...props} />;
}

export const loader = (props: Props, req: Request) => {
return {
...props,
url: req.url,
};
};

export default WishlistGallery;
2 changes: 2 additions & 0 deletions fresh.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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,
Expand Down
57 changes: 57 additions & 0 deletions islands/ShowMore.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<div
class={(isAtPage && pageInfo.nextPage)
? "flex justify-center col-span-full"
: "hidden"}
>
{children}
<button
class={`btn cursor-pointer absolute ${loading.value ? "hidden" : ""}`}
onClick={() => {
loading.value = true;
const element = document.getElementById(
`show-more-button-${loadedPage}`,
);
if (element) {
element.click();
}
if (pageInfo.nextPage) {
const url = new URL(pageInfo.nextPage, window.location.href);
url.searchParams.delete("partial");
window.history.replaceState({}, "", url.toString());
}
}}
>
Mostrar mais
</button>
</div>
);
}
11 changes: 11 additions & 0 deletions sdk/useShowMore.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { signal } from "@preact/signals";

const currentPage = signal(1);
const loading = signal(false);

export const useShowMore = () => {
return {
currentPage,
loading,
};
};
Loading

0 comments on commit f811821

Please sign in to comment.