Skip to content

Commit

Permalink
Updating Readme.md file
Browse files Browse the repository at this point in the history
  • Loading branch information
romualdo-ah committed Jan 21, 2022
1 parent a8839e5 commit 9a37c1f
Show file tree
Hide file tree
Showing 37 changed files with 2,195 additions and 359 deletions.
1 change: 1 addition & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
API_URL=https://5d6da1df777f670014036125.mockapi.io/api/v1/product
17 changes: 17 additions & 0 deletions components/Layout/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React ,{ReactNode}from 'react'
import Navbar from '../Navbar'

interface LayoutProps {
children: ReactNode
}

export default function Layout({children}:LayoutProps) {
return (
<>
<Navbar/>
<main className="flex w-full min-h-screen px-5 md:px-20 font-montserrat py-20 pb-24 bg-gray-200">
{children}
</main>
</>
)
}
49 changes: 49 additions & 0 deletions components/Navbar/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import React, { useEffect } from 'react';
import Link from 'next/link';
import { useRouter } from 'next/router';
import {BsCart3} from 'react-icons/bs';
import {useCart} from '../../hooks/useCart';
export default function Navbar() {

const {cart} = useCart();

const [productCounter, setProductCounter] = React.useState(0);

useEffect(
() => {
const numberOfProducts = cart.length;
setProductCounter(numberOfProducts);
},[cart]
);


const router = useRouter();
//set the active class on the navbar
const activeClass = (path: string) => {
if (router.pathname === path) {
return 'decoration-blue-300 hover:decoration-gray-300 underline underline-offset-2';
}
return '';
};

return (
<nav className="flex justify-between content-center align-middle p-3 bg-white drop-shadow-md z-50 fixed top-0 w-full bg-opacity-80 backdrop-blur">

<Link href="/home">
<a className={`${activeClass('/home')} text-black-500 hover:text-black-400`}>
Home
</a>
</Link>

<Link href="/cart">
<a className={`${activeClass('/cart')} hover:text-gray-400 content-center justify-center pt-1 pr-6 relative`}>
<BsCart3 className="w-5 h-5" />
<div className="text-gray-700 text-sm absolute -top-2 -right-1 text-gray-100 rounded-full flex justify-center items-center bg-slate-700 w-5 h-6 px-3">
<span className="text-xs">{productCounter}</span>
</div>
</a>
</Link>

</nav>
);
}
69 changes: 69 additions & 0 deletions components/Product/AmountControl/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import React, { useEffect, useState } from 'react';
import { productInterface } from '../../../interfaces';
import { useCart } from '../../../hooks/useCart';
import { HiPlus, HiMinus } from 'react-icons/hi';
interface AmountControlProps {
product: productInterface;
min?: number;
}

export function AmountControl({ product,min }: AmountControlProps) {
const { updateProductAmount, cart } = useCart();

const [ formatedProduct, setFormatedProduct ] = useState({
...product,
total: (product.amount * product.price).toFixed(2)
});

useEffect(
() => {
//find product in cart with the id of product
const productInCart = cart.find((item) => item.productId === product.id);
if (productInCart) {
setFormatedProduct({
...product,
total: (product.amount * product.price).toFixed(2),
amount: productInCart.amount
});
} else {
setFormatedProduct({ ...product, total: (product.amount * product.price).toFixed(2) });
}
},
[ product, cart ]
);

const handleIncreaseAmount = (product: productInterface) => {
const updatedProduct = { productId: product.id, amount: product.amount + 1 };

updateProductAmount(updatedProduct);
};

const handleDecreaseAmount = (product: productInterface) => {
if(product.amount > min){
const updatedProduct = { productId: product.id, amount: product.amount - 1 };
updateProductAmount(updatedProduct);
}
};
return (
<div className="flex flex-row ml-1 py-3 items-center">

{formatedProduct.amount === undefined ? (
<button className="h-5 w-5 text-gray-400">
<HiMinus />
</button>
) : (

<button className="h-5 w-5 text-gray-700" onClick={() => handleDecreaseAmount(formatedProduct)}>
<HiMinus />
</button>
)}

<p className="w-fit px-3 bg-gray-100 rounded mr-1 text-gray-700">
x{formatedProduct.amount ? formatedProduct.amount : 0}
</p>
<button className="text-xl text-gray-700" onClick={() => handleIncreaseAmount(formatedProduct)}>
<HiPlus />
</button>
</div>
);
}
48 changes: 48 additions & 0 deletions components/Product/BuyContainer/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import React, { useEffect } from 'react'
import Link from 'next/link'
import { useRouter } from 'next/router'
interface BuyContainerProps {
total: number,
text:string
}

