Skip to content

Commit

Permalink
feat: BuyTogether section implemented
Browse files Browse the repository at this point in the history
  • Loading branch information
yuriassuncx committed Apr 29, 2024
1 parent 156936d commit 2e285b3
Show file tree
Hide file tree
Showing 12 changed files with 339 additions and 35 deletions.
51 changes: 51 additions & 0 deletions components/product/BuyTogether/AddToCartButton/common.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import Button from "$store/components/ui/Button.tsx";
import { sendEvent } from "$store/sdk/analytics.tsx";
import { useUI } from "$store/sdk/useUI.ts";
import { AddToCartParams } from "apps/commerce/types.ts";
import { useState } from "preact/hooks";

export interface Props {
/** @description: sku name */
eventParams: AddToCartParams;
onAddItem: () => Promise<void>;
}

const useAddToCart = ({ eventParams, onAddItem }: Props) => {
const [loading, setLoading] = useState(false);
const { displayCart } = useUI();

const onClick = async (e: MouseEvent) => {
e.preventDefault();
e.stopPropagation();

try {
setLoading(true);

await onAddItem();

sendEvent({
name: "add_to_cart",
params: eventParams,
});

displayCart.value = true;
} finally {
setLoading(false);
}
};

return { onClick, loading, "data-deco": "add-to-cart" };
};

export default function AddToCartButton(props: Props) {
const btnProps = useAddToCart(props);

return (
<Button
{...btnProps}
class="max-w-[250px] h-[60px] rounded bg-green hover:bg-green/90 border border-green drop-shadow transition-all duration-150 text-white-normal font-bold text-lg leading-5"
>
Comprar Junto
</Button>
);
}
18 changes: 18 additions & 0 deletions components/product/BuyTogether/AddToCartButton/vtex.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { useCart } from "apps/vtex/hooks/useCart.ts";
import Button, { Props as BtnProps } from "./common.tsx";

export interface Props extends Omit<BtnProps, "onAddItem"> {
products: Array<{ seller: string; id: string; quantity: number }>;
}

function AddToCartButton({ products, eventParams }: Props) {
const { addItems } = useCart();
const onAddItem = () =>
addItems({
orderItems: products,
});

return <Button onAddItem={onAddItem} eventParams={eventParams} />;
}

export default AddToCartButton;
110 changes: 110 additions & 0 deletions components/product/BuyTogether/BuyTogether.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { useMemo } from "preact/hooks";
import Icon from "deco-sites/maconequiio/components/ui/Icon.tsx";
import Card from "./Card.tsx";
import AddToCartButtonVTEX from "$store/islands/AddToCartButton/BuyTogether/vtex.tsx";

import { useOffer } from "$store/sdk/useOffer.ts";
import { formatPrice } from "deco-sites/maconequiio/sdk/format.ts";

import type { Product } from "apps/commerce/types.ts";

export interface Props {
products: Product[] | null;
}

export default function BuyTogether({ products }: Props) {
if (!products || products.length === 0) return null;

const othersProducts = useMemo(
() => products.filter((_, index) => index !== 0),
[products],
);

if (!othersProducts || othersProducts.length === 0) return null;

const productsOffers = useMemo(
() => products.map((item) => item.offers?.lowPrice ?? 0),
[products],
);
const totalPrice = useMemo(
() => productsOffers.reduce((acc, price) => acc + price, 0),
[productsOffers],
);

const cartProducts = useMemo(() =>
products.map((product) => {
const { seller = "1" } = useOffer(product.offers);
return {
id: product.productID,
seller,
quantity: 1,
};
}), [products]);

const eventParams = useMemo(() =>
products.map((item) => ({
item_id: item.productID,
item_name: item.isVariantOf?.name || item.name,
quantity: 1,
})), [products]);

return (
<div class="flex items-center justify-center my-12 py-6 px-4 xl:px-0 bg-white-ice">
<div class="flex items-center gap-7 xl:max-w-full xl:mx-auto">
<div class="flex flex-col gap-2.5">
<h2 class="text-black-neutral font-bold text-xl leading-6">
Você está vendo
</h2>
<Card product={products[0]} />
</div>
<Icon
id="PlusNew"
width={24}
height={23}
strokeWidth={2}
class="text-green"
/>
<div class="flex flex-col gap-2.5">
<h2 class="text-black-neutral font-bold text-xl leading-6">
Compre junto
</h2>
<div class="flex items-center justify-between gap-7">
{othersProducts.map((product, index) => (
<>
<Card product={product} hasViewProductLink={true} />
{index !== othersProducts.length - 1 && (
<Icon
id="PlusNew"
width={24}
height={23}
strokeWidth={2}
class="text-green"
/>
)}
</>
))}
<Icon
id="Equals"
width={20}
height={17}
strokeWidth={2}
class="text-green"
/>
</div>
</div>
<div class="flex flex-col gap-4 items-center justify-center text-center">
<div class="flex flex-col gap-0">
<h2 class="text-lg text-black-neutral">Compre estes produtos</h2>
<span class="text-lg text-black-neutral font-bold">
por {formatPrice(totalPrice)}
</span>
</div>
<AddToCartButtonVTEX
products={cartProducts}
eventParams={{ items: eventParams }}
/>
</div>
</div>
</div>
);
}
88 changes: 88 additions & 0 deletions components/product/BuyTogether/Card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import Image from "apps/website/components/Image.tsx";

import { useOffer } from "$store/sdk/useOffer.ts";
import { relative } from "$store/sdk/url.ts";
import { formatPrice } from "$store/sdk/format.ts";

import type { Product } from "apps/commerce/types.ts";

export interface Props {
product: Product;
hasViewProductLink?: boolean;
}

const WIDTH = 183;
const HEIGHT = 183;