import { formatedPrice } from '../../../utils'

export function BuyContainer({total,text}:BuyContainerProps) {
const router= useRouter()

return (
<div className="fixed bottom-0 left-0 right-0 bg-white w-full p-1 h-fit flex flex-row items-center justify-around">
<div className='self-start'>
<span className="text-xl text-gray-800">total: </span>
<span className="text-sm text-gray-600">R$ {formatedPrice(total)??'0.00'}</span>
</div>
<div className={`w-1/3 bottom-2 left-2 right-4 ${total==0?" bg-gray-400 ":"bg-gray-900 "} p-2 rounded-md text-gray-400 text-center`}>
{

total>0?(

router.pathname.includes('/product/') ?
(
<Link href="/cart">
<a className='cursor-pointer'>
<span className="text-xl text-gray-200">{text}</span>
</a>
</Link>
):
(
<Link href={`/payment`}>
<a>
<span className="text-xl text-gray-200">{text}</span>
</a>
</Link>

)
)
:
<span className="text-xl text-gray-300">{text}</span>
}
</div>
</div>
)
}
47 changes: 47 additions & 0 deletions components/Product/OnCart/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import {useState,useEffect} from 'react';
import { productProp } from '../../../interfaces';
import {useCart} from '../../../hooks/useCart';
import {RiShoppingCart2Fill,RiShoppingCart2Line} from 'react-icons/ri';

export const OnCart=({product}:productProp)=>{

const { cart, removeProduct, addProduct } = useCart();
const [ liked, setLiked ] = useState(false);


useEffect(
() => {
const isSaved = cart.find((cart_product) => cart_product.productId == product.id);

if (isSaved) {
setLiked(true);
}
},
[ cart, product.id ]
);

const handleToogleLike = () => {

if (liked) {
removeProduct(product.id);
setLiked(false);
} else {
addProduct(product.id);
setLiked(true);
}
};

return (
<div
onClick={handleToogleLike}
className=" right-0 m-2 w-8 h-8 flex justify-center items-center self-end"
>
{liked ? (
<RiShoppingCart2Fill className=" text-gray-500 w-5 h-5" />
) : (
<RiShoppingCart2Line className="text-gray-400 w-5 h-5 focus:pointer-events-auto" />
)}
</div>
)

}
60 changes: 60 additions & 0 deletions components/Product/ProductCardCart/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import React, { useEffect, useState } from 'react';
import Link from 'next/link';
import { handleDelete } from '../../../utils';
import { IoMdClose } from 'react-icons/io';
import { AmountControl } from '../AmountControl';
import { useCart } from '../../../hooks/useCart';
import { productProp } from '../../../interfaces';
import { formatImageURL } from '../../../utils';
import { ProductImage } from '../ProductImage';