export default function BuyTogetherCard(
{ product, hasViewProductLink = false }: Props,
) {
const { url, name, image: images, offers } = product;
const [front] = images ?? [];
const { listPrice, price } = useOffer(offers);

const discount = Math.round(
(((listPrice ?? 0) - (price ?? 0)) / (listPrice ?? 0)) * 100,
);

return (
<div class="flex flex-col rounded w-[218px] h-[344px] bg-white-normal border border-[#BEBEBE] p-3">
<figure
class="relative"
style={{ aspectRatio: `${WIDTH} / ${HEIGHT}` }}
>
<div class="absolute top-2 z-10 flex items-center left-1/2">
<div class=""></div>
</div>

{/* Product Images */}
<div class="grid grid-cols-1 grid-rows-1 w-full">
<Image
src={front.url!}
alt={front.alternateName}
width={WIDTH}
height={HEIGHT}
class="col-span-full row-span-full rounded w-full"
sizes="(max-width: 640px) 50vw, 20vw"
loading="lazy"
decoding="async"
/>
</div>
</figure>

<div class="flex flex-col gap-1 pt-3 border-t border-t-white-base h-full justify-between">
<h2
class="line-clamp-3 text-sm text-black-neutral uppercase font-medium leading-4"
dangerouslySetInnerHTML={{ __html: name ?? "" }}
/>

{hasViewProductLink && (
<a
href={url && relative(url)}
class="underline text-red text-sm font-medium"
>
{"Ver produto >"}
</a>
)}

<div class="flex w-full items-center justify-between gap-2">
<div class="flex flex-col gap-0.5">
<div class="line-through text-gray-base text-xs leading-3">
de: {formatPrice(listPrice, offers?.priceCurrency)}
</div>

<div class="text-black-neutral text-sm font-bold leading-4">
por: {formatPrice(price, offers?.priceCurrency)}
</div>
</div>

{discount > 0 && (
<div class="flex items-center justify-center text-xs leading-3 font-bold bg-red-light text-white-normal w-10 h-8 p-0.5 rounded-tl-2xl rounded-br-2xl">
-{discount}%
</div>
)}
</div>
</div>
</div>
);
}
2 changes: 1 addition & 1 deletion components/product/ProductCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ function ProductCard({
""
)
: (
<div class="flex w-full justify-between gap-2">
<div class="flex w-full items-center justify-between gap-2">
<div class="flex flex-col gap-0.5">
<div class="line-through text-gray-base text-xs leading-3">
de: {formatPrice(listPrice, offers?.priceCurrency)}
Expand Down
2 changes: 2 additions & 0 deletions components/ui/Icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,15 @@ export type AvailableIcons =
| "Instagram"
| "Linkedin"
| "Minus"
| "Equals"
| "MapPin"
| "MagnifyingGlass"
| "Mastercard"
| "Message"
| "Phone"
| "Pix"
| "Plus"
| "PlusNew"
| "QuestionMarkCircle"
| "Return"
| "Ruler"
Expand Down
3 changes: 3 additions & 0 deletions fresh.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// This file is automatically updated during development when running `dev.ts`.

import * as $_app from "./routes/_app.tsx";
import * as $AddToCartButton_BuyTogether_vtex from "./islands/AddToCartButton/BuyTogether/vtex.tsx";
import * as $AddToCartButton_linx from "./islands/AddToCartButton/linx.tsx";
import * as $AddToCartButton_nuvemshop from "./islands/AddToCartButton/nuvemshop.tsx";
import * as $AddToCartButton_shopify from "./islands/AddToCartButton/shopify.tsx";
Expand Down Expand Up @@ -34,6 +35,8 @@ const manifest = {
"./routes/_app.tsx": $_app,
},
islands: {
"./islands/AddToCartButton/BuyTogether/vtex.tsx":
$AddToCartButton_BuyTogether_vtex,
"./islands/AddToCartButton/linx.tsx": $AddToCartButton_linx,
"./islands/AddToCartButton/nuvemshop.tsx": $AddToCartButton_nuvemshop,
"./islands/AddToCartButton/shopify.tsx": $AddToCartButton_shopify,
Expand Down
8 changes: 8 additions & 0 deletions islands/AddToCartButton/BuyTogether/vtex.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import Component from "$store/components/product/BuyTogether/AddToCartButton/vtex.tsx";
import type { Props } from "$store/components/product/BuyTogether/AddToCartButton/vtex.tsx";

function Island(props: Props) {
return <Component {...props} />;
}

export default Island;
54 changes: 28 additions & 26 deletions manifest.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,19 +50,20 @@ import * as $$$$$$40 from "./sections/Miscellaneous/CampaignTimer.tsx";
import * as $$$$$$41 from "./sections/Miscellaneous/CookieConsent.tsx";
import * as $$$$$$42 from "./sections/Miscellaneous/Slide.tsx";
import * as $$$$$$43 from "./sections/Newsletter/Newsletter.tsx";
import * as $$$$$$44 from "./sections/Product/ImageGalleryFrontBack.tsx";
import * as $$$$$$45 from "./sections/Product/ImageGallerySlider.tsx";
import * as $$$$$$46 from "./sections/Product/NotFound.tsx";
import * as $$$$$$47 from "./sections/Product/NotFoundChallenge.tsx";
import * as $$$$$$48 from "./sections/Product/ProductInfo.tsx";
import * as $$$$$$49 from "./sections/Product/ProductShelf.tsx";
import * as $$$$$$50 from "./sections/Product/ProductShelfTabbed.tsx";
import * as $$$$$$51 from "./sections/Product/SearchResult.tsx";
import * as $$$$$$52 from "./sections/Product/ShelfWithImage.tsx";
import * as $$$$$$53 from "./sections/Product/Wishlist.tsx";
import * as $$$$$$54 from "./sections/Social/InstagramPosts.tsx";
import * as $$$$$$55 from "./sections/Social/WhatsApp.tsx";
import * as $$$$$$56 from "./sections/Theme/Theme.tsx";
import * as $$$$$$44 from "./sections/Product/BuyTogether.tsx";
import * as $$$$$$45 from "./sections/Product/ImageGalleryFrontBack.tsx";
import * as $$$$$$46 from "./sections/Product/ImageGallerySlider.tsx";
import * as $$$$$$47 from "./sections/Product/NotFound.tsx";
import * as $$$$$$48 from "./sections/Product/NotFoundChallenge.tsx";
import * as $$$$$$49 from "./sections/Product/ProductInfo.tsx";
import * as $$$$$$50 from "./sections/Product/ProductShelf.tsx";
import * as $$$$$$51 from "./sections/Product/ProductShelfTabbed.tsx";
import * as $$$$$$52 from "./sections/Product/SearchResult.tsx";
import * as $$$$$$53 from "./sections/Product/ShelfWithImage.tsx";
import * as $$$$$$54 from "./sections/Product/Wishlist.tsx";
import * as $$$$$$55 from "./sections/Social/InstagramPosts.tsx";
import * as $$$$$$56 from "./sections/Social/WhatsApp.tsx";
import * as $$$$$$57 from "./sections/Theme/Theme.tsx";

const manifest = {
"loaders": {
Expand Down Expand Up @@ -117,20 +118,21 @@ const manifest = {
"deco-sites/maconequiio/sections/Miscellaneous/CookieConsent.tsx": $$$$$$41,
"deco-sites/maconequiio/sections/Miscellaneous/Slide.tsx": $$$$$$42,
"deco-sites/maconequiio/sections/Newsletter/Newsletter.tsx": $$$$$$43,
"deco-sites/maconequiio/sections/Product/BuyTogether.tsx": $$$$$$44,
"deco-sites/maconequiio/sections/Product/ImageGalleryFrontBack.tsx":
$$$$$$44,
"deco-sites/maconequiio/sections/Product/ImageGallerySlider.tsx": $$$$$$45,
"deco-sites/maconequiio/sections/Product/NotFound.tsx": $$$$$$46,
"deco-sites/maconequiio/sections/Product/NotFoundChallenge.tsx": $$$$$$47,
"deco-sites/maconequiio/sections/Product/ProductInfo.tsx": $$$$$$48,
"deco-sites/maconequiio/sections/Product/ProductShelf.tsx": $$$$$$49,
"deco-sites/maconequiio/sections/Product/ProductShelfTabbed.tsx": $$$$$$50,
"deco-sites/maconequiio/sections/Product/SearchResult.tsx": $$$$$$51,
"deco-sites/maconequiio/sections/Product/ShelfWithImage.tsx": $$$$$$52,
"deco-sites/maconequiio/sections/Product/Wishlist.tsx": $$$$$$53,
"deco-sites/maconequiio/sections/Social/InstagramPosts.tsx": $$$$$$54,
"deco-sites/maconequiio/sections/Social/WhatsApp.tsx": $$$$$$55,
"deco-sites/maconequiio/sections/Theme/Theme.tsx": $$$$$$56,
$$$$$$45,
"deco-sites/maconequiio/sections/Product/ImageGallerySlider.tsx": $$$$$$46,
"deco-sites/maconequiio/sections/Product/NotFound.tsx": $$$$$$47,
"deco-sites/maconequiio/sections/Product/NotFoundChallenge.tsx": $$$$$$48,
"deco-sites/maconequiio/sections/Product/ProductInfo.tsx": $$$$$$49,
"deco-sites/maconequiio/sections/Product/ProductShelf.tsx": $$$$$$50,
"deco-sites/maconequiio/sections/Product/ProductShelfTabbed.tsx": $$$$$$51,
"deco-sites/maconequiio/sections/Product/SearchResult.tsx": $$$$$$52,
"deco-sites/maconequiio/sections/Product/ShelfWithImage.tsx": $$$$$$53,
"deco-sites/maconequiio/sections/Product/Wishlist.tsx": $$$$$$54,
"deco-sites/maconequiio/sections/Social/InstagramPosts.tsx": $$$$$$55,
"deco-sites/maconequiio/sections/Social/WhatsApp.tsx": $$$$$$56,
"deco-sites/maconequiio/sections/Theme/Theme.tsx": $$$$$$57,
},
"apps": {
"deco-sites/maconequiio/apps/decohub.ts": $$$$$$$$$$$0,
Expand Down
Loading

0 comments on commit 2e285b3

Please sign in to comment.