export function ProductCardCart({ product }: productProp) {
const { removeProduct } = useCart();
const [ formatedProduct, setFormatedProduct ] = useState({
...product,
image: formatImageURL(product.image),
total: (product.amount * product.price).toFixed(2)
});

useEffect(
() => {
setFormatedProduct({
...product,
image: formatImageURL(product.image),
total: (product.amount * product.price).toFixed(2)
});
},
[ product ]
);

return (
<div className="flex flex-row justify-start w-full mb-3 relative h-32 rounded-md">
<Link href="/product/[id]" as={`/product/${product.id}`}>
<div className="max-h-64 max-w-64 bg-gray-300 h-32 w-48">
{/* passing cart as classname couse the style wanted is on ../../../styles/Cart.modules.css */}
<ProductImage product={product} styles={'rounded-l-md h-full'}/>
</div>
</Link>
<div className="flex flex-col w-full bg-white pl-3 rounded-r-md pt-2">
<div className="flex flex-row justify-between">
<Link href="/product/[id]" as={`/product/${product.id}`}>
<a>
<p className="w-full text-gray-600 text-sm line-clamp-1 hover:text-gray-400 transition-colors duration-300">{product.name}</p>
</a>
</Link>
</div>
<p className="text-lg">R$ {product.price}</p>

<div className="flex">
<AmountControl product={product} min={1} />
</div>
<p className="absolute bottom-2 bottom-2 text-gray-700 text-xs">
<span className="text-gray-600 mr-1">Total:</span> R$ {formatedProduct.total}
</p>
<button className="absolute top-2 right-2" onClick={() => handleDelete({handler:()=>removeProduct(formatedProduct.id),message:"Product has been deleted!"})}>
<IoMdClose className="w-4 h-4 text-gray-600" />
</button>
</div>
</div>
);
}
30 changes: 30 additions & 0 deletions components/Product/ProductCardCatalog/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React from 'react';
import Link from 'next/link';
import { productProp } from '../../../interfaces';
import { Product } from '../index';
import { OnCart } from '../OnCart';
import {Share} from '../Share';
import { ProductImage } from '../ProductImage';

export function ProductCard({ product }: productProp) {

return (
<div className="flex flex-col rounded-sm bg-gray-100 justify-between hover:drop-shadow-2xl transition-all ease-out duration-1000 cursor-pointer relative">
<div className=" h-72 w-80 z-10">
{/* rounded-t-md is a custom css classname found at styles/Lazy.module.css */}
<ProductImage product={product} styles='h-full rounded-t-md'/>
<div className='flex flex-row items-center w-full justify-end'>
<Share product={product}/>
<OnCart product={product} />
</div>
</div>

<Product product={product} />
<Link href={`/product/${product.id}`}>
<a>
<div className="m-1 p-2 bg-gray-700 text-gray-200 rounded-md text-center">See more</div>
</a>
</Link>
</div>
);
}
17 changes: 17 additions & 0 deletions components/Product/ProductImage/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React from 'react';
import { productInterface } from '../../../interfaces';
import { LazyLoadImage } from 'react-lazy-load-image-component';
import 'react-lazy-load-image-component/src/effects/blur.css';

interface ProductImageProps {
styles?: string;
product: productInterface;
}

export function ProductImage({ product, styles }: ProductImageProps) {
return (
<div className={`${styles} relative`}>
<LazyLoadImage src={product.image} alt={product.name} effect='blur' placeholderSrc="/default-image.jpg"/>
</div>
);
}
41 changes: 41 additions & 0 deletions components/Product/Share/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React from 'react';
import {FaTelegramPlane} from 'react-icons/fa';
import Swal from 'sweetalert2';
export function Share({product}) {

const handleClick = (product) => {
//copy to clipboard
const text = `${product.name} - R$ ${product.price}`;
const el = document.createElement('textarea');
el.value = text;
document.body.appendChild(el);
el.select();
document.execCommand('copy');
document.body.removeChild(el);

if (navigator.share) {
navigator
.share({
title: product.name,
text: `${product.name} - R$ ${product.price}`,
url: `${process.env.NEXT_PUBLIC_URL}/product/${product.id}`,
})
.then(() => {
console.log("Successfully shared!");
})
.catch((error) => {
console.error(error);
});
}
else
Swal.fire
({
title: 'Oops...🙄!',
text: 'It seems you are not using a browser that supports sharing',
})
};

return <>
<FaTelegramPlane className='text-gray-500 w-5 h-5 m-3' onClick={()=>handleClick(product)}/>
</>;
}
Loading

0 comments on commit 9a37c1f

Please sign in to comment.